Skip to content

Commit f07fa6c

Browse files
authored
Save/restore contexts for custom notifiers (#121743)
With #121448 we are restoring contexts as we unwind during suspension. However, since we delay the call to `OnCompleted`, we need to restore the leaf contexts explicitly now. Also change suspension so that we do not notify about context restores. This is different from async1, but it is necessary to keep calling `OnCompleted` in the right contexts when we get back to `RuntimeAsyncTask`. Also, skipping notifications for contexts when we know no user code will be executed is one of the optimizations we want to allow with runtime async.
1 parent d3d8eaa commit f07fa6c

File tree

12 files changed

+61
-9
lines changed

12 files changed

+61
-9
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ private struct RuntimeAsyncAwaitState
171171
public INotifyCompletion? Notifier;
172172
public IValueTaskSourceNotifier? ValueTaskSourceNotifier;
173173
public Task? TaskNotifier;
174+
175+
public ExecutionContext? ExecutionContext;
176+
public SynchronizationContext? SynchronizationContext;
177+
178+
public void CaptureContexts()
179+
{
180+
Thread curThread = Thread.CurrentThreadAssumedInitialized;
181+
ExecutionContext = curThread._executionContext;
182+
SynchronizationContext = curThread._synchronizationContext;
183+
}
174184
}
175185

176186
[ThreadStatic]
@@ -240,6 +250,7 @@ private static void TransparentAwait(object o)
240250
state.ValueTaskSourceNotifier = (IValueTaskSourceNotifier)o;
241251
}
242252

253+
state.CaptureContexts();
243254
AsyncSuspend(sentinelContinuation);
244255
}
245256

@@ -541,6 +552,8 @@ public static void HandleSuspended<T, TOps>(T task) where T : Task, ITaskComplet
541552
{
542553
ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
543554

555+
RestoreContextsOnSuspension(false, state.ExecutionContext, state.SynchronizationContext);
556+
544557
ICriticalNotifyCompletion? critNotifier = state.CriticalNotifier;
545558
INotifyCompletion? notifier = state.Notifier;
546559
IValueTaskSourceNotifier? vtsNotifier = state.ValueTaskSourceNotifier;
@@ -550,6 +563,8 @@ public static void HandleSuspended<T, TOps>(T task) where T : Task, ITaskComplet
550563
state.Notifier = null;
551564
state.ValueTaskSourceNotifier = null;
552565
state.TaskNotifier = null;
566+
state.ExecutionContext = null;
567+
state.SynchronizationContext = null;
553568

554569
Continuation sentinelContinuation = state.SentinelContinuation!;
555570
Continuation headContinuation = sentinelContinuation.Next!;
@@ -794,6 +809,28 @@ private static void RestoreContexts(bool resumed, ExecutionContext? previousExec
794809
}
795810
}
796811

