diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 34eaf7557a6e46..10b04a41383d42 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -206,6 +206,7 @@
+
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 bb068f1290c0f8..68b68cdb9096cc 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
@@ -48,7 +48,10 @@ internal enum ContinuationFlags
internal unsafe struct ResumeInfo
{
public delegate* Resume;
- // IP to use for diagnostics. Points into the jitted suspension code.
+ // IP to use for diagnostics. Can be null for hand-rolled continuations
+ // like ValueTaskContinuation.
+ // For normal JIT-created continuations this points into the jitted
+ // suspension code.
// For debug codegen the IP resolves via an ASYNC native->IL mapping to
// the IL AsyncHelpers.Await (or other async function) call which
// caused the suspension.
@@ -125,6 +128,16 @@ public ref byte GetResultStorageOrNull()
ref byte data = ref RuntimeHelpers.GetRawData(this);
return ref Unsafe.Add(ref data, (DataOffset - PointerSize) + index * PointerSize);
}
+
+ protected void EncodeFieldOffsetInFlags(ref byte field, ContinuationFlags firstBit, ContinuationFlags numBits)
+ {
+ int offset = (int)Unsafe.ByteOffset(ref RuntimeHelpers.GetRawData(this), ref field);
+ offset -= DataOffset;
+ Debug.Assert(offset >= 0 && offset % PointerSize == 0);
+ uint index = 1 + (uint)offset / PointerSize;
+ Debug.Assert(index < (1 << (int)numBits));
+ Flags |= (ContinuationFlags)((uint)index << (int)firstBit);
+ }
}
[StructLayout(LayoutKind.Explicit)]
@@ -202,7 +215,7 @@ private ref struct RuntimeAsyncStackState
// to one of these notifiers.
public ICriticalNotifyCompletion? CriticalNotifier;
public INotifyCompletion? Notifier;
- public ValueTaskSourceNotifier? ValueTaskSourceNotifier;
+ public ValueTaskContinuation? ValueTaskContinuation;
public Task? TaskNotifier;
// When we suspend in the leaf, the contexts are captured into these fields.
@@ -245,6 +258,7 @@ public void Pop(Thread thread)
private unsafe struct RuntimeAsyncAwaitState
{
public Continuation? SentinelContinuation;
+ public ValueTaskContinuation? CachedValueTaskContinuation;
// We cache the thread here to avoid unnecessary repeated TLS lookups.
public Thread? CurrentThread;
@@ -331,7 +345,7 @@ private static unsafe Continuation AllocContinuationClass(Continuation prevConti
#endif
///
- /// Used by internal thunks that implement awaiting on Task or a ValueTask.
+ /// Used by internal thunks that implement awaiting on ValueTask.
/// A ValueTask may wrap:
/// - Completed result (we never await this)
/// - Task
@@ -339,23 +353,76 @@ private static unsafe Continuation AllocContinuationClass(Continuation prevConti
/// Therefore, when we are awaiting a ValueTask completion we are really
/// awaiting a completion of an underlying Task or ValueTaskSource.
///
- /// Task or a ValueTaskNotifier whose completion we are awaiting.
+ /// ValueTask whose completion we are awaiting.
+ [BypassReadyToRun]
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
+ private static unsafe void TransparentAwaitValueTask(ValueTask valueTask)
+ {
+ ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+ Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
+
+ ValueTaskContinuation? vtsCont = state.CachedValueTaskContinuation;
+ if (vtsCont != null)
+ {
+ state.CachedValueTaskContinuation = null;
+ }
+ else
+ {
+ vtsCont = new ValueTaskContinuation();
+ }
+
+ Debug.Assert(valueTask._obj != null);
+ vtsCont.Initialize(valueTask._obj, valueTask._token);
+ vtsCont.ExecutionContext = ExecutionContext.CaptureForSuspension(state.CurrentThread!);
+
+ sentinelContinuation.Next = vtsCont;
+ state.StackState->ValueTaskContinuation = vtsCont;
+
+ state.CaptureContexts();
+ AsyncSuspend(vtsCont);
+ }
+
[BypassReadyToRun]
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
- private static unsafe void TransparentAwait(object o)
+ private static unsafe void TransparentAwaitValueTaskOfT(ValueTask valueTask)
{
ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
- if (o is Task t)
+ ValueTaskContinuation? vtsCont = state.CachedValueTaskContinuation;
+ if (vtsCont != null)
{
- state.StackState->TaskNotifier = t;
+ state.CachedValueTaskContinuation = null;
}
else
{
- state.StackState->ValueTaskSourceNotifier = (ValueTaskSourceNotifier)o;
+ vtsCont = new ValueTaskContinuation();
}
+ Debug.Assert(valueTask._obj != null);
+ vtsCont.Initialize(valueTask._obj, valueTask._token);
+ vtsCont.ExecutionContext = ExecutionContext.CaptureForSuspension(state.CurrentThread!);
+
+ sentinelContinuation.Next = vtsCont;
+ state.StackState->ValueTaskContinuation = vtsCont;
+
+ state.CaptureContexts();
+ AsyncSuspend(vtsCont);
+ }
+
+ ///
+ /// Used by internal thunks that implement awaiting on Task.
+ ///
+ /// Task whose completion we are awaiting.
+ [BypassReadyToRun]
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
+ private static unsafe void TransparentAwait(Task t)
+ {
+ ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
+ Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
+
+ state.StackState->TaskNotifier = t;
+
state.CaptureContexts();
AsyncSuspend(sentinelContinuation);
}
@@ -456,8 +523,9 @@ internal unsafe bool HandleSuspended(ref RuntimeAsyncAwaitState state)
ThreadPool.UnsafeQueueUserWorkItemInternal(this, preferLocal: true);
}
}
- else if (stackState->ValueTaskSourceNotifier is { } valueTaskSourceNotifier)
+ else if (stackState->ValueTaskContinuation is { } valueTaskSourceCont)
{
+ Debug.Assert(headContinuation == valueTaskSourceCont);
// The awaiter must inform the ValueTaskSource on whether the continuation
// wants to run on a context, although the source may decide to ignore the suggestion.
// Since the behavior of the source takes precedence, we clear the context flags of
@@ -491,7 +559,7 @@ internal unsafe bool HandleSuspended(ref RuntimeAsyncAwaitState state)
// Clear continuation flags, so that continuation runs transparently
nextUserContinuation.Flags &= ~continueFlags;
- valueTaskSourceNotifier.OnCompleted(s_runContinuationAction, this, configFlags);
+ valueTaskSourceCont.OnCompleted(s_runContinuationAction, this, configFlags);
}
else
{
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.ValueTaskContinuation.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.ValueTaskContinuation.cs
new file mode 100644
index 00000000000000..a25865aabd0155
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.ValueTaskContinuation.cs
@@ -0,0 +1,153 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Threading.Tasks.Sources;
+
+namespace System.Runtime.CompilerServices
+{
+ public static partial class AsyncHelpers
+ {
+ private sealed unsafe class ValueTaskContinuation : Continuation
+ {
+ // Currently all continuations are expected to capture and restore
+ // ExecutionContext, even though we do not actually need it here.
+ public ExecutionContext? ExecutionContext;
+ private object? _source;
+ private short _token;
+ private delegate*, object?, short, ValueTaskSourceOnCompletedFlags, void> _onCompleted;
+ private delegate* _getResult;
+
+ public ValueTaskContinuation()
+ {
+ ResumeInfo = (ResumeInfo*)Unsafe.AsPointer(in ValueTaskContinuationResume.ResumeInfo);
+
+ EncodeFieldOffsetInFlags(
+ ref Unsafe.As(ref ExecutionContext),
+ ContinuationFlags.ExecutionContextIndexFirstBit,
+ ContinuationFlags.ExecutionContextIndexNumBits);
+ }
+
+ public void OnCompleted(Action continuation, object? state, ValueTaskSourceOnCompletedFlags flags)
+ {
+ Debug.Assert(_source != null);
+ _onCompleted(_source, continuation, state, _token, flags);
+ }
+
+ public void GetResult(ref byte returnValue)
+ {
+ Debug.Assert(_source != null);
+
+ // Avoid retaining source. The call below may throw.
+ object source = _source;
+ _source = null;
+
+ _getResult(source, _token, ref returnValue);
+ }
+
+ public void Initialize(object source, short token)
+ {
+ _source = source;
+ _token = token;
+ _onCompleted = &OnCompleted;
+ _getResult = &GetResult;
+ }
+
+ public void Initialize(object source, short token)
+ {
+ _source = source;
+ _token = token;
+ _onCompleted = &OnCompleted;
+ _getResult = &GetResult;
+ }
+
+ private static void OnCompleted(object source, Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
+ {
+ if (source is Task t)
+ {
+ Debug.Assert(state is ITaskCompletionAction);
+ if (!t.TryAddCompletionAction(Unsafe.As(ref state)))
+ {
+ ThreadPool.UnsafeQueueUserWorkItemInternal(state, preferLocal: true);
+ }
+ }
+ else
+ {
+ Debug.Assert(source is IValueTaskSource);
+ IValueTaskSource typedSource = Unsafe.As(ref source);
+ typedSource.OnCompleted(continuation, state, token, flags);
+ }
+ }
+
+ private static void GetResult(object source, short token, ref byte result)
+ {
+ if (source is Task t)
+ {
+ TaskAwaiter.ValidateEnd(t);
+ }
+ else
+ {
+ Debug.Assert(source is IValueTaskSource);
+ IValueTaskSource typedSource = Unsafe.As(ref source);
+ typedSource.GetResult(token);
+ }
+ }
+
+ private static void OnCompleted(object source, Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
+ {
+ if (source is Task t)
+ {
+ Debug.Assert(state is ITaskCompletionAction);
+ if (!t.TryAddCompletionAction(Unsafe.As(ref state)))
+ {
+ ThreadPool.UnsafeQueueUserWorkItemInternal(state, preferLocal: true);
+ }
+ }
+ else
+ {
+ Debug.Assert(source is IValueTaskSource);
+ IValueTaskSource typedSource = Unsafe.As>(ref source);
+ typedSource.OnCompleted(continuation, state, token, flags);
+ }
+ }
+
+ private static void GetResult(object source, short token, ref byte result)
+ {
+ if (source is Task t)
+ {
+ TaskAwaiter.ValidateEnd(t);
+ Unsafe.As(ref result) = t.ResultOnSuccess;
+ }
+ else
+ {
+ Debug.Assert(source is IValueTaskSource);
+ IValueTaskSource typedSource = Unsafe.As>(ref source);
+ Unsafe.As(ref result) = typedSource.GetResult(token);
+ }
+ }
+
+ private static class ValueTaskContinuationResume
+ {
+ [FixedAddressValueType]
+ public static readonly ResumeInfo ResumeInfo = new ResumeInfo
+ {
+ DiagnosticIP = null,
+ Resume = &ResumeValueTaskContinuation,
+ };
+
+ public static Continuation? ResumeValueTaskContinuation(Continuation cont, ref byte result)
+ {
+ var vtsCont = (ValueTaskContinuation)cont;
+ vtsCont.Next = null;
+ vtsCont.ExecutionContext = null;
+ t_runtimeAsyncAwaitState.CachedValueTaskContinuation = vtsCont;
+
+ vtsCont.GetResult(ref result);
+ return null;
+ }
+ }
+ }
+ }
+}
diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp
index bce89a76385ff1..9b31462c536d50 100644
--- a/src/coreclr/debug/daccess/dacdbiimpl.cpp
+++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp
@@ -7165,7 +7165,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsValidObject(CORDB_ADDRESS obj,
if (mt == cls->GetMethodTable())
isValid = TRUE;
- else if (!mt->IsCanonicalMethodTable() || mt->IsContinuation())
+ else if (!mt->IsCanonicalMethodTable() || mt->IsContinuationWithoutMetadata())
isValid = cls->GetMethodTable()->GetClass() == cls;
}
EX_CATCH
diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h
index 0b07ee0ceb0b15..5e32a086c7c61f 100644
--- a/src/coreclr/inc/dacvars.h
+++ b/src/coreclr/inc/dacvars.h
@@ -180,6 +180,7 @@ DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pWeakReferenceClass, ::g_pWeakReferen
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pWeakReferenceOfTClass, ::g_pWeakReferenceOfTClass)
DEFINE_DACVAR_VOLATILE(UNKNOWN_POINTER_TYPE, dac__g_pContinuationClassIfSubTypeCreated, ::g_pContinuationClassIfSubTypeCreated)
+DEFINE_DACVAR_VOLATILE(UNKNOWN_POINTER_TYPE, dac__g_singletonContinuationEEClass, ::g_singletonContinuationEEClass)
#ifdef FEATURE_COMINTEROP
DEFINE_DACVAR(UNKNOWN_POINTER_TYPE, dac__g_pBaseCOMObject, ::g_pBaseCOMObject)
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index fa8900ae1a3e06..f687deaff880bc 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -64,6 +64,7 @@
+
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
index d5f367c6498b0b..8dac68d3d4bb6a 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
@@ -292,21 +292,26 @@ public static MethodIL EmitAsyncMethodThunk(MethodDesc asyncMethod, MethodDesc t
TypeDesc valueTaskType = taskReturningMethodReturnType;
MethodDesc isCompletedMethod;
MethodDesc completionResultMethod;
- MethodDesc asTaskOrNotifierMethod;
+ MethodDesc transparentAwaitValueTaskMethod;
if (!taskReturningMethodReturnType.HasInstantiation)
{
// ValueTask (non-generic)
isCompletedMethod = valueTaskType.GetKnownMethod("get_IsCompleted"u8, null);
completionResultMethod = valueTaskType.GetKnownMethod("ThrowIfCompletedUnsuccessfully"u8, null);
- asTaskOrNotifierMethod = valueTaskType.GetKnownMethod("AsTaskOrNotifier"u8, null);
+ transparentAwaitValueTaskMethod =
+ context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+ .GetKnownMethod("TransparentAwaitValueTask"u8, null);
}
else
{
// ValueTask (generic)
isCompletedMethod = valueTaskType.GetKnownMethod("get_IsCompleted"u8, null);
completionResultMethod = valueTaskType.GetKnownMethod("get_Result"u8, null);
- asTaskOrNotifierMethod = valueTaskType.GetKnownMethod("AsTaskOrNotifier"u8, null);
+ transparentAwaitValueTaskMethod =
+ context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
+ .GetKnownMethod("TransparentAwaitValueTaskOfT"u8, null)
+ .MakeInstantiatedMethod(valueTaskType.Instantiation[0]);
}
ILLocalVariable valueTaskLocal = emitter.NewLocal(valueTaskType);
@@ -315,15 +320,17 @@ public static MethodIL EmitAsyncMethodThunk(MethodDesc asyncMethod, MethodDesc t
// Store value task returned by call to actual user func
codestream.EmitStLoc(valueTaskLocal);
codestream.EmitLdLoca(valueTaskLocal);
+
+ // Was it already completed?
codestream.Emit(ILOpcode.call, emitter.NewToken(isCompletedMethod));
codestream.Emit(ILOpcode.brtrue, valueTaskCompletedLabel);
- codestream.EmitLdLoca(valueTaskLocal);
- codestream.Emit(ILOpcode.call, emitter.NewToken(asTaskOrNotifierMethod));
- codestream.Emit(ILOpcode.call, emitter.NewToken(
- context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
- .GetKnownMethod("TransparentAwait"u8, null)));
+ // No, tail await to TransparentAwaitValueTask
+ codestream.EmitLdLoc(valueTaskLocal);
+ codestream.Emit(ILOpcode.call, emitter.NewToken(context.GetCoreLibEntryPoint("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8, "TailAwait"u8, null)));
+ codestream.Emit(ILOpcode.call, emitter.NewToken(transparentAwaitValueTaskMethod));
+ // Yes, just get the result
codestream.EmitLabel(valueTaskCompletedLabel);
codestream.EmitLdLoca(valueTaskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(completionResultMethod));
diff --git a/src/coreclr/vm/asynccontinuations.cpp b/src/coreclr/vm/asynccontinuations.cpp
index 0005682e3670ac..b139fd45a61a36 100644
--- a/src/coreclr/vm/asynccontinuations.cpp
+++ b/src/coreclr/vm/asynccontinuations.cpp
@@ -43,8 +43,6 @@ void AsyncContinuationsManager::NotifyUnloadingClasses()
#endif // PROFILING_SUPPORTED
}
-static EEClass* volatile g_singletonContinuationEEClass;
-
EEClass* AsyncContinuationsManager::GetOrCreateSingletonSubContinuationEEClass()
{
if (g_singletonContinuationEEClass != NULL)
diff --git a/src/coreclr/vm/asyncthunks.cpp b/src/coreclr/vm/asyncthunks.cpp
index ef45a4a41c715d..c3dfa0a60fc84a 100644
--- a/src/coreclr/vm/asyncthunks.cpp
+++ b/src/coreclr/vm/asyncthunks.cpp
@@ -499,10 +499,8 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
// ValueTask vt = other(arg);
// if (!vt.IsCompleted)
// {
- // taskOrNotifier = vt.AsTaskOrNotifier()
-
- // // Magic function which will suspend the current run of async methods
- // AsyncHelpers.TransparentAwait(taskOrNotifier);
+ // TailAwait();
+ // AsyncHelpers.TransparentAwaitValueTask(vt);
// }
// return vt.Result/vt.ThrowIfCompletedUnsuccessfully();
@@ -538,18 +536,18 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
MethodTable* pMTValueTask;
int isCompletedToken;
int completionResultToken;
- int asTaskOrNotifierToken;
+ int transparentAwaitValueTaskToken;
if (msig.IsReturnTypeVoid())
{
pMTValueTask = CoreLibBinder::GetClass(CLASS__VALUETASK);
MethodDesc* pMDValueTaskIsCompleted = CoreLibBinder::GetMethod(METHOD__VALUETASK__GET_ISCOMPLETED);
MethodDesc* pMDCompletionResult = CoreLibBinder::GetMethod(METHOD__VALUETASK__THROW_IF_COMPLETED_UNSUCCESSFULLY);
- MethodDesc* pMDAsTaskOrNotifier = CoreLibBinder::GetMethod(METHOD__VALUETASK__AS_TASK_OR_NOTIFIER);
+ MethodDesc* pMDTransparentAwaitValueTask = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_VALUE_TASK);
isCompletedToken = pCode->GetToken(pMDValueTaskIsCompleted);
completionResultToken = pCode->GetToken(pMDCompletionResult);
- asTaskOrNotifierToken = pCode->GetToken(pMDAsTaskOrNotifier);
+ transparentAwaitValueTaskToken = pCode->GetToken(pMDTransparentAwaitValueTask);
}
else
{
@@ -558,15 +556,15 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
MethodDesc* pMDValueTaskIsCompleted = CoreLibBinder::GetMethod(METHOD__VALUETASK_1__GET_ISCOMPLETED);
MethodDesc* pMDCompletionResult = CoreLibBinder::GetMethod(METHOD__VALUETASK_1__GET_RESULT);
- MethodDesc* pMDAsTaskOrNotifier = CoreLibBinder::GetMethod(METHOD__VALUETASK_1__AS_TASK_OR_NOTIFIER);
+ MethodDesc* pMDTransparentAwaitValueTask = CoreLibBinder::GetMethod(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT_VALUE_TASK_OF_T);
pMDValueTaskIsCompleted = FindOrCreateAssociatedMethodDesc(pMDValueTaskIsCompleted, pMTValueTask, FALSE, Instantiation(), FALSE);
pMDCompletionResult = FindOrCreateAssociatedMethodDesc(pMDCompletionResult, pMTValueTask, FALSE, Instantiation(), FALSE);
- pMDAsTaskOrNotifier = FindOrCreateAssociatedMethodDesc(pMDAsTaskOrNotifier, pMTValueTask, FALSE, Instantiation(), FALSE);
+ pMDTransparentAwaitValueTask = FindOrCreateAssociatedMethodDesc(pMDTransparentAwaitValueTask, pMDTransparentAwaitValueTask->GetMethodTable(), FALSE, Instantiation(&thLogicalRetType, 1), FALSE);
isCompletedToken = GetTokenForGenericTypeMethodCallWithAsyncReturnType(pCode, pMDValueTaskIsCompleted);
completionResultToken = GetTokenForGenericTypeMethodCallWithAsyncReturnType(pCode, pMDCompletionResult);
- asTaskOrNotifierToken = GetTokenForGenericTypeMethodCallWithAsyncReturnType(pCode, pMDAsTaskOrNotifier);
+ transparentAwaitValueTaskToken = GetTokenForGenericMethodCallWithAsyncReturnType(pCode, pMDTransparentAwaitValueTask);
}
LocalDesc valueTaskLocalDesc(pMTValueTask);
@@ -576,13 +574,17 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
// Store value task returned by call to actual user func
pCode->EmitSTLOC(valueTaskLocal);
pCode->EmitLDLOCA(valueTaskLocal);
+
+ // Was it already completed?
pCode->EmitCALL(isCompletedToken, 1, 1);
pCode->EmitBRTRUE(valueTaskCompletedLabel);
- pCode->EmitLDLOCA(valueTaskLocal);
- pCode->EmitCALL(asTaskOrNotifierToken, 1, 1);
- pCode->EmitCALL(METHOD__ASYNC_HELPERS__TRANSPARENT_AWAIT, 1, 0);
+ // No, tail await to TransparentAwaitValueTask
+ pCode->EmitLDLOC(valueTaskLocal);
+ pCode->EmitCALL(METHOD__ASYNC_HELPERS__TAIL_AWAIT, 0, 0);
+ pCode->EmitCALL(transparentAwaitValueTaskToken, 1, 0);
+ // Yes, just get the result
pCode->EmitLabel(valueTaskCompletedLabel);
pCode->EmitLDLOCA(valueTaskLocal);
pCode->EmitCALL(completionResultToken, 1, msig.IsReturnTypeVoid() ? 0 : 1);
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 485792072ae4d5..4a36a6518a0446 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -328,14 +328,12 @@ DEFINE_METHOD(FILE_LOAD_EXCEPTION, CREATE, Create, NoSig)
DEFINE_CLASS(VALUETASK_1, Tasks, ValueTask`1)
DEFINE_METHOD(VALUETASK_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
DEFINE_METHOD(VALUETASK_1, GET_RESULT, get_Result, NoSig)
-DEFINE_METHOD(VALUETASK_1, AS_TASK_OR_NOTIFIER, AsTaskOrNotifier, IM_RetObj)
DEFINE_CLASS(VALUETASK, Tasks, ValueTask)
DEFINE_METHOD(VALUETASK, FROM_RESULT_T, FromResult, GM_T_RetValueTaskOfT)
DEFINE_METHOD(VALUETASK, GET_COMPLETED_TASK, get_CompletedTask, SM_RetValueTask)
DEFINE_METHOD(VALUETASK, GET_ISCOMPLETED, get_IsCompleted, NoSig)
DEFINE_METHOD(VALUETASK, THROW_IF_COMPLETED_UNSUCCESSFULLY, ThrowIfCompletedUnsuccessfully, NoSig)
-DEFINE_METHOD(VALUETASK, AS_TASK_OR_NOTIFIER, AsTaskOrNotifier, IM_RetObj)
DEFINE_CLASS(TASK_1, Tasks, Task`1)
@@ -717,7 +715,9 @@ DEFINE_METHOD(ASYNC_HELPERS, TASK_FROM_EXCEPTION_1, TaskFromException, GM_E
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, TransparentAwait, 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, COMPLETED_TASK_RESULT, CompletedTaskResult, NoSig)
DEFINE_METHOD(ASYNC_HELPERS, COMPLETED_TASK, CompletedTask, NoSig)
DEFINE_METHOD(ASYNC_HELPERS, CAPTURE_EXECUTION_CONTEXT, CaptureExecutionContext, NoSig)
@@ -729,6 +729,7 @@ DEFINE_METHOD(ASYNC_HELPERS, FINISH_SUSPENSION_NO_CONTINUATION_CONTEXT, Fin
DEFINE_METHOD(ASYNC_HELPERS, FINISH_SUSPENSION_WITH_CONTINUATION_CONTEXT, FinishSuspensionWithContinuationContext, NoSig)
DEFINE_METHOD(ASYNC_HELPERS, ASYNC_CALL_CONTINUATION, AsyncCallContinuation, NoSig)
DEFINE_METHOD(ASYNC_HELPERS, TAIL_AWAIT, TailAwait, NoSig)
+
DEFINE_FIELD(ASYNC_HELPERS, TLS_RUNTIME_ASYNC_AWAIT_STATE, t_runtimeAsyncAwaitState)
#ifdef FEATURE_INTERPRETER
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index aaf24318c47019..3e76a304bce4c5 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -3478,7 +3478,7 @@ size_t CEEInfo::printClassName(CORINFO_CLASS_HANDLE cls, char* buffer, size_t bu
mdTypeDef td = th.GetCl();
if (IsNilToken(td))
{
- if (th.IsContinuation())
+ if (th.IsContinuationWithoutMetadata())
{
AsyncContinuationsManager::PrintContinuationName(
th.AsMethodTable(),
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index 5a5a714a360edf..cdf596f6c91c5f 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -375,7 +375,7 @@ BOOL MethodTable::ValidateWithPossibleAV()
}
// generic instantiation check
- return (HasInstantiation() || IsArray() || IsContinuation()) && (pEEClassFromMethodTable->GetClassWithPossibleAV() == pEEClass);
+ return (HasInstantiation() || IsArray() || IsContinuationWithoutMetadata()) && (pEEClassFromMethodTable->GetClassWithPossibleAV() == pEEClass);
}
@@ -421,7 +421,7 @@ WORD MethodTable::GetNumMethods()
PTR_MethodTable MethodTable::GetTypicalMethodTable()
{
LIMITED_METHOD_DAC_CONTRACT;
- if (IsArray() || IsContinuation())
+ if (IsArray() || IsContinuationWithoutMetadata())
return (PTR_MethodTable)this;
PTR_MethodTable methodTableMaybe = GetModule()->LookupTypeDef(GetCl()).AsMethodTable();
@@ -1297,7 +1297,7 @@ BOOL MethodTable::CanCastToClass(MethodTable *pTargetMT, TypeHandlePairList *pVi
PRECONDITION(CheckPointer(pTargetMT));
PRECONDITION(!pTargetMT->IsArray());
PRECONDITION(!pTargetMT->IsInterface());
- PRECONDITION(!pTargetMT->IsContinuation());
+ PRECONDITION(!pTargetMT->IsContinuationWithoutMetadata());
}
CONTRACTL_END
@@ -6396,9 +6396,16 @@ BOOL MethodTable::SanityCheck()
if (GetNumGenericArgs() != 0)
return (pCanonMT->GetClass() == pClass);
else
- return (pCanonMT == this) || IsArray() || IsContinuation();
+ return (pCanonMT == this) || IsArray() || IsContinuationWithoutMetadata();
}
+BOOL MethodTable::IsContinuationWithoutMetadata()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ PTR_MethodTable contClass = g_pContinuationClassIfSubTypeCreated;
+ return contClass != NULL && m_pParentMethodTable == contClass && GetClass() == g_singletonContinuationEEClass;
+}
//==========================================================================================
void MethodTable::SetCl(mdTypeDef token)
@@ -7642,7 +7649,7 @@ CHECK MethodTable::CheckInstanceActivated()
{
WRAPPER_NO_CONTRACT;
- if (IsArray() || IsContinuation())
+ if (IsArray() || IsContinuationWithoutMetadata())
CHECK_OK;
diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h
index 402b0cde9b15bd..cffa4b1fa94e5b 100644
--- a/src/coreclr/vm/methodtable.h
+++ b/src/coreclr/vm/methodtable.h
@@ -985,7 +985,7 @@ class MethodTable
PTR_Module GetModule()
{
LIMITED_METHOD_CONTRACT;
- _ASSERTE(!IsContinuation());
+ _ASSERTE(!IsContinuationWithoutMetadata());
return m_pModule;
}
@@ -2852,7 +2852,7 @@ class MethodTable
SetFlag(enum_flag_Category_Nullable);
}
- inline BOOL IsContinuation();
+ BOOL IsContinuationWithoutMetadata();
// The following methods are only valid for the method tables for array types.
CorElementType GetArrayElementType()
diff --git a/src/coreclr/vm/methodtable.inl b/src/coreclr/vm/methodtable.inl
index d0b112d01d7526..261ef7969eaebc 100644
--- a/src/coreclr/vm/methodtable.inl
+++ b/src/coreclr/vm/methodtable.inl
@@ -299,14 +299,6 @@ inline BOOL MethodTable::IsValueType()
return GetFlag(enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType;
}
-inline BOOL MethodTable::IsContinuation()
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- PTR_MethodTable contClass = g_pContinuationClassIfSubTypeCreated;
- return contClass != NULL && m_pParentMethodTable == contClass;
-}
-
//==========================================================================================
inline DWORD MethodTable::GetRank()
{
diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp
index 5dd7d4e85d468a..5c8f4738fcf97a 100644
--- a/src/coreclr/vm/typehandle.cpp
+++ b/src/coreclr/vm/typehandle.cpp
@@ -91,11 +91,11 @@ BOOL TypeHandle::IsString() const
return !IsTypeDesc() && AsMethodTable()->IsString();
}
-BOOL TypeHandle::IsContinuation() const
+BOOL TypeHandle::IsContinuationWithoutMetadata() const
{
LIMITED_METHOD_CONTRACT;
- return !IsTypeDesc() && AsMethodTable()->IsContinuation();
+ return !IsTypeDesc() && AsMethodTable()->IsContinuationWithoutMetadata();
}
BOOL TypeHandle::IsGenericVariable() const {
@@ -333,7 +333,7 @@ void TypeHandle::AllocateManagedClassObject(RUNTIMETYPEHANDLE* pDest)
}
CONTRACTL_END
- if (IsContinuation())
+ if (IsContinuationWithoutMetadata())
{
COMPlusThrow(kNotSupportedException, W("NotSupported_Continuation"));
return;
diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h
index 1fb2f944bb509d..f63abc781b08a6 100644
--- a/src/coreclr/vm/typehandle.h
+++ b/src/coreclr/vm/typehandle.h
@@ -443,7 +443,7 @@ class TypeHandle
BOOL IsString() const;
// Continuation sub types
- BOOL IsContinuation() const;
+ BOOL IsContinuationWithoutMetadata() const;
// True if this type *is* a formal generic type parameter or any component of it is a formal generic type parameter
BOOL ContainsGenericVariables(BOOL methodOnly=FALSE) const;
diff --git a/src/coreclr/vm/typehandle.inl b/src/coreclr/vm/typehandle.inl
index d1916c121e56c2..2781f22d54bfb5 100644
--- a/src/coreclr/vm/typehandle.inl
+++ b/src/coreclr/vm/typehandle.inl
@@ -38,7 +38,7 @@ inline TypeHandle TypeHandle::UpCastTypeIfNeeded() const
if (IsTypeDesc())
return *this;
- if (AsMethodTable()->IsContinuation())
+ if (AsMethodTable()->IsContinuationWithoutMetadata())
{
return TypeHandle(g_pContinuationClassIfSubTypeCreated);
}
diff --git a/src/coreclr/vm/typestring.cpp b/src/coreclr/vm/typestring.cpp
index 2c22666561a7ea..4c125ea3121e51 100644
--- a/src/coreclr/vm/typestring.cpp
+++ b/src/coreclr/vm/typestring.cpp
@@ -837,7 +837,7 @@ void TypeString::AppendType(TypeNameBuilder& tnb, TypeHandle ty, Instantiation t
mdTypeDef td = ty.GetCl();
if (IsNilToken(td))
{
- if (ty.IsContinuation())
+ if (ty.IsContinuationWithoutMetadata())
{
AsyncContinuationsManager::PrintContinuationName(
ty.AsMethodTable(),
diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp
index a567a206749c62..8567eabc0c1d1c 100644
--- a/src/coreclr/vm/vars.cpp
+++ b/src/coreclr/vm/vars.cpp
@@ -68,8 +68,10 @@ GPTR_IMPL(MethodTable, g_pWeakReferenceOfTClass);
#ifdef DACCESS_COMPILE
GPTR_IMPL(MethodTable, g_pContinuationClassIfSubTypeCreated);
+GPTR_IMPL(EEClass, g_singletonContinuationEEClass);
#else
GVAL_IMPL(Volatile, g_pContinuationClassIfSubTypeCreated);
+GVAL_IMPL(Volatile, g_singletonContinuationEEClass);
#endif
#ifdef FEATURE_COMINTEROP
diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp
index acae3ae57e7817..73df857cee3d40 100644
--- a/src/coreclr/vm/vars.hpp
+++ b/src/coreclr/vm/vars.hpp
@@ -359,8 +359,10 @@ GPTR_DECL(MethodTable, g_pWeakReferenceOfTClass);
#ifdef DACCESS_COMPILE
GPTR_DECL(MethodTable, g_pContinuationClassIfSubTypeCreated);
+GPTR_DECL(EEClass, g_singletonContinuationEEClass);
#else
GVAL_DECL(Volatile, g_pContinuationClassIfSubTypeCreated);
+GVAL_DECL(Volatile, g_singletonContinuationEEClass);
#endif
#ifdef FEATURE_COMINTEROP
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs
index 4eaf2679966461..90f86c68159c28 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs
@@ -179,21 +179,6 @@ obj as Task ??
GetTaskForValueTaskSource(Unsafe.As(obj));
}
- ///
- /// Helper to invoke IValueTaskSource.OnCompleted from a caller that does not know the actual type of the source.
- ///
- private static void OnCompleted(object obj, Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) =>
- Unsafe.As(obj).OnCompleted(continuation, state, token, flags);
-
- internal unsafe object AsTaskOrNotifier()
- {
- object? obj = _obj;
- Debug.Assert(obj is Task || obj is IValueTaskSource);
- return
- obj as Task ??
- (object)ValueTaskSourceNotifier.GetInstance(obj, &OnCompleted, _token);
- }
-
/// Gets a that may be used at any point in the future.
public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
@@ -603,21 +588,6 @@ public Task AsTask()
return GetTaskForValueTaskSource(Unsafe.As>(obj));
}
- ///
- /// Helper to invoke IValueTaskSource.OnCompleted from a caller that does not know the actual type of the source.
- ///
- private static void OnCompleted(object obj, Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) =>
- Unsafe.As>(obj).OnCompleted(continuation, state, token, flags);
-
- internal unsafe object AsTaskOrNotifier()
- {
- object? obj = _obj;
- Debug.Assert(obj is Task || obj is IValueTaskSource);
- return
- obj as Task ??
- (object)ValueTaskSourceNotifier.GetInstance(obj, &OnCompleted, _token);
- }
-
/// Gets a that may be used at any point in the future.
public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
@@ -878,54 +848,4 @@ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCaptu
return string.Empty;
}
}
-
- internal sealed unsafe class ValueTaskSourceNotifier
- {
- // ValueTaskSourceNotifier is used only during suspension sequence, thus
- // a given thread will never need more than one instance.
- // We will just reuse the same instance when needed.
- [ThreadStatic]
- private static ValueTaskSourceNotifier? t_instance;
-
- private object _source;
- private delegate* managed, object?, short, ValueTaskSourceOnCompletedFlags, void> _onCompleted;
- private short _token;
-
- private ValueTaskSourceNotifier(
- object source,
- delegate* managed, object?, short, ValueTaskSourceOnCompletedFlags, void> onCompleted,
- short token)
- {
- _source = source;
- _onCompleted = onCompleted;
- _token = token;
- }
-
- public static ValueTaskSourceNotifier GetInstance(
- object source,
- delegate* managed, object?, short, ValueTaskSourceOnCompletedFlags, void> onCompleted,
- short token)
- {
- ValueTaskSourceNotifier? instance = t_instance;
- if (instance == null)
- {
- return t_instance = new ValueTaskSourceNotifier(source, onCompleted, token);
- }
-
- instance._source = source;
- instance._onCompleted = onCompleted;
- instance._token = token;
-
- return instance;
- }
-
- public void OnCompleted(Action continuation, object? state, ValueTaskSourceOnCompletedFlags flags)
- {
- _onCompleted(_source, continuation, state, _token, flags);
-
- // The data that we store is effectively single-use.
- // Once used, clear the _source to not retain unknown data.
- _source = null!;
- }
- }
}