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

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


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


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.


Your results are odd - are you sure there's nothing else going on?


Your first example ( with Task ) only works because Task.Wait() can run the task body "inline".

If you put a breakpoint in the task lambda and look at the call stack, you will see that the lambda is being called from inside the Task.Wait() method - there is no concurrency. Since the task is being executed with just normal synchronous method calls, HttpContext.Current must return the same value as it would from anywhere else in your controller method.


Your second example ( with SynchronizationContext.Post ) will deadlock and your lambda will never run.

This is because you are using an AutoResetEvent, which doesn't "know" anything about your Post. The call to WaitOne() will block the thread until the AutoResetEvent is Set. At the same time, the SynchronizationContext is waiting for the thread to be free in order to run the lambda.

Since the thread is blocked in WaitOne, the posted lambda will never execute, which means the AutoResetEvent will never be set, which means the WaitOne will never be satisfied. This is a deadlock.