812+
// Restore contexts onto current Thread as we unwind during suspension. We control the code that runs
813+
// during suspension and we do not need to raise ExecutionContext notifications -- we know that it is
814+
// not going to be accessed and that DispatchContinuations will return it back to the leaf's context
815+
// before calling user code, and restore the original contexts with appropriate notifications before
816+
// returning.
817+
private static void RestoreContextsOnSuspension(bool resumed, ExecutionContext? previousExecCtx, SynchronizationContext? previousSyncCtx)
818+
{
819+
if (!resumed)
820+
{
821+
Thread thread = Thread.CurrentThreadAssumedInitialized;
822+
if (previousSyncCtx != thread._synchronizationContext)
823+
{
824+
thread._synchronizationContext = previousSyncCtx;
825+
}
826+
827+
if (previousExecCtx != thread._executionContext)
828+
{
829+
thread._executionContext = previousExecCtx;
830+
}
831+
}
832+
}
833+
797834
private static void CaptureContinuationContext(ref object continuationContext, ref ContinuationFlags flags)
798835
{
799836
SynchronizationContext? syncCtx = Thread.CurrentThreadAssumedInitialized._synchronizationContext;

src/coreclr/inc/corinfo.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,13 +1754,18 @@ struct CORINFO_ASYNC_INFO
17541754
CORINFO_FIELD_HANDLE continuationStateFldHnd;
17551755
// 'Flags' field
17561756
CORINFO_FIELD_HANDLE continuationFlagsFldHnd;
1757-
// Method handle for AsyncHelpers.CaptureExecutionContext
1757+
// Method handle for AsyncHelpers.CaptureExecutionContext, used during suspension
17581758
CORINFO_METHOD_HANDLE captureExecutionContextMethHnd;
1759-
// Method handle for AsyncHelpers.RestoreExecutionContext
1759+
// Method handle for AsyncHelpers.RestoreExecutionContext, used during resumption
17601760
CORINFO_METHOD_HANDLE restoreExecutionContextMethHnd;
1761+
// Method handle for AsyncHelpers.CaptureContinuationContext, used during suspension
17611762
CORINFO_METHOD_HANDLE captureContinuationContextMethHnd;
1763+
// Method handle for AsyncHelpers.CaptureContexts, used at the beginning of async methods
17621764
CORINFO_METHOD_HANDLE captureContextsMethHnd;
1765+
// Method handle for AsyncHelpers.RestoreContexts, used before normal returns from async methods
17631766
CORINFO_METHOD_HANDLE restoreContextsMethHnd;
1767+
// Method handle for AsyncHelpers.RestoreContextsOnSuspension, used before suspending in async methods
1768+
CORINFO_METHOD_HANDLE restoreContextsOnSuspensionMethHnd;
17641769
};
17651770

17661771
// Flags passed from JIT to runtime.

src/coreclr/inc/jiteeversionguid.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737

3838
#include <minipal/guid.h>
3939

40-
constexpr GUID JITEEVersionIdentifier = { /* 2d5333ab-eda7-4fd9-afc9-4a073dc0f7ec */
41-
0x2d5333ab,
42-
0xeda7,
43-
0x4fd9,
44-
{0xaf, 0xc9, 0x4a, 0x07, 0x3d, 0xc0, 0xf7, 0xec}
40+
constexpr GUID JITEEVersionIdentifier = { /* c3e8a087-931a-4aa4-a311-13e6736e509a */
41+
0xc3e8a087,
42+
0x931a,
43+
0x4aa4,
44+
{0xa3, 0x11, 0x13, 0xe6, 0x73, 0x6e, 0x50, 0x9a}
4545
};
4646

4747
#endif // JIT_EE_VERSIONING_GUID_H

src/coreclr/jit/async.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1708,7 +1708,8 @@ void AsyncTransformation::RestoreContexts(BasicBlock* block, GenTreeCall* call,
17081708
GenTree* resumedPlaceholder = m_comp->gtNewIconNode(0);
17091709
GenTree* execContextPlaceholder = m_comp->gtNewNull();
17101710
GenTree* syncContextPlaceholder = m_comp->gtNewNull();
1711-
GenTreeCall* restoreCall = m_comp->gtNewCallNode(CT_USER_FUNC, m_asyncInfo->restoreContextsMethHnd, TYP_VOID);
1711+
GenTreeCall* restoreCall =
1712+
m_comp->gtNewCallNode(CT_USER_FUNC, m_asyncInfo->restoreContextsOnSuspensionMethHnd, TYP_VOID);
17121713

17131714
restoreCall->gtArgs.PushFront(m_comp, NewCallArg::Primitive(syncContextPlaceholder));
17141715
restoreCall->gtArgs.PushFront(m_comp, NewCallArg::Primitive(execContextPlaceholder));

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3402,6 +3402,7 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut)
34023402
pAsyncInfoOut.captureContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("CaptureContinuationContext"u8, null));
34033403
pAsyncInfoOut.captureContextsMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("CaptureContexts"u8, null));
34043404
pAsyncInfoOut.restoreContextsMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("RestoreContexts"u8, null));
3405+
pAsyncInfoOut.restoreContextsOnSuspensionMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("RestoreContextsOnSuspension"u8, null));
34053406
}
34063407

34073408
private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize)

src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,7 @@ public unsafe struct CORINFO_ASYNC_INFO
905905
public CORINFO_METHOD_STRUCT_* captureContinuationContextMethHnd;
906906
public CORINFO_METHOD_STRUCT_* captureContextsMethHnd;
907907
public CORINFO_METHOD_STRUCT_* restoreContextsMethHnd;
908+
public CORINFO_METHOD_STRUCT_* restoreContextsOnSuspensionMethHnd;
908909
}
909910

