Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the current SynchronizationContext be null?

https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

It's a great article, and I'm aware that all details could not be covered because that would essentially involve pasting the source code of .NET framework. So quoting the text:

Each thread has a current context. If "Current" is null, then the thread's current context is "new SynchronizationContext()", by convention.

On the other hand, however:

By default, the current SynchronizationContext is captured at an await point, and this SynchronizationContext is used to resume after the await (more precisely, it captures the current SynchronizationContext unless it is null, in which case it captures the current TaskScheduler)

These two statements pretty much contradict each other, so I'm thinking this is the result of some simplification that has been made by the author (I'm fine with it).

Could anyone explain this? Code that might help in answering my question (look for syncCtx variable), this piece of code is related to the second quote.

like image 414
user4205580 Avatar asked Nov 29 '15 15:11

user4205580


1 Answers

The relevant piece of code you're looking for is inside the internal method Task.SetContinuationForAwait:

// 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);
    }
}

It actually does two checks, first to see that it isn't null, and the second to make sure it isn't the default SynchronizationContext, which I think is the key point here.

If you open up a Console Application and try to fetch the SynchronizationContext.Current, you'll definitely see that it can be null.

class Program
{
    public static void Main(string[] args)
    { 
        Console.WriteLine(SynchronizationContext.Current == null ? "NoContext" :
                                                                   "Context!");
    }
}
like image 73
Yuval Itzchakov Avatar answered Oct 26 '22 22:10

Yuval Itzchakov