Skip to content

Fix flaky AsyncContinuation cDAC dump test on osx-arm64 by using Task.Yield#128004

Merged
max-charlamb merged 1 commit into
dotnet:mainfrom
max-charlamb:fix-async-continuation-debuggee-yield
May 10, 2026
Merged

Fix flaky AsyncContinuation cDAC dump test on osx-arm64 by using Task.Yield#128004
max-charlamb merged 1 commit into
dotnet:mainfrom
max-charlamb:fix-async-continuation-debuggee-yield

Conversation

@max-charlamb
Copy link
Copy Markdown
Member

@max-charlamb max-charlamb commented May 10, 2026

Note

This pull request was prepared with assistance from GitHub Copilot.

Problem

AsyncContinuationDumpTests.ThreadLocalContinuation_IsContinuation fails intermittently on osx-arm64 R2R with:

Could not find AsyncDispatcherInfo type in CoreLib

Failure tracked in #127774. Recently observed in builds 1413917 and 1411121.

Fixes #127774

Root cause

The test relies on the runtime having executed AsyncHelpers+RuntimeAsyncTask<T>.DispatchContinuations(), which writes AsyncDispatcherInfo.t_current and forces the JIT to load the AsyncDispatcherInfo MethodTable.

The current debuggee uses await Task.Delay(1) to force a suspension. On fast machines (in particular Apple Silicon CI machines) the 1ms timer can fire before the runtime-async Await helper checks IsCompleted, so the awaiter is observed as already-completed and the await runs straight through synchronously. With no suspension there is no dispatch, no t_current write, and the AsyncDispatcherInfo MT is never loaded.

I downloaded the failing osx-arm64 dump and walked the crashed thread:

[2-4] FailFast
[5]   InnerAsync
[6]   OuterAsync   (async2 body)
[7]   OuterAsync   (thunk)
[8]   Main
[9]   CallEntryPoint

There is no DispatchContinuations frame on the stack. OuterAsync calls InnerAsync directly, confirming the await did not suspend.

I also walked all 3 modules' TypeDefToMethodTable maps and confirmed AsyncDispatcherInfo MT is genuinely null everywhere (i.e. cDAC is reporting reality - the type never got loaded). Object, Task, and Continuation MTs are all loaded as expected.

Fix

Replace await Task.Delay(1) with await Task.Yield() in InnerAsync.

Task.Yield() returns a YieldAwaitable whose awaiter's IsCompleted always returns false, so the continuation is unconditionally posted back via DispatchContinuations. This guarantees AsyncDispatcherInfo.t_current is written before InnerAsync resumes and calls FailFast, regardless of machine timing.

Validation

Posting as a draft to run CI across all platforms; the osx-arm64 R2R leg is the one to watch.

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the AsyncContinuation cDAC dump test debuggee to make async suspension deterministic, avoiding intermittent failures where the AsyncDispatcherInfo type hasn’t been forced to load on very fast machines.

Changes:

  • Replace await Task.Delay(1) with await Task.Yield() to guarantee a suspension point (since YieldAwaiter.IsCompleted is always false).
  • Add an explanatory comment documenting why Task.Delay(1) was timing-dependent and why Task.Yield() is used instead.

@max-charlamb max-charlamb marked this pull request as ready for review May 10, 2026 21:34
Copilot AI review requested due to automatic review settings May 10, 2026 21:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

The AsyncContinuation cDAC dump test fails intermittently on osx-arm64
because 'await Task.Delay(1)' in InnerAsync can complete synchronously
(the timer fires before await checks IsCompleted), so the async chain
runs straight through with no continuation dispatch. AsyncDispatcherInfo
MT is never loaded and the test asserts that lookup succeeds.

Replace Task.Delay(1) with Task.Yield(). Task.Yield's awaiter always
reports IsCompleted=false, so the continuation is guaranteed to be
posted back via DispatchContinuations and AsyncDispatcherInfo.t_current
is guaranteed to be written before InnerAsync resumes and FailFasts.

Verified locally that with Task.Delay(1) the failing osx-arm64 dump
shows OuterAsync calling InnerAsync directly (no DispatchContinuations
on the stack), confirming the await did not suspend.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb force-pushed the fix-async-continuation-debuggee-yield branch from 23853f4 to 279786d Compare May 10, 2026 21:37
@max-charlamb max-charlamb marked this pull request as draft May 10, 2026 21:38
@max-charlamb max-charlamb marked this pull request as ready for review May 10, 2026 23:56
Copilot AI review requested due to automatic review settings May 10, 2026 23:56
@max-charlamb max-charlamb merged commit 5001116 into dotnet:main May 10, 2026
61 checks passed
@max-charlamb max-charlamb deleted the fix-async-continuation-debuggee-yield branch May 10, 2026 23:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

jakobbotsch pushed a commit to jakobbotsch/runtime that referenced this pull request May 12, 2026
….Yield (dotnet#128004)

> [!NOTE]
> This pull request was prepared with assistance from GitHub Copilot.

## Problem

`AsyncContinuationDumpTests.ThreadLocalContinuation_IsContinuation`
fails intermittently on **osx-arm64 R2R** with:

> Could not find AsyncDispatcherInfo type in CoreLib

Failure tracked in dotnet#127774. Recently observed in builds
[1413917](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1413917)
and
[1411121](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1411121).

Fixes dotnet#127774

## Root cause

The test relies on the runtime having executed
`AsyncHelpers+RuntimeAsyncTask<T>.DispatchContinuations()`, which writes
`AsyncDispatcherInfo.t_current` and forces the JIT to load the
`AsyncDispatcherInfo` MethodTable.

The current debuggee uses `await Task.Delay(1)` to force a suspension.
On fast machines (in particular Apple Silicon CI machines) the 1ms timer
can fire before the runtime-async `Await` helper checks `IsCompleted`,
so the awaiter is observed as already-completed and the await runs
straight through synchronously. With no suspension there is no dispatch,
no `t_current` write, and the `AsyncDispatcherInfo` MT is never loaded.

I downloaded the failing osx-arm64 dump and walked the crashed thread:

```
[2-4] FailFast
[5]   InnerAsync
[6]   OuterAsync   (async2 body)
[7]   OuterAsync   (thunk)
[8]   Main
[9]   CallEntryPoint
```

There is no `DispatchContinuations` frame on the stack. `OuterAsync`
calls `InnerAsync` directly, confirming the await did not suspend.

I also walked all 3 modules' `TypeDefToMethodTable` maps and confirmed
`AsyncDispatcherInfo` MT is genuinely null everywhere (i.e. cDAC is
reporting reality - the type never got loaded). `Object`, `Task`, and
`Continuation` MTs are all loaded as expected.

## Fix

Replace `await Task.Delay(1)` with `await Task.Yield()` in `InnerAsync`.

`Task.Yield()` returns a `YieldAwaitable` whose awaiter's `IsCompleted`
always returns `false`, so the continuation is unconditionally posted
back via `DispatchContinuations`. This guarantees
`AsyncDispatcherInfo.t_current` is written before `InnerAsync` resumes
and calls `FailFast`, regardless of machine timing.

## Validation

Posting as a draft to run CI across all platforms; the osx-arm64 R2R leg
is the one to watch.

Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test failure: Microsoft.Diagnostics.DataContractReader.DumpTests.AsyncContinuationDumpTests.ThreadLocalContinuation_IsContinuation

3 participants