Skip to content

Commit 0a321bb

Browse files
authored
Allow continuations to have no ExecutionContext (#128323)
`ValueTaskContinuation` does not need to save/restore ExecutionContext, and also in the future we expect to allow continuations to skip this save/restore when the JIT proved that it is not going to be looked at.
1 parent 756ebec commit 0a321bb

2 files changed

Lines changed: 19 additions & 25 deletions

File tree

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

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,20 @@ public bool HasException()
9999
}
100100

101101
[MethodImpl(MethodImplOptions.AggressiveInlining)]
102-
public unsafe ExecutionContext? GetExecutionContext()
102+
public unsafe bool TryGetExecutionContext(out ExecutionContext? execContext)
103103
{
104104
const uint mask = (1u << (int)ContinuationFlags.ExecutionContextIndexNumBits) - 1;
105105
uint index = ((uint)Flags >> (int)ContinuationFlags.ExecutionContextIndexFirstBit) & mask;
106+
if (index == 0)
107+
{
108+
execContext = null;
109+
return false;
110+
}
111+
106112
Debug.Assert(index != 0);
107113
ref byte data = ref RuntimeHelpers.GetRawData(this);
108-
return Unsafe.As<byte, ExecutionContext?>(ref Unsafe.Add(ref data, (DataOffset - PointerSize) + index * PointerSize));
114+
execContext = Unsafe.As<byte, ExecutionContext?>(ref Unsafe.Add(ref data, (DataOffset - PointerSize) + index * PointerSize));
115+
return true;
109116
}
110117

111118
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -128,16 +135,6 @@ public ref byte GetResultStorageOrNull()
128135
ref byte data = ref RuntimeHelpers.GetRawData(this);
129136
return ref Unsafe.Add(ref data, (DataOffset - PointerSize) + index * PointerSize);
130137
}
131-
132-
protected void EncodeFieldOffsetInFlags(ref byte field, ContinuationFlags firstBit, ContinuationFlags numBits)
133-
{
134-
int offset = (int)Unsafe.ByteOffset(ref RuntimeHelpers.GetRawData(this), ref field);
135-
offset -= DataOffset;
136-
Debug.Assert(offset >= 0 && offset % PointerSize == 0);
137-
uint index = 1 + (uint)offset / PointerSize;
138-
Debug.Assert(index < (1 << (int)numBits));
139-
Flags |= (ContinuationFlags)((uint)index << (int)firstBit);
140-
}
141138
}
142139

143140
[StructLayout(LayoutKind.Explicit)]
@@ -373,7 +370,6 @@ private static unsafe void TransparentAwaitValueTask(ValueTask valueTask)
373370

374371
Debug.Assert(valueTask._obj != null);
375372
vtsCont.Initialize(valueTask._obj, valueTask._token);
376-
vtsCont.ExecutionContext = ExecutionContext.CaptureForSuspension(state.CurrentThread!);
377373

378374
sentinelContinuation.Next = vtsCont;
379375
state.StackState->ValueTaskContinuation = vtsCont;
@@ -401,7 +397,6 @@ private static unsafe void TransparentAwaitValueTaskOfT<T>(ValueTask<T?> valueTa
401397

402398
Debug.Assert(valueTask._obj != null);
403399
vtsCont.Initialize<T>(valueTask._obj, valueTask._token);
404-
vtsCont.ExecutionContext = ExecutionContext.CaptureForSuspension(state.CurrentThread!);
405400

406401
sentinelContinuation.Next = vtsCont;
407402
state.StackState->ValueTaskContinuation = vtsCont;
@@ -660,7 +655,11 @@ private unsafe void DispatchContinuations()
660655
asyncDispatcherInfo.NextContinuation = nextContinuation;
661656

662657
Debug.Assert(awaitState.CurrentThread != null);
663-
RestoreExecutionContext(awaitState.CurrentThread, curContinuation.GetExecutionContext());
658+
if (curContinuation.TryGetExecutionContext(out ExecutionContext? execContext))
659+
{
660+
RestoreExecutionContext(awaitState.CurrentThread, execContext);
661+
}
662+
664663
ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref GetResultStorage();
665664

666665
Continuation? newContinuation = curContinuation.ResumeInfo->Resume(curContinuation, ref resultLoc);
@@ -766,7 +765,11 @@ private unsafe void InstrumentedDispatchContinuations(AsyncInstrumentation.Flags
766765
asyncDispatcherInfo.NextContinuation = nextContinuation;
767766

768767
Debug.Assert(awaitState.CurrentThread != null);
769-
RestoreExecutionContext(awaitState.CurrentThread, curContinuation.GetExecutionContext());
768+
if (curContinuation.TryGetExecutionContext(out ExecutionContext? execContext))
769+
{
770+
RestoreExecutionContext(awaitState.CurrentThread, execContext);
771+
}
772+
770773
ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref GetResultStorage();
771774

772775
RuntimeAsyncInstrumentationHelpers.ResumeRuntimeAsyncMethod(ref asyncDispatcherInfo, flags, curContinuation);

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ public static partial class AsyncHelpers
1212
{
1313
private sealed unsafe class ValueTaskContinuation : Continuation
1414
{
15-
// Currently all continuations are expected to capture and restore
16-
// ExecutionContext, even though we do not actually need it here.
17-
public ExecutionContext? ExecutionContext;
1815
internal object? Source;
1916
internal short Token;
2017
internal delegate*<object, Action<object?>, object?, short, ValueTaskSourceOnCompletedFlags, void> OnCompletedValueTaskSource;
@@ -23,11 +20,6 @@ private sealed unsafe class ValueTaskContinuation : Continuation
2320
public ValueTaskContinuation()
2421
{
2522
ResumeInfo = (ResumeInfo*)Unsafe.AsPointer(in ValueTaskContinuationResume.ResumeInfo);
26-
27-
EncodeFieldOffsetInFlags(
28-
ref Unsafe.As<ExecutionContext?, byte>(ref ExecutionContext),
29-
ContinuationFlags.ExecutionContextIndexFirstBit,
30-
ContinuationFlags.ExecutionContextIndexNumBits);
3123
}
3224

3325
public void GetResult(ref byte returnValue)
@@ -113,7 +105,6 @@ private static class ValueTaskContinuationResume
113105
{
114106
var vtsCont = (ValueTaskContinuation)cont;
115107
vtsCont.Next = null;
116-
vtsCont.ExecutionContext = null;
117108
t_runtimeAsyncAwaitState.CachedValueTaskContinuation = vtsCont;
118109

119110
vtsCont.GetResult(ref result);

0 commit comments

Comments
 (0)