Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,20 @@ internal enum ContinuationFlags
// Otherwise the exact offset of the member is computed as
// DataOffset + (index - 1) * PointerSize
//
ExceptionIndexFirstBit = 3,
ExceptionIndexNumBits = 2,
ExecutionContextIndexFirstBit = 3,
ExecutionContextIndexNumBits = 2,

ContinuationContextIndexFirstBit = 5,
ContinuationContextIndexNumBits = 2,

ExceptionIndexFirstBit = 7,
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 = 7,
ResultIndexNumBits = 25,
ResultIndexFirstBit = 10,
ResultIndexNumBits = 22,
}

// Keep in sync with CORINFO_AsyncResumeInfo in corinfo.h
Expand Down Expand Up @@ -92,6 +95,16 @@ public bool HasException()
return ((uint)Flags & (mask << (int)ContinuationFlags.ExceptionIndexFirstBit)) != 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ExecutionContext? GetExecutionContext()
{
const uint mask = (1u << (int)ContinuationFlags.ExecutionContextIndexNumBits) - 1;
uint index = ((uint)Flags >> (int)ContinuationFlags.ExecutionContextIndexFirstBit) & mask;
Debug.Assert(index != 0);
ref byte data = ref RuntimeHelpers.GetRawData(this);
return Unsafe.As<byte, ExecutionContext?>(ref Unsafe.Add(ref data, (DataOffset - PointerSize) + index * PointerSize));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetException(Exception ex)
{
Expand Down Expand Up @@ -227,7 +240,7 @@ private unsafe struct RuntimeAsyncAwaitState
public Continuation? SentinelContinuation;

// We cache the thread here to avoid unnecessary repeated TLS lookups.
public Thread CurrentThread;
public Thread? CurrentThread;

public RuntimeAsyncStackState* StackState;

Expand All @@ -236,7 +249,7 @@ public void CaptureContexts()
// CaptureContext is called from leaf await helpers. We either just started a runtime async chain
// (from a thunk), or we came from DispatchContinuations (on resumption).
// Both cases have already initialized CurrentThread.
Thread curThread = CurrentThread;
Thread? curThread = CurrentThread;
Debug.Assert(curThread != null);
Debug.Assert(StackState != null);
// Here we get the execution context for presenting to the notifier,
Expand Down Expand Up @@ -394,7 +407,7 @@ private void SetContinuationState(Continuation value)

internal unsafe bool HandleSuspended(ref RuntimeAsyncAwaitState state)
{
Thread currentThread = state.CurrentThread;
Thread? currentThread = state.CurrentThread;
Debug.Assert(currentThread != null);

RuntimeAsyncStackState* stackState = state.StackState;
Expand Down Expand Up @@ -534,7 +547,7 @@ private unsafe void DispatchContinuations()
}
}

RuntimeAsyncStackState stackState = default;
RuntimeAsyncStackState stackState;
Comment thread
jakobbotsch marked this conversation as resolved.

ref RuntimeAsyncAwaitState awaitState = ref t_runtimeAsyncAwaitState;
awaitState.Push(&stackState);
Expand All @@ -555,6 +568,8 @@ private unsafe void DispatchContinuations()
Continuation? nextContinuation = curContinuation.Next;
asyncDispatcherInfo.NextContinuation = nextContinuation;

Debug.Assert(awaitState.CurrentThread != null);
RestoreExecutionContext(awaitState.CurrentThread, curContinuation.GetExecutionContext());
Comment thread
jakobbotsch marked this conversation as resolved.
ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref GetResultStorage();

Continuation? newContinuation = curContinuation.ResumeInfo->Resume(curContinuation, ref resultLoc);
Expand Down Expand Up @@ -633,7 +648,7 @@ private unsafe void DispatchContinuations()
[StackTraceHidden]
private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags flags)
{
RuntimeAsyncStackState stackState = default;
RuntimeAsyncStackState stackState;

ref RuntimeAsyncAwaitState awaitState = ref t_runtimeAsyncAwaitState;
awaitState.Push(&stackState);
Comment thread
jakobbotsch marked this conversation as resolved.
Expand All @@ -656,6 +671,8 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags
Continuation? nextContinuation = curContinuation.Next;
asyncDispatcherInfo.NextContinuation = nextContinuation;

Debug.Assert(awaitState.CurrentThread != null);
RestoreExecutionContext(awaitState.CurrentThread, curContinuation.GetExecutionContext());
ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref GetResultStorage();

RuntimeAsyncInstrumentationHelpers.ResumeRuntimeAsyncMethod(ref asyncDispatcherInfo, flags, curContinuation);
Expand Down Expand Up @@ -937,16 +954,10 @@ private static ValueTask ValueTaskFromException(Exception ex)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RestoreExecutionContext(ExecutionContext? previousExecCtx)
private static void RestoreExecutionContext(Thread thread, ExecutionContext? previousExecCtx)
{
if (previousExecCtx == ExecutionContext.DefaultFlowSuppressed)
{
return;
}

Thread thread = Thread.CurrentThreadAssumedInitialized;
ExecutionContext? currentExecCtx = thread._executionContext;
if (previousExecCtx != currentExecCtx)
if (previousExecCtx != currentExecCtx && previousExecCtx != ExecutionContext.DefaultFlowSuppressed)
{
ExecutionContext.RestoreChangedContextToThread(thread, previousExecCtx, currentExecCtx);
}
Comment thread
jakobbotsch marked this conversation as resolved.
Expand Down
13 changes: 8 additions & 5 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1803,18 +1803,21 @@ enum CorInfoContinuationFlags
// If the encoded index is 0, it means no such member is present.
// Otherwise the exact offset of the member is computed as
// OFFSETOF__CORINFO_Continuation__data + (index - 1) * PointerSize
//
CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT = 3,
CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS = 2,

CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT = 3,
CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS = 2,

CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT = 5,
CORINFO_CONTINUATION_CONTEXT_INDEX_NUM_BITS = 2,

CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT = 7,
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 = 7,
CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS = 25,
CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT = 10,
CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS = 22,
};

struct CORINFO_ASYNC_INFO
Expand Down
41 changes: 22 additions & 19 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5869,21 +5869,19 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation
flags |= index << firstBit;
};

