25

Trying to use new C# 5 async model it was surprising to me AspNetSynchronizationContext is an internal class (as well as AspNetSynchronizationContextBase base). Thus undocumented. But it's essential to know what it does when utilizing async/await feature within your ASP.NET code. Am I correct that It does guarantee your continuations will get the same HttpContext.Current as original callers? It does not guarantee the continuations will execute on the same thread as the callers?

If the latter assumption is not true and I get the original thread can I be sure to get the same thread context in continuations? I mean principal/culture associated with the thread and thread local storage? That's important because ASP.NET localization relies on thread's culture and my application relies on .NET role security model (thread's principal).

UserControl
  • 14,766
  • 20
  • 100
  • 187
  • All I know for sure is that when you resume after an await call, whatever thread you get has it's 'Name' reset to nothing. Once it goes back to the pool, it's cleared. As for other properties associated with the thread... I don't know. – Triynko Jun 22 '18 at 05:19

2 Answers2

29

Am I correct that It does guarantee your continuations will get the same HttpContext.Current as original callers? It does not guarantee the continuations will execute on the same thread as the callers?

Yes, HttpContext.Current is preserved, and yes, the continuations may execute on a different thread.

I mean principal/culture associated with the thread and thread local storage? That's important because ASP.NET localization relies on thread's culture and my application relies on .NET role security model (thread's principal).

Ordinary thread-local storage is lost. You can mitigate this by using LogicalCallContext (which flows with ExecutionContext), but with async it's easier to just reference the variables directly.

Principal is always preserved; to do otherwise would be a security risk. This flows with ExecutionContext.

I believe that culture flows with AspNetSynchronizationContext, but I haven't tested this out on .NET 4.5's new implementation.


You may find my MSDN article on SynchronizationContext helpful. It's not official documentation (I don't work for Microsoft), but at least it's something. Note that the AspNetSynchronizationContext referenced in that article is now called LegacyAspNetSynchronizationContext in .NET 4.5.

Another great resource is Stephen Toub's ExecutionContext vs. SynchronizationContext.

Community
  • 1
  • 1
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Great answer! Exactly what i wanted to hear plus few useful links. Thank you very much! – UserControl Sep 30 '12 at 13:00
  • I'm assuming that `AspNetSynchronizationContext` behaves a bit odd when it comes to `Thread.CurrentPrincipal`: http://stackoverflow.com/a/12030785/463785 do you know if this is the fault of `AspNetSynchronizationContext` or some other deep level ASP.NET stuff? – tugberk Jan 28 '13 at 14:51
  • I can't say for sure. It's possible that the core .NET context (`ExecutionContext`) has some special rules for `CurrentPrincipal` (for security reasons), and ASP.NET can't overcome this (e.g. in a partial-trust scenario). But that's just conjecture. – Stephen Cleary Jan 28 '13 at 16:09
6

Well, while capturing the ExecutionContext is always guaranteed, capturing and running the execution on the same SynchronizationContext depends on the Awaiter.

The most common awaiter (the type returned by GetAwaiter() method that's internally called when you "await" something) is TaskAwaiter that returned by Task.GetAwaiter(). By default, TaskAwaiter will capture the current SynchronizationContext and run the continuation delegate on the captured SynchronizationContext. Which means, you'll be able to use HttpContext.Current in the rest of your method, and you don't mind that it'll run as a continuation. So, this code will works as expected (the part where you writes "B" will run on the same synchronizationcontext as the first line):

HttpContext.Current.Response.Write("A");
await Task.Delay(1000);
HttpContext.Current.Response.Write("B")

You can change this behavior by using Task.ConfigureAwait(false) method, that tells the awaiter to not marshal the rest of the method back to the original SynchronizationContext.

Of course, if you use Task.Run or Task.Factory.StartNew in the async method you're calling, it's your responsibility to capture the SynchronizationContext again.

Best of luck.

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94
Shahar Gvirtz
  • 2,418
  • 1
  • 14
  • 17
  • Yeah, but I think that's not what the question asks. It asks what specifically happens in ASP.NET if you *do* capture the context. – svick Sep 30 '12 at 11:10
  • then sure, you have it all... that's the definition of the context. it's applies both to the ExecutionContext (which captured anyway) and the SynchronizationContext (which is actually part of the ExceutionContext, but it can be captured and can be not captured). – Shahar Gvirtz Sep 30 '12 at 11:13