You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Support moving continuations to SynchronizationContext/TaskScheduler (#117314)
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 continuations on the same thread 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 this PR disables code 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. Work to reenable this will be done 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).
0 commit comments