diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index 33cd1e03e205db..fb24a4a33daa08 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; @@ -30,20 +31,20 @@ internal enum ContinuationFlags // Otherwise the exact offset of the member is computed as // DataOffset + (index - 1) * PointerSize // - ExecutionContextIndexFirstBit = 3, + ExecutionContextIndexFirstBit = 4, ExecutionContextIndexNumBits = 2, - ContinuationContextIndexFirstBit = 5, + ContinuationContextIndexFirstBit = 6, ContinuationContextIndexNumBits = 2, - ExceptionIndexFirstBit = 7, + ExceptionIndexFirstBit = 8, ExceptionIndexNumBits = 3, // For JIT, the continuation stores space for every possible type of // async callee's result. We need to represent the offset to each of // these, so we allocate the rest of the bits for this. - ResultIndexFirstBit = 10, - ResultIndexNumBits = 22, + ResultIndexFirstBit = 11, + ResultIndexNumBits = 21, } // Keep in sync with CORINFO_AsyncResumeInfo in corinfo.h @@ -207,6 +208,7 @@ public static partial class AsyncHelpers // task-returning thunk, or DispatchContinuations. A pointer to this // state is kept in the runtime async TLS. This storage method avoids // costly write barriers on the hot path of suspension/resumption. + [NonVersionable] private ref struct RuntimeAsyncStackState { // The following are the possible introducers of asynchrony into a chain of awaits. @@ -221,39 +223,12 @@ private ref struct RuntimeAsyncStackState public ExecutionContext? LeafExecutionContext; public SynchronizationContext? LeafSynchronizationContext; - // When we enter the root of the async chain (either an async thunk - // or DispatchContinuations), the contexts are captured into these - // fields. - public ExecutionContext? RootExecutionContext; - public SynchronizationContext? RootSynchronizationContext; - public unsafe RuntimeAsyncStackState* Next; - - public void Push(Thread thread) - { - RootExecutionContext = thread._executionContext; - RootSynchronizationContext = thread._synchronizationContext; - } - - public void Pop(Thread thread) - { - // The common case is that these have not changed, so avoid the cost of a write barrier if not needed. - if (RootSynchronizationContext != thread._synchronizationContext) - { - // Restore changed SynchronizationContext back to previous - thread._synchronizationContext = RootSynchronizationContext; - } - - ExecutionContext? currentExecutionCtx = thread._executionContext; - if (RootExecutionContext != currentExecutionCtx) - { - ExecutionContext.RestoreChangedContextToThread(thread, RootExecutionContext, currentExecutionCtx); - } - } } // Used during suspensions to hold the continuation chain and on what we are waiting. - // Methods like FinalizeTaskReturningThunk will unlink the state and wrap into a Task. + // Methods like CreateRuntimeAsyncTask will unlink the state and wrap into a Task. + [NonVersionable] private unsafe struct RuntimeAsyncAwaitState { public Continuation? SentinelContinuation; @@ -282,22 +257,51 @@ public void CaptureContexts() // At the start of an async chain (task-returning thunk or DispatchContinuations) this function // is called + [NonVersionable] public void Push(RuntimeAsyncStackState* stackState) { stackState->Next = StackState; StackState = stackState; - stackState->Push(CurrentThread ??= Thread.CurrentThread); + CurrentThread ??= Thread.CurrentThread; } // This function is called at the end of an async chain + [NonVersionable] public void Pop() { Debug.Assert(CurrentThread != null); - StackState->Pop(CurrentThread); StackState = StackState->Next; } } + private struct AsyncContexts + { + private SynchronizationContext? _synchronizationContext; + private ExecutionContext? _executionContext; + + public AsyncContexts(Thread thread) + { + _synchronizationContext = thread._synchronizationContext; + _executionContext = thread._executionContext; + } + + public void Pop(Thread thread) + { + // The common case is that these have not changed, so avoid the cost of a write barrier if not needed. + if (_synchronizationContext != thread._synchronizationContext) + { + // Restore changed SynchronizationContext back to previous + thread._synchronizationContext = _synchronizationContext; + } + + ExecutionContext? currentExecutionCtx = thread._executionContext; + if (_executionContext != currentExecutionCtx) + { + ExecutionContext.RestoreChangedContextToThread(thread, _executionContext, currentExecutionCtx); + } + } + } + [ThreadStatic] private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState; @@ -351,7 +355,7 @@ private static unsafe Continuation AllocContinuationClass(Continuation prevConti [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe void AwaitTask(Task task, ConfigureAwaitOptions options) + private static unsafe void Suspend(Task task, ConfigureAwaitOptions options) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -386,7 +390,7 @@ private static unsafe void AwaitTask(Task task, ConfigureAwaitOptions options) [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe T AwaitTask(Task task, ConfigureAwaitOptions options) + private static unsafe T Suspend(Task task, ConfigureAwaitOptions options) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -432,7 +436,7 @@ private static unsafe T AwaitTask(Task task, ConfigureAwaitOptions options /// ValueTask whose completion we are awaiting. [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe void TransparentAwaitValueTask(ValueTask valueTask) + private static unsafe void TransparentSuspend(ValueTask valueTask) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -481,7 +485,7 @@ private static unsafe void TransparentAwaitValueTask(ValueTask valueTask) [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe void AwaitValueTaskSource(IValueTaskSource source, short token, bool continueOnCapturedContext) + private static unsafe void Suspend(IValueTaskSource source, short token, bool continueOnCapturedContext) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -518,7 +522,7 @@ private static unsafe void AwaitValueTaskSource(IValueTaskSource source, short t [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe T TransparentAwaitValueTaskOfT(ValueTask valueTask) + private static unsafe T TransparentSuspend(ValueTask valueTask) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -567,7 +571,7 @@ private static unsafe T TransparentAwaitValueTaskOfT(ValueTask valueTask) [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe T AwaitValueTaskSource(IValueTaskSource source, short token, bool continueOnCapturedContext) + private static unsafe T Suspend(IValueTaskSource source, short token, bool continueOnCapturedContext) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -609,7 +613,7 @@ private static unsafe T AwaitValueTaskSource(IValueTaskSource source, shor /// Task whose completion we are awaiting. [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe void TransparentAwait(Task task) + private static unsafe void TransparentSuspend(Task task) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -639,7 +643,7 @@ private static unsafe void TransparentAwait(Task task) /// Task whose completion we are awaiting. [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] - private static unsafe T TransparentAwaitOfT(Task task) + private static unsafe T TransparentSuspend(Task task) { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation(); @@ -664,14 +668,17 @@ private static unsafe T TransparentAwaitOfT(Task task) return default!; } + // The next four overloads 'TransparentAwait' are called by the JIT in + // async versions of task-returning methods to await the task that + // would normally be returned. [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] - private static void TransparentAwaitWithResult(Task task) + private static void TransparentAwait(Task task) { if (!task.IsCompleted) { TailAwait(); - TransparentAwait(task); + TransparentSuspend(task); return; } @@ -680,12 +687,12 @@ private static void TransparentAwaitWithResult(Task task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] - private static void TransparentAwaitWithResult(ValueTask task) + private static void TransparentAwait(ValueTask task) { if (!task.IsCompleted) { TailAwait(); - TransparentAwaitValueTask(task); + TransparentSuspend(task); return; } @@ -694,12 +701,12 @@ private static void TransparentAwaitWithResult(ValueTask task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] - private static T TransparentAwaitWithResult(Task task) + private static T TransparentAwait(Task task) { if (!task.IsCompleted) { TailAwait(); - return TransparentAwaitOfT(task); + return TransparentSuspend(task); } TaskAwaiter.ValidateEnd(task); @@ -708,12 +715,12 @@ private static T TransparentAwaitWithResult(Task task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] - private static T TransparentAwaitWithResult(ValueTask task) + private static T TransparentAwait(ValueTask task) { if (!task.IsCompleted) { TailAwait(); - return TransparentAwaitValueTaskOfT(task); + return TransparentSuspend(task); } return task.Result; @@ -916,6 +923,8 @@ private unsafe void DispatchContinuations() ref RuntimeAsyncAwaitState awaitState = ref t_runtimeAsyncAwaitState; awaitState.Push(&stackState); + AsyncContexts contexts = new AsyncContexts(awaitState.CurrentThread!); + ref AsyncDispatcherInfo* refDispatcherInfo = ref AsyncDispatcherInfo.t_current; AsyncDispatcherInfo asyncDispatcherInfo; @@ -947,6 +956,7 @@ private unsafe void DispatchContinuations() newContinuation.Next = nextContinuation; HandleSuspended(ref awaitState); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; return; @@ -963,6 +973,7 @@ private unsafe void DispatchContinuations() TrySetCanceled(oce.CancellationToken, oce) : TrySetException(ex); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; @@ -982,6 +993,7 @@ private unsafe void DispatchContinuations() { bool successfullySet = TrySetResult(m_result); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; @@ -995,6 +1007,7 @@ private unsafe void DispatchContinuations() if (QueueContinuationFollowUpActionIfNecessary(asyncDispatcherInfo.NextContinuation)) { + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; return; @@ -1006,6 +1019,7 @@ private unsafe void DispatchContinuations() { SetContinuationState(asyncDispatcherInfo.NextContinuation); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; @@ -1027,6 +1041,8 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags ref RuntimeAsyncAwaitState awaitState = ref t_runtimeAsyncAwaitState; awaitState.Push(&stackState); + AsyncContexts contexts = new AsyncContexts(awaitState.CurrentThread!); + ref AsyncDispatcherInfo* refDispatcherInfo = ref AsyncDispatcherInfo.t_current; AsyncDispatcherInfo asyncDispatcherInfo; @@ -1065,6 +1081,7 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags RuntimeAsyncInstrumentationHelpers.AwaitSuspendedRuntimeAsyncContext(ref asyncDispatcherInfo, flags, curContinuation, newContinuation, awaitState.SentinelContinuation!.Next); InstrumentedHandleSuspended(flags, ref awaitState); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; return; @@ -1085,6 +1102,7 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags TrySetCanceled(oce.CancellationToken, oce) : TrySetException(ex); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; @@ -1108,6 +1126,7 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags bool successfullySet = TrySetResult(m_result); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; @@ -1123,6 +1142,7 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags { RuntimeAsyncInstrumentationHelpers.QueueSuspendedRuntimeAsyncContext(ref asyncDispatcherInfo, flags, asyncDispatcherInfo.NextContinuation); + contexts.Pop(awaitState.CurrentThread!); awaitState.Pop(); refDispatcherInfo = asyncDispatcherInfo.Next; return; @@ -1247,49 +1267,54 @@ private static void InstrumentedFinalizeRuntimeAsyncTask(RuntimeAsyncTask return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void FinalizeRuntimeAsyncTask(ref RuntimeAsyncAwaitState state, RuntimeAsyncTask task) { + // We restore back to the leaf context to call OnCompleted, so we + // need to push/pop contexts around that as well. + AsyncContexts contexts = new AsyncContexts(state.CurrentThread!); + if (AsyncInstrumentation.IsActive && AsyncInstrumentation.LoadFlags(out AsyncInstrumentation.Flags flags)) { if (AsyncInstrumentation.IsEnabled.AsyncProfiler(flags) || AsyncInstrumentation.IsEnabled.AsyncDebugger(flags)) { InstrumentedFinalizeRuntimeAsyncTask(task, ref state, flags); + contexts.Pop(state.CurrentThread!); return; } } task.HandleSuspended(ref state); + contexts.Pop(state.CurrentThread!); } // Change return type to RuntimeAsyncTask -- no benefit since this is used for Task returning thunks only #pragma warning disable CA1859 // When a Task-returning thunk gets a continuation result // it calls here to make a Task that awaits on the current async state. - private static Task FinalizeTaskReturningThunk(ref RuntimeAsyncAwaitState state) + private static Task CreateRuntimeAsyncTask(ref RuntimeAsyncAwaitState state) { RuntimeAsyncTask result = new(); - FinalizeRuntimeAsyncTask(ref state, result!); + FinalizeRuntimeAsyncTask(ref state, result); return result; } - private static Task FinalizeTaskReturningThunk(ref RuntimeAsyncAwaitState state) + private static Task CreateRuntimeAsyncTask(ref RuntimeAsyncAwaitState state) { RuntimeAsyncTask result = new(); - FinalizeRuntimeAsyncTask(ref state, result!); + FinalizeRuntimeAsyncTask(ref state, result); return result; } - private static ValueTask FinalizeValueTaskReturningThunk(ref RuntimeAsyncAwaitState state) + private static ValueTask CreateRuntimeAsyncValueTask(ref RuntimeAsyncAwaitState state) { // We only come to these methods in the expensive case (already // suspended), so ValueTask optimization here is not relevant. - return new ValueTask(FinalizeTaskReturningThunk(ref state)); + return new ValueTask(CreateRuntimeAsyncTask(ref state)); } - private static ValueTask FinalizeValueTaskReturningThunk(ref RuntimeAsyncAwaitState state) + private static ValueTask CreateRuntimeAsyncValueTask(ref RuntimeAsyncAwaitState state) { - return new ValueTask(FinalizeTaskReturningThunk(ref state)); + return new ValueTask(CreateRuntimeAsyncTask(ref state)); } private static Task TaskFromException(Exception ex) @@ -1545,19 +1570,6 @@ private static void FinishSuspensionWithContinuationContext(ref object continuat } } - [StackTraceHidden] - internal static T CompletedTaskResult(Task task) - { - TaskAwaiter.ValidateEnd(task); - return task.ResultOnSuccess; - } - - [StackTraceHidden] - internal static void CompletedTask(Task task) - { - TaskAwaiter.ValidateEnd(task); - } - // Instrumentation helpers called from InstrumentedDispatchContinuations. // These methods should not throw - exceptions would break the dispatch loop. internal static class RuntimeAsyncInstrumentationHelpers diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 4dfa9d39bc10c7..d085c08701d27f 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1792,20 +1792,20 @@ enum CorInfoContinuationFlags // Otherwise the exact offset of the member is computed as // OFFSETOF__CORINFO_Continuation__data + (index - 1) * PointerSize - CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT = 3, + CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT = 4, CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS = 2, - CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT = 5, + CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT = 6, CORINFO_CONTINUATION_CONTEXT_INDEX_NUM_BITS = 2, - CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT = 7, + CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT = 8, CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS = 3, // For JIT, the continuation stores space for every possible type of // async callee's result. We need to represent the offset to each of // these, so we allocate the rest of the bits for this. - CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT = 10, - CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS = 22, + CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT = 11, + CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS = 21, }; struct CORINFO_ASYNC_INFO diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e0177d76cd5826..19cb30e5cb287b 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* fcb1b400-696c-4425-a8a7-bb082430a217 */ - 0xfcb1b400, - 0x696c, - 0x4425, - {0xa8, 0xa7, 0xbb, 0x08, 0x24, 0x30, 0xa2, 0x17} +constexpr GUID JITEEVersionIdentifier = { /* 5fe1ab78-3c99-4866-8771-5596c7a0e181 */ + 0x5fe1ab78, + 0x3c99, + 0x4866, + {0x87, 0x71, 0x55, 0x96, 0xc7, 0xa0, 0xe1, 0x81} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 36167779d00519..0de724ff89d2bd 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -19,10 +19,10 @@ // src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. -#define READYTORUN_MAJOR_VERSION 24 +#define READYTORUN_MAJOR_VERSION 25 #define READYTORUN_MINOR_VERSION 0x0000 -#define MINIMUM_READYTORUN_MAJOR_VERSION 24 +#define MINIMUM_READYTORUN_MAJOR_VERSION 25 // R2R Version 2.1 adds the InliningInfo section // R2R Version 2.2 adds the ProfileDataInfo section @@ -64,6 +64,7 @@ // R2R Version 22 changes NativeVarInfo encoding to include CALL_RETURN_VALUE // R2R Version 23 changes delegate layout to have target before methodPtr // R2R Version 24 changes ARM32 virtual stub dispatch hidden parameter register to R12 +// R2R Version 25 renames runtime async infrastructure members, makes thunk-used members NonVersionable, and frees up a flag in CorInfoContinuationFlags struct READYTORUN_CORE_HEADER { diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 73fdbadee48886..4a3583c8aba49f 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -11701,7 +11701,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) //------------------------------------------------------------------------ // impWrapTopOfStackInAwait: // Wrap the value on the top of the stack in -// AsyncHelpers.TransparentAwaitWithResult. +// AsyncHelpers.TransparentAwait. // // Returns: // True if successful. False if the EE could not create the call (only during @@ -11713,7 +11713,7 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) // mistyped; the original IL returns a Task or ValueTask, but the runtime // async version expects to return the unwrapped result. This function // accomplishes the unwrapping by inserting an async call to -// AsyncHelpers.TransparentAwaitWithResult around the value on the top of the +// AsyncHelpers.TransparentAwait around the value on the top of the // stack. // bool Compiler::impWrapTopOfStackInAwait() @@ -11860,7 +11860,7 @@ bool Compiler::impWrapTopOfStackInAwait() //------------------------------------------------------------------------ // impFoldAwaitedTopOfStack: // Fold a few patterns where introducing a call to -// AsyncHelpers.TransparentAwaitWithResult is unnecessary. +// AsyncHelpers.TransparentAwait is unnecessary. // // Returns: // True if the top of stack was folded and the importer stack was updated diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index e1cc51c1f9f698..9e8e5b1f3d2afe 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -11,7 +11,7 @@ struct ReadyToRunHeaderConstants { static const uint32_t Signature = 0x00525452; // 'RTR' - static const uint32_t CurrentMajorVersion = 24; + static const uint32_t CurrentMajorVersion = 25; static const uint32_t CurrentMinorVersion = 0; }; diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index bba736ba320985..64ed09698e99f3 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants { public const uint Signature = 0x00525452; // 'RTR' - public const ushort CurrentMajorVersion = 24; + public const ushort CurrentMajorVersion = 25; public const ushort CurrentMinorVersion = 0; } #if READYTORUN diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3961ae45e5bf4c..d354032f657caf 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3618,7 +3618,7 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) ? context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8) : context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8); MethodSignature signature = new MethodSignature(MethodSignatureFlags.Static, 0, context.GetWellKnownType(WellKnownType.Void), [parameterType]); - runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, signature); + runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwait"u8, signature); } else { @@ -3627,7 +3627,7 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) ? context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8).MakeInstantiatedType(signatureVariable) : context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task`1"u8).MakeInstantiatedType(signatureVariable); MethodSignature signature = new MethodSignature(MethodSignatureFlags.Static, 1, signatureVariable, [parameterType]); - runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, signature).MakeInstantiatedMethod(returnType); + runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwait"u8, signature).MakeInstantiatedMethod(returnType); } MethodDesc result = runtimeDeterminedResult.GetCanonMethodTarget(CanonicalFormKind.Specific); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs index dc6cc330b645c1..7a9add8bbb76d1 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs @@ -11,7 +11,7 @@ public static class AsyncThunkILEmitter // Emits a thunk that wraps an async method to return a Task or ValueTask. // The thunk calls the async method, and if it completes synchronously, // it returns a completed Task/ValueTask. If the async method suspends, - // it calls FinalizeTaskReturningThunk/FinalizeValueTaskReturningThunk method to get the Task/ValueTask. + // it calls CreateRuntimeAsyncTask/CreateRuntimeAsyncValueTask method to get the Task/ValueTask. // The emitted code matches method EmitTaskReturningThunk in CoreCLR VM. public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, MethodDesc asyncMethod) @@ -191,35 +191,35 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me codestream.EmitLabel(suspendedLabel); - MethodDesc finalizeTaskReturningThunkMd; + MethodDesc createRuntimeAsyncTaskMd; if (logicalReturnType != null) { - MethodSignature finalizeReturningThunkSignature = new MethodSignature( + MethodSignature createRuntimeAsyncTaskSignature = new MethodSignature( MethodSignatureFlags.Static, genericParameterCount: 1, returnType: ((MetadataType)returnType.GetTypeDefinition()).MakeInstantiatedType(context.GetSignatureVariable(0, true)), parameters: [awaitStateType.MakeByRefType()] ); - finalizeTaskReturningThunkMd = asyncHelpersType - .GetKnownMethod(isValueTask ? "FinalizeValueTaskReturningThunk"u8 : "FinalizeTaskReturningThunk"u8, finalizeReturningThunkSignature) + createRuntimeAsyncTaskMd = asyncHelpersType + .GetKnownMethod(isValueTask ? "CreateRuntimeAsyncValueTask"u8 : "CreateRuntimeAsyncTask"u8, createRuntimeAsyncTaskSignature) .MakeInstantiatedMethod(new Instantiation(logicalReturnType)); } else { - MethodSignature finalizeReturningThunkSignature = new MethodSignature( + MethodSignature createRuntimeAsyncTaskSignature = new MethodSignature( MethodSignatureFlags.Static, genericParameterCount: 0, returnType: returnType, parameters: [awaitStateType.MakeByRefType()] ); - finalizeTaskReturningThunkMd = asyncHelpersType - .GetKnownMethod(isValueTask ? "FinalizeValueTaskReturningThunk"u8 : "FinalizeTaskReturningThunk"u8, finalizeReturningThunkSignature); + createRuntimeAsyncTaskMd = asyncHelpersType + .GetKnownMethod(isValueTask ? "CreateRuntimeAsyncValueTask"u8 : "CreateRuntimeAsyncTask"u8, createRuntimeAsyncTaskSignature); } codestream.EmitLdLoc(refAwaitStateLocal); - codestream.Emit(ILOpcode.call, emitter.NewToken(finalizeTaskReturningThunkMd)); + codestream.Emit(ILOpcode.call, emitter.NewToken(createRuntimeAsyncTaskMd)); codestream.EmitStLoc(returnTaskLocal); codestream.Emit(ILOpcode.leave, returnTaskLabel); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 120ad07502c61b..7e233e358ba6c3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -1822,7 +1822,7 @@ private void ImportReturn() ? context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask"u8) : context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8); MethodSignature signature = new MethodSignature(MethodSignatureFlags.Static, 0, context.GetWellKnownType(WellKnownType.Void), [parameterType]); - runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, signature); + runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwait"u8, signature); } else { @@ -1831,7 +1831,7 @@ private void ImportReturn() ? context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "ValueTask`1"u8).MakeInstantiatedType(signatureVariable) : context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task`1"u8).MakeInstantiatedType(signatureVariable); MethodSignature signature = new MethodSignature(MethodSignatureFlags.Static, 1, signatureVariable, [parameterType]); - runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, signature).MakeInstantiatedMethod(returnType); + runtimeDeterminedResult = asyncHelpers.GetKnownMethod("TransparentAwait"u8, signature).MakeInstantiatedMethod(returnType); } MethodDesc targetMethod = runtimeDeterminedResult.GetCanonMethodTarget(CanonicalFormKind.Specific); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 1ec280967b82af..7da8c4f258dcd5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -1023,7 +1023,7 @@ private void AddNecessaryAsyncReferences(MethodDesc method) continuation.GetKnownField("State"u8), continuation.GetKnownField("Flags"u8), ]; - // The signature types for the TransparentAwaitWithResult overloads used by + // The signature types for the TransparentAwait overloads used by // CorInfoImpl.getAwaitReturnCall (kept in sync with that method). TypeDesc voidType = TypeSystemContext.GetWellKnownType(WellKnownType.Void); TypeDesc taskType = TypeSystemContext.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8); @@ -1049,10 +1049,10 @@ private void AddNecessaryAsyncReferences(MethodDesc method) // For CorInfoImpl.getAwaitReturnCall. The JIT synthesizes calls to these overloads, so they // have no IL token in the caller and their manifest tokens must be pre-seeded here. - asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 0, voidType, [taskType])), - asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 0, voidType, [valueTaskType])), - asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 1, methodVar, [taskOfTType.MakeInstantiatedType(methodVar)])), - asyncHelpers.GetKnownMethod("TransparentAwaitWithResult"u8, new MethodSignature(MethodSignatureFlags.Static, 1, methodVar, [valueTaskOfTType.MakeInstantiatedType(methodVar)])), + asyncHelpers.GetKnownMethod("TransparentAwait"u8, new MethodSignature(MethodSignatureFlags.Static, 0, voidType, [taskType])), + asyncHelpers.GetKnownMethod("TransparentAwait"u8, new MethodSignature(MethodSignatureFlags.Static, 0, voidType, [valueTaskType])), + asyncHelpers.GetKnownMethod("TransparentAwait"u8, new MethodSignature(MethodSignatureFlags.Static, 1, methodVar, [taskOfTType.MakeInstantiatedType(methodVar)])), + asyncHelpers.GetKnownMethod("TransparentAwait"u8, new MethodSignature(MethodSignatureFlags.Static, 1, methodVar, [valueTaskOfTType.MakeInstantiatedType(methodVar)])), ]; var moduleForNewReferences = ((EcmaMethod)method.GetPrimaryMethodDesc().GetTypicalMethodDefinition()).Module; _tokenManager.EnsureDefTokensAreAvailable([..requiredMethods, ..requiredTypes, ..requiredFields], moduleForNewReferences, true); diff --git a/src/coreclr/vm/asyncthunks.cpp b/src/coreclr/vm/asyncthunks.cpp index 4f976e561d873a..233bc7a3832c67 100644 --- a/src/coreclr/vm/asyncthunks.cpp +++ b/src/coreclr/vm/asyncthunks.cpp @@ -93,7 +93,7 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncCallVariant, MetaSig& // if (AsyncHelpers.AsyncCallContinuation() == null) // return Task.FromResult(result); // - // return FinalizeTaskReturningThunk(ref awaitState); + // return CreateRuntimeAsyncTask(ref awaitState); // } // catch (Exception ex) // { @@ -229,29 +229,29 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncCallVariant, MetaSig& pCode->EmitLabel(suspendedLabel); - int finalizeTaskReturningThunkToken; + int createRuntimeAsyncTaskToken; if (logicalResultLocal != UINT_MAX) { MethodDesc* md; if (isValueTask) - md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK_1); + md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CREATE_RUNTIME_ASYNC_VALUE_TASK_1); else - md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__FINALIZE_TASK_RETURNING_THUNK_1); + md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CREATE_RUNTIME_ASYNC_TASK_1); md = FindOrCreateAssociatedMethodDesc(md, md->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE); - finalizeTaskReturningThunkToken = GetTokenForGenericMethodCallWithAsyncReturnType(pCode, md); + createRuntimeAsyncTaskToken = GetTokenForGenericMethodCallWithAsyncReturnType(pCode, md); } else { MethodDesc* md; if (isValueTask) - md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__FINALIZE_VALUETASK_RETURNING_THUNK); + md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CREATE_RUNTIME_ASYNC_VALUE_TASK); else - md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__FINALIZE_TASK_RETURNING_THUNK); - finalizeTaskReturningThunkToken = pCode->GetToken(md); + md = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__CREATE_RUNTIME_ASYNC_TASK); + createRuntimeAsyncTaskToken = pCode->GetToken(md); } pCode->EmitLDLOC(refAwaitStateLocal); - pCode->EmitCALL(finalizeTaskReturningThunkToken, 1, 1); + pCode->EmitCALL(createRuntimeAsyncTaskToken, 1, 1); pCode->EmitSTLOC(returnTaskLocal); pCode->EmitLEAVE(returnTaskLabel); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index fa6f4000edaa7e..7020945877cc67 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -708,26 +708,20 @@ DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION, AllocContinuation, DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION_METHOD, AllocContinuationMethod, NoSig) DEFINE_METHOD(ASYNC_HELPERS, ALLOC_CONTINUATION_CLASS, AllocContinuationClass, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_TASK_RETURNING_THUNK, FinalizeTaskReturningThunk, SM_RefRuntimeAsyncAwaitState_RetTask) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_TASK_RETURNING_THUNK_1, FinalizeTaskReturningThunk, GM_RefRuntimeAsyncAwaitState_RetTaskOfT) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_VALUETASK_RETURNING_THUNK, FinalizeValueTaskReturningThunk, SM_RefRuntimeAsyncAwaitState_RetValueTask) -DEFINE_METHOD(ASYNC_HELPERS, FINALIZE_VALUETASK_RETURNING_THUNK_1, FinalizeValueTaskReturningThunk, GM_RefRuntimeAsyncAwaitState_RetValueTaskOfT) +DEFINE_METHOD(ASYNC_HELPERS, CREATE_RUNTIME_ASYNC_TASK, CreateRuntimeAsyncTask, SM_RefRuntimeAsyncAwaitState_RetTask) +DEFINE_METHOD(ASYNC_HELPERS, CREATE_RUNTIME_ASYNC_TASK_1, CreateRuntimeAsyncTask, GM_RefRuntimeAsyncAwaitState_RetTaskOfT) +DEFINE_METHOD(ASYNC_HELPERS, CREATE_RUNTIME_ASYNC_VALUE_TASK, CreateRuntimeAsyncValueTask, SM_RefRuntimeAsyncAwaitState_RetValueTask) +DEFINE_METHOD(ASYNC_HELPERS, CREATE_RUNTIME_ASYNC_VALUE_TASK_1, CreateRuntimeAsyncValueTask, GM_RefRuntimeAsyncAwaitState_RetValueTaskOfT) DEFINE_METHOD(ASYNC_HELPERS, TASK_FROM_EXCEPTION, TaskFromException, SM_Exception_RetTask) DEFINE_METHOD(ASYNC_HELPERS, TASK_FROM_EXCEPTION_1, TaskFromException, GM_Exception_RetTaskOfT) DEFINE_METHOD(ASYNC_HELPERS, VALUETASK_FROM_EXCEPTION, ValueTaskFromException, SM_Exception_RetValueTask) DEFINE_METHOD(ASYNC_HELPERS, VALUETASK_FROM_EXCEPTION_1, ValueTaskFromException, GM_Exception_RetValueTaskOfT) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT, TransparentAwait, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_OF_T, TransparentAwaitOfT, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_VALUE_TASK, TransparentAwaitValueTask, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_VALUE_TASK_OF_T, TransparentAwaitValueTaskOfT, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_TASK_WITH_RESULT, TransparentAwaitWithResult, SM_Task_RetVoid) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_VALUETASK_WITH_RESULT, TransparentAwaitWithResult, SM_ValueTask_RetVoid) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_TASK_OF_T_WITH_RESULT, TransparentAwaitWithResult, GM_TaskOfT_RetT) -DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_VALUETASK_OF_T_WITH_RESULT, TransparentAwaitWithResult, GM_ValueTaskOfT_RetT) -DEFINE_METHOD(ASYNC_HELPERS, COMPLETED_TASK_RESULT, CompletedTaskResult, NoSig) -DEFINE_METHOD(ASYNC_HELPERS, COMPLETED_TASK, CompletedTask, NoSig) +DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_TASK, TransparentAwait, SM_Task_RetVoid) +DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_VALUETASK, TransparentAwait, SM_ValueTask_RetVoid) +DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_TASK_OF_T, TransparentAwait, GM_TaskOfT_RetT) +DEFINE_METHOD(ASYNC_HELPERS, TRANSPARENT_AWAIT_VALUETASK_OF_T, TransparentAwait, GM_ValueTaskOfT_RetT) DEFINE_METHOD(ASYNC_HELPERS, CAPTURE_EXECUTION_CONTEXT, CaptureExecutionContext, NoSig) DEFINE_METHOD(ASYNC_HELPERS, CAPTURE_CONTINUATION_CONTEXT, CaptureContinuationContext, NoSig) DEFINE_METHOD(ASYNC_HELPERS, CAPTURE_CONTEXTS, CaptureContexts, NoSig) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f8fa996f3782aa..23b8e1cce7449e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10387,11 +10387,11 @@ CORINFO_METHOD_HANDLE CEEInfo::getAwaitReturnCall(CORINFO_METHOD_HANDLE callerHa { if (sig.IsReturnTypeVoid()) { - pTypicalAwaitMD = pMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_VALUETASK_WITH_RESULT); + pTypicalAwaitMD = pMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_VALUETASK); } else { - pTypicalAwaitMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_VALUETASK_OF_T_WITH_RESULT); + pTypicalAwaitMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_VALUETASK_OF_T); pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pTypicalAwaitMD, pTypicalAwaitMD->GetMethodTable(), FALSE, Instantiation(&retType, 1), TRUE); } } @@ -10399,11 +10399,11 @@ CORINFO_METHOD_HANDLE CEEInfo::getAwaitReturnCall(CORINFO_METHOD_HANDLE callerHa { if (sig.IsReturnTypeVoid()) { - pTypicalAwaitMD = pMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_TASK_WITH_RESULT); + pTypicalAwaitMD = pMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_TASK); } else { - pTypicalAwaitMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_TASK_OF_T_WITH_RESULT); + pTypicalAwaitMD = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_TASK_OF_T); pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pTypicalAwaitMD, pTypicalAwaitMD->GetMethodTable(), FALSE, Instantiation(&retType, 1), TRUE); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs index daa89fb6a7a225..02e0130920e3ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs @@ -69,7 +69,7 @@ public static T Await(Task task) if (!task.IsCompleted) { TailAwait(); - return AwaitTask(task, ConfigureAwaitOptions.ContinueOnCapturedContext); + return Suspend(task, ConfigureAwaitOptions.ContinueOnCapturedContext); } TaskAwaiter.ValidateEnd(task); @@ -89,7 +89,7 @@ public static void Await(Task task) if (!task.IsCompleted) { TailAwait(); - AwaitTask(task, ConfigureAwaitOptions.ContinueOnCapturedContext); + Suspend(task, ConfigureAwaitOptions.ContinueOnCapturedContext); return; } @@ -122,7 +122,7 @@ public static T Await(ValueTask task) } TailAwait(); - return AwaitTask(t, ConfigureAwaitOptions.ContinueOnCapturedContext); + return Suspend(t, ConfigureAwaitOptions.ContinueOnCapturedContext); } Debug.Assert(obj is IValueTaskSource); @@ -130,7 +130,7 @@ public static T Await(ValueTask task) if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending) { TailAwait(); - return AwaitValueTaskSource(vts, task._token, true); + return Suspend(vts, task._token, true); } return vts.GetResult(task._token); @@ -161,7 +161,7 @@ public static void Await(ValueTask task) } TailAwait(); - AwaitTask(t, ConfigureAwaitOptions.ContinueOnCapturedContext); + Suspend(t, ConfigureAwaitOptions.ContinueOnCapturedContext); return; } @@ -170,7 +170,7 @@ public static void Await(ValueTask task) if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending) { TailAwait(); - AwaitValueTaskSource(vts, task._token, true); + Suspend(vts, task._token, true); return; } @@ -198,7 +198,7 @@ public static void Await(ConfiguredTaskAwaitable configuredAwaitable) if (!awaiter.IsCompleted) { TailAwait(); - AwaitTask(awaiter.m_task, awaiter.m_options); + Suspend(awaiter.m_task, awaiter.m_options); return; } @@ -245,7 +245,7 @@ public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable) } TailAwait(); - AwaitTask(t, task._continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None); + Suspend(t, task._continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None); return; } @@ -254,7 +254,7 @@ public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable) if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending) { TailAwait(); - AwaitValueTaskSource(vts, task._token, task._continueOnCapturedContext); + Suspend(vts, task._token, task._continueOnCapturedContext); return; } @@ -280,7 +280,7 @@ public static T Await(ConfiguredTaskAwaitable configuredAwaitable) if (!awaiter.IsCompleted) { TailAwait(); - return AwaitTask(awaiter.m_task, awaiter.m_options); + return Suspend(awaiter.m_task, awaiter.m_options); } return awaiter.GetResult(); @@ -313,7 +313,7 @@ public static T Await(ConfiguredValueTaskAwaitable configuredAwaitable) } TailAwait(); - return AwaitTask(t, task._continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None); + return Suspend(t, task._continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None); } Debug.Assert(obj is IValueTaskSource); @@ -321,7 +321,7 @@ public static T Await(ConfiguredValueTaskAwaitable configuredAwaitable) if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending) { TailAwait(); - return AwaitValueTaskSource(vts, task._token, task._continueOnCapturedContext); + return Suspend(vts, task._token, task._continueOnCapturedContext); } return vts.GetResult(task._token); diff --git a/src/tests/async/execution-context/execution-context.cs b/src/tests/async/execution-context/execution-context.cs index 389f33eaf43a80..2a63225ded9a9e 100644 --- a/src/tests/async/execution-context/execution-context.cs +++ b/src/tests/async/execution-context/execution-context.cs @@ -170,4 +170,41 @@ private static async ValueTask ChangeThenReturnValueTask() { s_local.Value = 123; } + + private static AsyncLocal s_leakLocal = new AsyncLocal(); + + // Regression test: a callee that changes the ExecutionContext and then + // suspends must not leak the change to its synchronous caller through the + // task-returning thunk. The check must happen in the caller's synchronous + // region (before the caller itself suspends); a suspension in the caller + // would otherwise mask the leak via the caller's own context restore. + [Fact] + public static void TestNoExecutionContextLeakOnSuspension() + { + TestNoExecutionContextLeakOnSuspensionAsync().GetAwaiter().GetResult(); + } + + private static async Task TestNoExecutionContextLeakOnSuspensionAsync() + { + s_leakLocal.Value = 100; + + var tcs = new TaskCompletionSource(); + Task callee = ChangeExecutionContextThenSuspend(tcs.Task); + + // The callee changed the AsyncLocal and then suspended. That change must + // not have leaked out to us through the task-returning thunk. + Assert.Equal(100, s_leakLocal.Value); + + tcs.SetResult(); + await callee; + + Assert.Equal(100, s_leakLocal.Value); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static async Task ChangeExecutionContextThenSuspend(Task toAwait) + { + s_leakLocal.Value = 999; + await toAwait; + } }