17

It's well-known that in a general-purpose library, ConfigureAwait(false) should be used on every await call to avoid continuing on the current SynchronizationContext.

As an alternative to peppering the entire codebase with ConfigureAwait(false), one could simply set the SynchronizationContext to null once, at the public surface method, and restore it before returning to the user. In other words:

public async Task SomeSurfaceMethod()
{
    var callerSyncCtx = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);
    try
    {
        // Do work
    }
    finally
    {
        SynchronizationContext.SetSynchronizationContext(callerSyncCtx);
    }
}

This could also be wrapped in a using for better readability.

Is there a disadvantage to this approach, does it not end up producing the same effect?

The major advantage is obviously readability - the removal of all ConfigureAwait(false) calls. It may also reduce the likelihood of forgetting a ConfigureAwait(false) somewhere (although analyzers mitigate this, and it could be argued that developers could just as well forget changing the SynchronizationContext).

A somewhat exotic advantage is not embedding the choice of capturing the SynchronizationContext or not in all methods. In other words, in one case I may want to run method X with a SynchronizationContext, while in another I may want to run the same method without one. When ConfigureAwait(false) is embedded everywhere that isn't possible. Granted, this is quite a rare requirement, but I bumped into it while working on Npgsql (triggering this question).

Shay Rojansky
  • 15,357
  • 2
  • 40
  • 69
  • And what about `SomeSurfaceMethod(keepInContext bool) {await asyncMethod().ConfigureAwait(keepInContext)}` simple and allows caller to choice... – jlvaquero Sep 12 '16 at 10:16
  • @jlvaquero the problem is that await calls *within* `asyncMethod` have to also specify `ConfigureAwait`, so you have to pass `keepInContext` all the way via the stack. Seems rather clunky vs. setting SynchronizationContext just once at the surface level... – Shay Rojansky Sep 12 '16 at 10:18
  • You argue that this adds readability but initially it will not - for anyone new to your codebase, they'll find code that's obviously "broken" because it's not using `ConfigureAwait`, which is what they've learned to do when writing library code. I haven't got an answer yet to your question but I'll keep thinking on it. – Damien_The_Unbeliever Sep 13 '16 at 06:09
  • 1
    @Damien_The_Unbeliever while that's a valid point, it's the case whenever any sort of change is proposed... The fact that people are used to one practice isn't really an argument against changing it... – Shay Rojansky Sep 13 '16 at 07:33
  • 1
    Check the links: http://stackoverflow.com/questions/28776408/c-sharp-async-await-chaining-with-configureawaitfalse http://stackoverflow.com/questions/28305968/use-task-run-in-synchronous-method-to-avoid-deadlock-waiting-on-async-method/28307965#28307965 – Mrinal Kamboj Sep 16 '16 at 09:17
  • @MrinalKamboj thanks for the links, they indeed discuss the same approach. Nobody seems to have commented on any disadvantage until now. – Shay Rojansky Sep 16 '16 at 09:23
  • 1
    Check this link too: http://stackoverflow.com/questions/39518980/using-configureawaitfalse-for-private-async-methods As Stephen Cleary suggest, both are the valid mechanism, Idea remains, you can go for either way and there will not be any issue – Mrinal Kamboj Sep 16 '16 at 09:26

1 Answers1

2

As @MrinalKamboj wrote in the comments, the approach of temporarily setting the SynchronizationContext to null at the public surface method and setting back has already been proposed here. There doesn't seem to be any specific problem associated with this (see Stephen Cleary's answer here).

Community
  • 1
  • 1
Shay Rojansky
  • 15,357
  • 2
  • 40
  • 69