Phase 22 of 30 · Topic 22.1

Async State Machine Decomposition (IAsyncStateMachine)

1Concept

When a method is marked `async`, the Roslyn compiler synthesizes an internal `struct AsyncStateMachine : IAsyncStateMachine` containing a state variable, builder, and hoisted local variables.

2Architecture Diagram

async Task<int> FetchDataAsync() { int x = 10; await Task.Delay(10); return x; }
       │
       ▼ Compiler Decomposition:
struct FetchDataStateMachine : IAsyncStateMachine
{
    public int State; // -1 = Running, 0 = Awaiting, -2 = Finished
    public int x;     // Hoisted local variable
    public AsyncTaskMethodBuilder<int> Builder;
    public void MoveNext() { ... }
}

3Code Example

C# 13 & .NET 9
using System;
using System.Threading.Tasks;

public class AsyncInternalsDemo
{
    public static async Task<string> ExecuteWorkflowAsync()
    {
        int step = 1;
        await Task.Yield(); // Forces async state machine continuation dispatch
        step = 2;
        return $"Completed Step {step}";
    }

    public static async Task Main()
    {
        string res = await ExecuteWorkflowAsync();
        Console.WriteLine($"Workflow Result: {res}");
    }
}

4Expected Output

Workflow Result: Completed Step 2

5Key Takeaways

  • The async state machine struct is stack-allocated until an incomplete `await` forces heap boxing.
  • State number tracks progress across multiple await continuation points.
  • Avoid splitting methods into unnecessary tiny async helper methods.