Skip to content

Support moving continuations to SynchronizationContext/TaskScheduler #117314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

jakobbotsch
Copy link
Member

@jakobbotsch jakobbotsch commented Jul 4, 2025

This adds support for moving continuations after task awaits to the right context:

  • For unconfigured task awaits or ConfigureAwait(true), continuations are posted to the captured SynchronizationContext or TaskScheduler by the BCL
  • For ConfigureAwait(false) continuations are moved to the thread pool

The JIT inserts a call to a new AsyncHelpers.CaptureContinuationContext when suspending because of a task await. That function either captures the current SynchronizationContext or TaskScheduler into the continuation in a known place. Later the BCL will fetch the context from the continuation object based on a flag to determine whether the continuation needs to be posted somewhere else or whether it can be inlined. There is a new Task-derived ThunkTask that replaces the existing C#-async-function-with-a-loop and which handles the continuations explicitly.

Inlining of continuations currently has no check on stack depth. This might be necessary to add. There are some differences compared to async 1 with how continuations are executed. In particular some common cases that resulted in unbounded stack usage with async 1 does not result in increasing stack usage with runtime async. Hence I am not 100% sure whether we need a mitigation or not, but more investigation is needed from my side. I'd like to leave that as a follow-up.

To get to the right behavior I have disabled inlining of task-awaited functions. This is a large concession, but getting the behavior right in the face of inlining proved to be quite tricky with the JIT's current internal representation. It is near top priority for me to reenable the inlining support, but I would like to work on it separately.

There is still one missing piece for correctness: saving and restoring the Thread._synchronizationContext field around async calls. It runs into similar trickiness when trying to represent the expected behavior (which is that the field is restored around synchronous calls, but not restored on resumption).

@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 4, 2025
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI runtime-async
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant