Category Archives: Asynchronous

Replacing multiple ConfigureAwait with the SynchronizationContextRemover

First off this is a post based upon the post An alternative to ConfigureAwait(false) everywhere.

This is a neat solution using the knowledge of how async/await and continuations fit together.

The problem Ben Willi is solving is the situation where, as a writer of library code one generally wants to use ConfigureAwat(false) so that the library does not block the synchronization context that’s used by the application. Obviously this is of particular concern when the current synchronzation context is the UI thread.

The issue is that if you have many await’s each having to set the async method/task as ConfigureAwait(false) things get a little messy and require any additional changes to also ConfigureAwait(false). Plus the synchronization onto the calling thread each time mightcause lag in the application.

See Ben’s post for a full description of the problem that’s being solved as he has example code to demonstrate the pitfalls.

What I liked and it’s a reminder of how the async/await works. What we’re trying to do is remove ConfigureAwait from all the following sort of code

await RunAsync().ConfigureAwait(false);
await ProcessAsync().ConfigureAwait(false);
await CompleteAsync().ConfigureAwait(false);

If we ignore the ConfigureAwait for a moment, in pseudo code, these three calls end up like this via the compiler

RunAsync.OnComplete
(
   ProcessAsync().OnComplete
   (
      CompleteAsync().OnComplete
      (
      )
   )
)

What happens internally is that each call is wrapped in a continuation of the previous awaited call.

See also Creating awaitable types for a look at creating awaitable types.

Let’s first look at what the new code might look like, then we’ll look at how the solution works

await new SynchronizationContextRemover();

await RunAsync();
await ProcessAsync();
await CompleteAsync();

here’s the code has for the SynchronizationContextRemover

public struct SynchronizationContextRemover : INotifyCompletion
{
   public bool IsCompleted => SynchronizationContext.Current == null;

   public void OnCompleted(Action continuation)
   {
      var prev = SynchronizationContext.Current;
      try
      {
         SynchronizationContext.SetSynchronizationContext(null);
         continuation();
      }
      finally
      {
         SynchronizationContext.SetSynchronizationContext(prev);
      }
   }

   public SynchronizationContextRemover GetAwaiter()
   {
      return this;
   }

   public void GetResult()
   {            
   }
}

What the SynchronizationContextRemover does is create an awaitable type (the GetAwaiter) which is this, so an instance of itself, which then supplies the code for an awaitable type, such as IsCompleted, GetResult and OnCompleted property and methods.

With the knowledge of how the awaits turn into methods with continuations to the next method etc. this would now form the following pseudo code

SynchronizationContextRemover.OnComplete
(
   RunAsync.OnComplete
   (
      ProcessAsync().OnComplete
      (
         CompleteAsync().OnComplete
         (
         )
      )
   )
)

Hence each awaitable method is nested, ultimately, in the OnCompleted method of SynchronizationContextRemover.

The SynchronizationContextRemover OnCompleted method then switched the synchronization context to null prior to the continuations being called so that the other method calls will now also use the null synchronization context and therefore code is not marshalled back to the original synchronization context (i.e. the UI thread).

Beware those async void exceptions in unit tests

This is a cautionary tale…

I’ve been moving my builds to a new server box and in doing so started to notice some problems. These problems didn’t exist on the previous build box and this might be down to the updates versions of the tools, such as nant etc. I was using on the new box.

Anyway the crux of the problem was I was getting exceptions when the tests were run, nothing told me what assembly or what test, I just ended up with

NullReference exceptions and a stack trace stating this

System.Runtime.CompilerServices.AsyncMethodBuilderCore.b__0(System.Object)

Straight away it looks like async/await issues.

To cut a long story short.

I removed all the Test assemblies, then one by one place them back into the build, running the “test” target in nant each time until I found the assembly with the problems. Running the tests this way also seemed to report more specifics about the area of the exception.

As we know, we need to be particularly careful handling exceptions that occur within an async void method (mine exist because they’re event handlers which are raised upon property changes in a view model).

Interestingly when I ran the Resharper Test Runner all my tests passed, but switching to running in the tests in Debug showed up all the exceptions (ofcourse assuming that you have the relevant “Exceptions” enabled within Visual Studio). The downside of this approach, ofcourse, is that I also see all the exceptions that are intentionally tested for, i.e. ensuring my code exceptions when it should. But it was worth it to solve these problems.

Luckily none of these exceptions would have been a problem in normal running of my application, they were generally down to missing mocking code or the likes, but it did go to show I could be smug in believing my tests caught every issue.

Using ConfigureAwait

By default code after an await continues on the calling thread (i.e. the thread prior to the await keyword). In many cases this is what we want to happen. Remember async methods are not necessarily run on another task/thread, but in those instances where we know that they are truly asynchronous, we might actually want our code after the await to also run in the same context (i.e. on the same background thread of the async method).

So, for the sake of argument we’ll assume we’re awaiting a Task which we know is run on a background thread. Upon completion we intend to process some results from this Task, also on a background thread. Now obviously we could do something like

private async Task Process()
{
   var results = await SomeBackgroundThreadMethod();
   await Task.Run(() => ProcessOnBackgroundThread(results);
}

We can see that if SomeBackgroundThreadMethod runs on a background thread then after the await we continue on the calling thread before again spinning up another Task to run our processing code. So we’re potentially using two background threads when in reality, if we know SomeBackgroundThreadMethod actually is running on a background thread then we could simply continue to process on this same thread.

Okay, so this is a little contrived because, as you know, the SomeBackgroundThreadMethod would itself return a Task and we could simply ContinueWith on this Task. But an alternative to ContinueWith is to call ConfigureAwait on the SomeBackgroundThreadMethod Task, such as

private async Task Process()
{
   var results = await SomeBackgroundThreadMethod().ConfigureAwait(false);
   ProcessOnBackgroundThread(results)
}

Gotchas?

  • Now the code which uses ConfigureAwait seems quite obvious, but ConfigureAwait does not magically create a Task or background thread if none existed on the return from SomeBackgroundThreadMethod. For example if SomeBackgroundThreadMethod simply returns a TaskCompletionSource, ConfigureAwait would simply end up being on the calling thread.
  • ConfigureAwait only affects code following it and within the scope of the method using it, hence in the above code once we exit the Process method all async/await calls will return to their default behaviour, it’s only code within the Process method after the await which continues on the same thread as SomeBackgroundThreadMethod.

References
Task.ConfigureAwait