Calling TaskCompletionSource.SetResult in a non blocking manner

I’ve discovered that TaskCompletionSource.SetResult(); invokes the code awaiting the task before returning. In my case that result in a deadlock.

Yes, I have a blog post documenting this (AFAIK it’s not documented on MSDN). The deadlock happens because of two things:

  1. There’s a mixture of async and blocking code (i.e., an async method is calling Wait).
  2. Task continuations are scheduled using TaskContinuationOptions.ExecuteSynchronously.

I recommend starting with the simplest possible solution: removing the first thing (1). I.e., don’t mix async and Wait calls:

await SendAwaitResponse("first message");
SendAwaitResponse("second message").Wait();

Instead, use await consistently:

await SendAwaitResponse("first message");
await SendAwaitResponse("second message");

If you need to, you can Wait at an alternative point further up the call stack (not in an async method).

That’s my most-recommended solution. However, if you want to try removing the second thing (2), you can do a couple of tricks: either wrap the SetResult in a Task.Run to force it onto a separate thread (my AsyncEx library has *WithBackgroundContinuations extension methods that do exactly this), or give your thread an actual context (such as my AsyncContext type) and specify ConfigureAwait(false), which will cause the continuation to ignore the ExecuteSynchronously flag.

But those solutions are much more complex than just separating the async and blocking code.

As a side note, take a look at TPL Dataflow; it sounds like you may find it useful.

Leave a Comment