Lesson 1 of 4 | Control Flow, Cancellation, and Time Budgets
CancellationToken Ownership and Propagation
Cancellation is a cooperative protocol. A token carries a request; it does not terminate a thread, roll back a transaction, or guarantee that a remote server stopped work.
Decide who owns cancellation
At an ASP.NET Core boundary, the request owns HttpContext.RequestAborted. Application services should accept that token and pass it to every operation that can safely stop:
public async Task<OrderSummary> HandleAsync(
Guid orderId,
CancellationToken cancellationToken)
{
var order = await repository.GetAsync(orderId, cancellationToken);
var price = await pricing.GetAsync(order.Sku, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
return new OrderSummary(order, price);
}
A service may create a linked token when it owns an additional shutdown or time-budget signal. It must dispose the linked CancellationTokenSource.
Propagate without changing meaning
Use the same token for work that belongs to the caller's operation. Do not replace it with CancellationToken.None merely to make a failing test pass. Conversely, cleanup that must complete after cancellation may need its own short, explicitly documented token.
Avoid catching OperationCanceledException as a general failure. A useful boundary pattern is:
try
{
await handler.HandleAsync(request, requestAborted);
}
catch (OperationCanceledException) when (requestAborted.IsCancellationRequested)
{
// Client disconnected or canceled. Record at an appropriate level.
throw;
}
The exception is considered cancellation only when it is associated with an expected canceled token. An unrelated timeout or a library bug should not be mislabeled.
CPU-bound loops must observe the token
Token-aware I/O APIs observe cancellation while awaiting. CPU-bound work needs explicit checkpoints:
for (var i = 0; i < records.Count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
Transform(records[i]);
}
Check often enough to respond promptly, but not on every microscopic operation when the check itself would distort a hot loop.
Interview traps
- A token is a value that refers to shared cancellation state; disposing the token itself is not required.
- Only the source can request cancellation.
- Cancellation does not automatically undo completed side effects.
- If a database write committed before cancellation, the system needs idempotency or compensation—not wishful rollback.
- Public async APIs normally put the token last.
Review checklist
Trace one request from controller to database and remote HTTP calls. At each await, ask whether cancellation is supported, whether stopping is safe, and which component owns the token. A strong design makes that ownership visible instead of scattering new CancellationTokenSource() throughout the code.