for (int32_t i = -3; i < liveVars.GetSize(); i++)
for (int32_t i = -4; i < liveVars.GetSize(); i++)
{
int32_t var;

if (i == -3)
if (i == -4)
{
if (!needsEHHandling)
continue;
INTERP_DUMP("Allocate EH at offset %d\n", currentOffset);
INTERP_DUMP("Allocate ExecutionContext at offset %d\n", currentOffset);
SetSlotToTrue(objRefSlots, currentOffset);
encodeIndex(currentOffset, CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT, CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS);
encodeIndex(currentOffset, CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT, CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS);
currentOffset += sizeof(void*); // Align to pointer size to match the expected layout
continue;
}
if (i == -2)
if (i == -3)
{
if (!captureContinuationContext)
continue;
Expand All @@ -5893,6 +5891,16 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation
currentOffset += sizeof(void*); // Align to pointer size to match the expected layout
continue;
}
if (i == -2)
{
if (!needsEHHandling)
continue;
INTERP_DUMP("Allocate EH at offset %d\n", currentOffset);
SetSlotToTrue(objRefSlots, currentOffset);
encodeIndex(currentOffset, CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT, CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS);
currentOffset += sizeof(void*); // Align to pointer size to match the expected layout
continue;
}
if (i == -1)
{
returnValueDataStartOffset = currentOffset;
Expand Down Expand Up @@ -5945,15 +5953,6 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation
currentOffset += size;
}

int32_t execContextOffset = 0;
{
// Mark ExecContext pointer as a GC reference
execContextOffset = currentOffset;
INTERP_DUMP("Allocate ExecutableContext at offset %d\n", currentOffset);
SetSlotToTrue(objRefSlots, currentOffset);
currentOffset += sizeof(void*);
}

int32_t keepAliveOffset = 0;
if (needsKeepAlive)
{
Expand All @@ -5964,8 +5963,14 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation
currentOffset += sizeof(void*);
}

// Tail of the data may not have had any object refs to grow objRefSlots, finish growing it now
int32_t numSlotsExpected = currentOffset / sizeof(void*);
if (objRefSlots.GetSize() < numSlotsExpected)
{
objRefSlots.GrowBy(numSlotsExpected - objRefSlots.GetSize());
}

// Step 5: Get continuation type handle
assert((int32_t)(currentOffset / sizeof(void*)) <= objRefSlots.GetSize());
CORINFO_CLASS_HANDLE continuationTypeHnd = m_compHnd->getContinuationType(
currentOffset,
objRefSlots.GetUnderlyingArray(),
Expand Down Expand Up @@ -5996,7 +6001,6 @@ void InterpCompiler::EmitSuspend(const CORINFO_CALL_INFO &callInfo, Continuation

suspendData->flags = (CorInfoContinuationFlags)flags;

suspendData->offsetIntoContinuationTypeForExecutionContext = execContextOffset + OFFSETOF__CORINFO_Continuation__data;
suspendData->keepAliveOffset = keepAliveOffset + OFFSETOF__CORINFO_Continuation__data;
suspendData->captureSyncContextMethod = asyncInfo.captureContinuationContextMethHnd;
suspendData->restoreExecutionContextMethod = asyncInfo.restoreExecutionContextMethHnd;
Expand Down Expand Up @@ -11493,7 +11497,6 @@ static void DumpInterpAsyncSuspendData(InterpAsyncSuspendData* pSuspendInfo)
printf(" AsyncSuspendData[");
printf("continuationTypeHnd=%p", pSuspendInfo->continuationTypeHnd);
printf(", flags=%d", pSuspendInfo->flags);
printf(", offsetIntoContinuationTypeForExecutionContext=%d", pSuspendInfo->offsetIntoContinuationTypeForExecutionContext);
printf(", liveLocalsIntervals=");
PrintLocalIntervals(pSuspendInfo->liveLocalsIntervals);
printf(", zeroedLocalsIntervals=");
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/interpreter/inc/interpretershared.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ struct InterpAsyncSuspendData
InterpIntervalMapEntry* zeroedLocalsIntervals; // This will be used for the locals we need to keep live.
InterpIntervalMapEntry* liveLocalsIntervals; // Following the end of this struct is the array of InterpIntervalMapEntry for live locals
CorInfoContinuationFlags flags;
int32_t offsetIntoContinuationTypeForExecutionContext;
int32_t keepAliveOffset; // Only needed if we have a generic context to keep alive
InterpByteCodeStart* methodStartIP;
Comment thread
jakobbotsch marked this conversation as resolved.
COMPILER_SHARED_TYPE(CORINFO_CLASS_HANDLE, DPTR(MethodTable), asyncMethodReturnType);
Expand Down
Loading
Loading