The goal is to weed out improper usages of ConfigureAwait in a very large code base.
Some teams choose to use a code analysis tool for this. There are several available. The most common approach I've seen is to require a ConfigureAwait
for every await
, and explicitly specify either true
or false
. This ensures that each await
has been reviewed and the flow of context is explicit. Other teams apply project-specific rules of "always use ConfigureAwait(false)
" and just depend on code review for projects that can't follow that rule.
The problem with your example code is that it's not possible for DoWhatever
to know whether it was indirectly invoked, because of the Task.Run
. If you rewrite those methods, this becomes clear:
public static async Task CapturesContext()
{
var task = Task.Run(() => DoWhatever());
await task;
}
public static async Task DoesNotCaptureContext()
{
var task = Task.Run(() => DoWhatever());
var configuredTask = task.ConfigureAwait(false);
await configuredTask;
}
The first lines of the rewritten methods should make it clear that DoWhatever
has no idea whether CapturesContext
or DoesNotCaptureContext
will capture the context or not. Note the "will" (future tense) - it is entirely possible that DoWhatever
runs and finishes executing before ConfigureAwait(false)
is even called.
Now, you can check from within a task whether it is running on a context right now. But in this case, for both example methods, DoWhatever
will not see a context due to the Task.Run
. So that doesn't help you detect the fact that CapturesContext
does capture the context; DoWhatever
doesn't see the context so it can't detect it.
The custom SynchronizationContext
is a good solution for unit tests, but it would be awkward to use at runtime, since you do have some methods that need the context. For this reason, most teams choose to depend on code review and/or code analysis tooling.