# Async Internals, ValueTask and Cancellation

## 1. The State Machine Behind async/await

The compiler rewrites an `async` method into a **state machine** struct. Each `await` becomes: get an awaiter; if it's **already completed**, continue synchronously (no allocation, no thread switch); otherwise store the current state, register a **continuation**, and return to the caller. When the awaited operation completes, the continuation resumes the state machine at the saved state.

```text
caller → MyMethodAsync() runs synchronously until the first incomplete await
       ← returns an incomplete Task
... I/O completes on an I/O completion port / epoll ...
thread pool thread → continuation → resumes after the await → eventually completes the Task
```

No thread is blocked while waiting for I/O. That's the whole point: a server can handle thousands of in-flight requests with a few dozen threads.

## 2. SynchronizationContext and ConfigureAwait

By default `await` captures the current **SynchronizationContext** (UI thread, classic ASP.NET request context) and resumes there. `ConfigureAwait(false)` says "resume on any thread pool thread".

- **ASP.NET Core has no SynchronizationContext**, so in app code `ConfigureAwait(false)` makes no difference.
- **Libraries** should still use it, because they may be called from UI or legacy contexts.
- Blocking on async code (`.Result`, `.Wait()`) in a context that has a single thread deadlocks: the continuation needs the thread that's blocked waiting for it. In ASP.NET Core it doesn't deadlock but causes **thread pool starvation** under load.

## 3. Task vs ValueTask

`Task` is a class: an allocation per call (except cached completed tasks like `Task.CompletedTask`). `ValueTask<T>` is a struct that can hold a result **directly** when the method completes synchronously, avoiding the allocation on hot paths such as cache hits or buffered reads.

```csharp
public ValueTask<User> GetAsync(int id) =>
    _cache.TryGetValue(id, out var u) ? new ValueTask<User>(u) : new ValueTask<User>(LoadAsync(id));
```

Rules for consuming a `ValueTask`: **await it exactly once**, don't await it concurrently, and don't call `.Result` before it completes. If you need to do any of those, call `.AsTask()`. Use `ValueTask` only when measurements show the allocation matters; `Task` is the safe default.

## 4. Cancellation and Timeouts

Cancellation is cooperative: a `CancellationToken` flows through the call chain, and code checks it or passes it to APIs that support it.

```csharp
using var cts = CancellationTokenSource.CreateLinkedTokenSource(requestAborted);
cts.CancelAfter(TimeSpan.FromSeconds(2));             // timeout
var data = await httpClient.GetStringAsync(url, cts.Token);
```

- In ASP.NET Core, `HttpContext.RequestAborted` is cancelled when the client disconnects; accept a `CancellationToken` parameter in endpoints.
- Distinguish a **timeout** from the **caller cancelling**: catch `OperationCanceledException when (!outer.IsCancellationRequested)` and rethrow as `TimeoutException`.
- Dispose `CancellationTokenSource`s that use timers (`CancelAfter`).

## 5. Concurrency Helpers

- `Task.WhenAll` to run independent operations concurrently; `Task.WhenAny` for races and timeouts; `Parallel.ForEachAsync` with `MaxDegreeOfParallelism` for bounded concurrency.
- `SemaphoreSlim.WaitAsync` as an async lock or limiter (`lock` can't contain `await`).
- Avoid **async void** except for event handlers: exceptions crash the process and callers can't await.
- `IAsyncEnumerable<T>` with `await foreach` streams results as they arrive.

## Common Pitfalls

1. **Sync over async** (`.Result`, `.Wait()`): deadlocks or thread pool starvation.
2. **Awaiting a ValueTask twice** or storing it for later.
3. **Fire-and-forget** tasks without error handling.
4. **Not passing CancellationTokens** to I/O calls.
5. **`Task.Run` in ASP.NET Core request handlers** to "make it async": it just moves work to another pool thread.

## Quick Reference

```text
async = compiler state machine · completed awaits continue synchronously · no thread blocked on I/O
ConfigureAwait(false) in libraries · ASP.NET Core has no SynchronizationContext · never block on async
ValueTask: hot paths that often complete synchronously · await once · .AsTask() otherwise
CancellationToken everywhere · linked CTS + CancelAfter for timeouts · SemaphoreSlim for async locks
```
