Does VB.NET observe cancellation immediately after a synchronously completed await?
0 reputation · 02 Dec 2025, 13:12 UTC
0 reputation · 02 Dec 2025, 13:12 UTC
When an async method in VB.NET contains multiple await points and a CancellationToken is cancelled after the first awaited task completes synchronously, the runtime may not observe the cancellation until the next await point. This creates a window where the continuation executes despite the token being cancelled.
The goal is to determine whether the VB.NET state machine inserts a cancellation check immediately after a synchronously completed await, or if the check is deferred until the next asynchronous suspension point. This behavior is documented as implementation-dependent and may vary across .NET runtime versions.
ThrowIfCancellationRequested call after a synchronously completed await?32275 reputation · 02 Dec 2025, 19:24 UTC
No. The VB.NET async state machine does not insert a cancellation check immediately after an await whose task has already completed synchronously. If the token is cancelled before (or during) that await, the code between it and the next real suspension point runs to completion. Cancellation is only observed at the next await that actually yields, or where you call ThrowIfCancellationRequested yourself.
The VB.NET compiler generates a state machine that mirrors the C# implementation. An Await lowers roughly to: get the awaiter, check IsCompleted, and if the task is already done, call GetResult() and fall through to the next statement without consulting any CancellationToken. The token is not implicitly threaded into the state machine; the compiler has no knowledge of which token you intend to monitor. Cancellation is only observed when:
token.ThrowIfCancellationRequested().Task.Delay(ms, token)), and that operation is the thing being awaited.Regarding your specific questions:
ThrowIfCancellationRequested after any await, synchronous or otherwise.If your method performs meaningful work between awaits, insert an explicit check at the point where you require prompt observation:
Public Async Function DoWorkAsync(ct As CancellationToken) As Task
Await Task.CompletedTask ' completes synchronously
ct.ThrowIfCancellationRequested() ' observe cancellation here
' ... work between awaits ...
Await Task.Delay(100, ct) ' also observes via the token
End FunctionTo verify this behavior, cancel a CancellationTokenSource before entering an async method. Await Task.CompletedTask, perform a noticeable operation (like a loop), and then await Task.Delay(1, ct). You will observe that the loop completes and the OperationCanceledException is only thrown at the Task.Delay call. Adding ct.ThrowIfCancellationRequested() immediately after the first await will move the exception to that point.
This analysis assumes standard async/await usage in VB.NET. Note that if the awaited expression changes from a synchronously completed task to a genuine asynchronous operation (like real I/O), the timing of cancellation observation will shift because the underlying API will likely check the token before yielding. Treat cancellation as cooperative and place explicit checks at the granularity your application requires.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.