I have an async function that is being awaited from the UI thread. I am certain ConfigureAwait(false)
is not being called. In fact I've tried explicitly calling ConfigureAwait(true)
just to be sure. However, when the awaited Task finishes it is continuing on a worker thread. After some digging I stumbled upon this question, which at least pointed me toward a possible cause. I added
var context = SynchronizationContext.Current;
var scheduler = TaskScheduler.Current;
just before the awaited function so I could see if the TaskAwaiter was able to capture the context. It seems the context is null but the scheduler is assigned a valid reference. However, when the awaited task completed it was still continuing on a worker thread. So I dug a little more.
Using Resharper I found this little gem in Task.SetContinuationForAwait
:
// If the user wants the continuation to run on the current "context" if there is one...
if (continueOnCapturedContext)
{
// First try getting the current synchronization context.
// If the current context is really just the base SynchronizationContext type,
// which is intended to be equivalent to not having a current SynchronizationContext at all,
// then ignore it. This helps with performance by avoiding unnecessary posts and queueing
// of work items, but more so it ensures that if code happens to publish the default context
// as current, it won't prevent usage of a current task scheduler if there is one.
var syncCtx = SynchronizationContext.CurrentNoFlow;
if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
{
tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark);
}
else
{
// If there was no SynchronizationContext, then try for the current scheduler.
// We only care about it if it's not the default.
var scheduler = TaskScheduler.InternalCurrent;
if (scheduler != null && scheduler != TaskScheduler.Default)
{
tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark);
}
}
}
I set break points on the two if statements and found that both syncCtx
and scheduler
were null so this explains why the awaited task was continuing on a worker thread but it doesn't explain why SynchronizationContext.Current
is null on the UI thread or why TaskScheduler.Current
is null at the point the continuation is about to be invoked.