910911
// Flags passed from JIT to runtime.

src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ private void ImportCall(ILOpcode opcode, int token)
471471
_dependencies.Add(_factory.MethodEntrypoint(asyncHelpers.GetKnownMethod("CaptureExecutionContext"u8, null)), asyncReason);
472472
_dependencies.Add(_factory.MethodEntrypoint(asyncHelpers.GetKnownMethod("RestoreExecutionContext"u8, null)), asyncReason);
473473
_dependencies.Add(_factory.MethodEntrypoint(asyncHelpers.GetKnownMethod("CaptureContinuationContext"u8, null)), asyncReason);
474+
_dependencies.Add(_factory.MethodEntrypoint(asyncHelpers.GetKnownMethod("RestoreContextsOnSuspension"u8, null)), asyncReason);
474475
}
475476

476477
// If this is the task await pattern, we're actually going to call the variant

src/coreclr/tools/superpmi/superpmi-shared/agnostic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ struct Agnostic_CORINFO_ASYNC_INFO
205205
DWORDLONG captureContinuationContextMethHnd;
206206
DWORDLONG captureContextsMethHnd;
207207
DWORDLONG restoreContextsMethHnd;
208+
DWORDLONG restoreContextsOnSuspensionMethHnd;
208209
};
209210

210211
struct Agnostic_GetOSRInfo

src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4462,6 +4462,7 @@ void MethodContext::recGetAsyncInfo(const CORINFO_ASYNC_INFO* pAsyncInfo)
44624462
value.captureContinuationContextMethHnd = CastHandle(pAsyncInfo->captureContinuationContextMethHnd);
44634463
value.captureContextsMethHnd = CastHandle(pAsyncInfo->captureContextsMethHnd);
44644464
value.restoreContextsMethHnd = CastHandle(pAsyncInfo->restoreContextsMethHnd);
4465+
value.restoreContextsOnSuspensionMethHnd = CastHandle(pAsyncInfo->restoreContextsOnSuspensionMethHnd);
44654466

44664467
GetAsyncInfo->Add(0, value);
44674468
DEBUG_REC(dmpGetAsyncInfo(0, value));
@@ -4486,6 +4487,7 @@ void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut)
44864487
pAsyncInfoOut->captureContinuationContextMethHnd = (CORINFO_METHOD_HANDLE)value.captureContinuationContextMethHnd;
44874488
pAsyncInfoOut->captureContextsMethHnd = (CORINFO_METHOD_HANDLE)value.captureContextsMethHnd;
44884489
pAsyncInfoOut->restoreContextsMethHnd = (CORINFO_METHOD_HANDLE)value.restoreContextsMethHnd;
4490+
pAsyncInfoOut->restoreContextsOnSuspensionMethHnd = (CORINFO_METHOD_HANDLE)value.restoreContextsOnSuspensionMethHnd;
44894491
DEBUG_REP(dmpGetAsyncInfo(0, value));
44904492
}
44914493

src/coreclr/vm/corelib.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ DEFINE_METHOD(ASYNC_HELPERS, RESTORE_EXECUTION_CONTEXT, RestoreExecutionCon
739739
DEFINE_METHOD(ASYNC_HELPERS, CAPTURE_CONTINUATION_CONTEXT, CaptureContinuationContext, NoSig)
740740
DEFINE_METHOD(ASYNC_HELPERS, CAPTURE_CONTEXTS, CaptureContexts, NoSig)
741741
DEFINE_METHOD(ASYNC_HELPERS, RESTORE_CONTEXTS, RestoreContexts, NoSig)
742+
DEFINE_METHOD(ASYNC_HELPERS, RESTORE_CONTEXTS_ON_SUSPENSION, RestoreContextsOnSuspension, NoSig)
742743
DEFINE_METHOD(ASYNC_HELPERS, ASYNC_CALL_CONTINUATION, AsyncCallContinuation, NoSig)
743744

744745
#ifdef TARGET_BROWSER

0 commit comments

Comments
 (0)