Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is ASP.NET HttpContext.Current not set when starting a task with current synchronization context

Tags:

I was playing around with asynchronous features of .NET a little bit and came up with a situation that I couldn't really explain. When executing the following code inside a synchronous ASP.NET MVC controller

var t = Task.Factory.StartNew(()=>{
        var ctx = System.Web.HttpContext.Current;
        //ctx == null here
},
    CancellationToken.None,
    TaskCreationOptions.None,
    TaskScheduler.FromCurrentSynchronizationContext()
);

t.Wait();

ctx is null within the delegate. Now to my understanding, the context should be restored when you use the TaskScheduler.FromCurrentSynchronizationContext() task scheduler. So why isn't it here? (I can, btw, see that the delegate gets executed synchronously on the same thread).

Also, from msdn, a TaskScheduler.FromCurrentSynchronizationContext() should behave as follows:

All Task instances queued to the returned scheduler will be executed through a call to the Post method on that context.

However, when I use this code:

var wh = new AutoResetEvent(false);

SynchronizationContext.Current.Post(s=> {
    var ctx = System.Web.HttpContext.Current;
    //ctx is set here
    wh.Set();
    return;
},null);

wh.WaitOne();

The context is actually set.

I know that this example is little bit contrived, but I'd really like to understand what happens to increase my understanding of asynchronous programming on .NET.

like image 941
Freek Avatar asked Dec 20 '12 11:12

Freek


People also ask

What is Synchronization context in c#?

SynchronizationContext is a representation of the current environment that our code is running in. That is, in an asynchronous program, when we delegate a unit of work to another thread, we capture the current environment and store it in an instance of SynchronizationContext and place it on Task object.

Is HttpContext thread safe?

HttpContext access from a background threadHttpContext isn't thread-safe. Reading or writing properties of the HttpContext outside of processing a request can result in a NullReferenceException.

What is HttpContext in ASP NET MVC?

The HttpContext object constructed by the ASP.NET Core web server acts as a container for a single request. It stores the request and response information, such as the properties of request, request-related services, and any data to/from the request or errors, if there are any.


2 Answers

Your observations seem to be correct, it is a bit puzzling. You specify the scheduler as "TaskScheduler.FromCurrentSynchronizationContext()". This associates a new "SynchronizationContextTaskScheduler". Now if you look into this class it uses: enter image description here

So if the task scheduler has access to the same "Synchronization Context" and that should reference "LegacyAspNetSychronizationContext". So surely it appears that HttpContext.current should not be null.

In the second case, when you use a SychronizationContext (Refer:MSDN Article) the thread's context is shared with the task:

"Another aspect of SynchronizationContext is that every thread has a “current” context. A thread’s context isn’t necessarily unique; its context instance may be shared with other threads."


SynchronizationContext.Current is provided by LegacyAspNetSychronizationContext in this case and internally has a reference to HttpApplication.

When the Post method has to invoke registered callback, it calls HttpApplication.OnThreadEnter, which ultimately results in setting of the current thread's context as HttpCurrent.Context:

enter image description here

All the classes referenced here are defined as internal in the framework and is making it a bit difficult to investigate further.

PS: Illustrating that both SynchornizationContext objects in fact point to "LegacyAspNetSynchronizationContext": enter image description here

like image 65
coder_bro Avatar answered Oct 14 '22 22:10

coder_bro


I was googling for HTTPContext info some time ago. And I found this:

http://odetocode.com/articles/112.aspx

It's about threading and HTTPContext. There is good explanation:

The CallContext provides a service extremely similar to thread local storage (except CallContext can perform some additional magic during a remoting call). Thread local storage is a concept where each logical thread in an application domain has a unique data slot to keep data specific to itself. Threads do not share the data, and one thread cannot modify the data local to a different thread. ASP.NET, after selecting a thread to execute an incoming request, stores a reference to the current request context in the thread’s local storage. Now, no matter where the thread goes while executing (a business object, a data access object), the context is nearby and easily retrieved.

Knowing the above we can state the following: if, while processing a request, execution moves to a different thread (via QueueUserWorkItem, or an asynchronous delegate, as two examples), HttpContext.Current will not know how to retrieve the current context, and will return null. You might think one way around the problem would be to pass a reference to the worker thread

So you have to create reference to your HTTPContext.Current via some variable and this variable will be adressed from other threads you will create in your code.

like image 24
n.podbielski Avatar answered Oct 14 '22 23:10

n.podbielski