SynchronizationContext ConfigureAwait and deadlocks

 
 
  • Gérald Barré
The previous post covered ExecutionContext, which decides what state a continuation sees. This post is about the other thing await captures, which decides where the continuation runs. It is also the mechanism behind the single most famous bug in async C#: calling .Result and hanging forever. What a SynchronizationContext is A SynchronizationContext is an abstraction over "post this callback to the right… [read more]

Async method builders and where the allocations go

 
 
  • Gérald Barré
The post on the compiler rewrite pulled an async method apart and found a generated struct with a MoveNext method. One field in that struct was left unexplained: public AsyncTaskMethodBuilder<int> <>t__builder; The builder is the other half of the machinery. The state machine knows where it is; the builder knows what to hand back to the caller and how to get running again. It is also the piece you can… [read more]

Writing your own CancellationToken from scratch

 
 
  • Gérald Barré
The previous post built a MyTask that can start work and report that it finished. It cannot stop anything. That gap is not an oversight in the implementation: stopping is a separate concern, and .NET models it as a separate pair of types. This post builds those too. MyCancellationTokenSource and MyCancellationToken, from scratch, and then wires them into MyTask. The implementation is short. The design… [read more]

How the C# compiler rewrites an async method

 
 
  • Gérald Barré
async and await are not runtime features up to .NET 10. They are a compiler rewrite. When you mark a method async, the C# compiler removes the method body, moves it into a generated type, and replaces the original method with a few lines that create and start that type. Everything else, the scheduling, the exception handling, the value that eventually lands in the returned Task, is ordinary code you could… [read more]