From: Stephen Toub Date: Fri, 6 Apr 2018 13:48:35 +0000 (-0700) Subject: Add YieldAwaiter support to the async method builder delegate optimization (#17441) X-Git-Tag: accepted/tizen/unified/20190422.045933~2382 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=721343dc84247d54ee5f3365b97b06090dfa8ade;p=platform%2Fupstream%2Fcoreclr.git Add YieldAwaiter support to the async method builder delegate optimization (#17441) We added an optimization to async methods that lets the builder avoid allocating the Action delegate when it recognizes the awaiter being used. Previously this was enabled for all of the publicly exposed awaiters in corelib, with the exception of YieldAwaiter (what's used with Task.YIeld). It was enabled by having the awaiter implement an internal interface. This commit just generalizes that interface name and then implements it on YIeldAwaiter to complete the picture. --- diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 5994531..11e6215 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -33,7 +33,7 @@ namespace System.Runtime.CompilerServices [StructLayout(LayoutKind.Auto)] public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion #if CORECLR - , IValueTaskAwaiter + , IStateMachineBoxAwareAwaiter #endif { /// The value being awaited. @@ -94,7 +94,7 @@ namespace System.Runtime.CompilerServices } #if CORECLR - void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) + void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) { if (_value.ObjectIsTask) { @@ -135,7 +135,7 @@ namespace System.Runtime.CompilerServices [StructLayout(LayoutKind.Auto)] public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion #if CORECLR - , IValueTaskAwaiter + , IStateMachineBoxAwareAwaiter #endif { /// The value being awaited. @@ -196,7 +196,7 @@ namespace System.Runtime.CompilerServices } #if CORECLR - void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) + void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) { if (_value.ObjectIsTask) { diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 12bac0e..31955b6 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -11,7 +11,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for a . public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion #if CORECLR - , IValueTaskAwaiter + , IStateMachineBoxAwareAwaiter #endif { /// Shim used to invoke an passed as the state argument to a . @@ -80,7 +80,7 @@ namespace System.Runtime.CompilerServices } #if CORECLR - void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) + void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) { if (_value.ObjectIsTask) { @@ -113,7 +113,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for a . public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion #if CORECLR - , IValueTaskAwaiter + , IStateMachineBoxAwareAwaiter #endif { /// The value being awaited. @@ -171,7 +171,7 @@ namespace System.Runtime.CompilerServices } #if CORECLR - void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) + void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) { if (_value.ObjectIsTask) { @@ -190,8 +190,8 @@ namespace System.Runtime.CompilerServices } #if CORECLR - /// Internal interface used to enable optimizations from on .> - internal interface IValueTaskAwaiter + /// Internal interface used to enable optimizations from .> + internal interface IStateMachineBoxAwareAwaiter { /// Invoked to set of the as the awaiter's continuation. /// The box object. diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs index c45ef24..8aed0fc 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/YieldAwaitable.cs @@ -47,6 +47,9 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter that switches into a target environment. /// This type is intended for compiler use only. public readonly struct YieldAwaiter : ICriticalNotifyCompletion +#if CORECLR + , IStateMachineBoxAwareAwaiter +#endif { /// Gets whether a yield is not required. /// This property is intended for compiler user rather than use directly in code. @@ -115,6 +118,41 @@ namespace System.Runtime.CompilerServices } } +#if CORECLR + void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) + { + Debug.Assert(box != null); + + // If tracing is enabled, delegate the Action-based implementation. + if (TplEtwProvider.Log.IsEnabled()) + { + QueueContinuation(box.MoveNextAction, flowContext: false); + return; + } + + // Otherwise, this is the same logic as in QueueContinuation, except using + // an IAsyncStateMachineBox instead of an Action, and only for flowContext:false. + + SynchronizationContext syncCtx = SynchronizationContext.Current; + if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext)) + { + syncCtx.Post(s => ((IAsyncStateMachineBox)s).MoveNext(), box); + } + else + { + TaskScheduler scheduler = TaskScheduler.Current; + if (scheduler == TaskScheduler.Default) + { + ThreadPool.UnsafeQueueUserWorkItem(s => ((IAsyncStateMachineBox)s).MoveNext(), box); + } + else + { + Task.Factory.StartNew(s => ((IAsyncStateMachineBox)s).MoveNext(), box, default, TaskCreationOptions.PreferFairness, scheduler); + } + } + } +#endif + private static Action OutputCorrelationEtwEvent(Action continuation) { #if CORERT @@ -153,7 +191,6 @@ namespace System.Runtime.CompilerServices private static readonly WaitCallback s_waitCallbackRunAction = RunAction; /// SendOrPostCallback that invokes the Action supplied as object state. private static readonly SendOrPostCallback s_sendOrPostCallbackRunAction = RunAction; - /// Runs an Action delegate provided as state. /// The Action delegate to invoke. private static void RunAction(object state) { ((Action)state)(); } diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs index f23a4aa..f247d10 100644 --- a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs @@ -389,11 +389,11 @@ namespace System.Runtime.CompilerServices ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As(ref awaiter); TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, ta.m_continueOnCapturedContext); } - else if ((null != (object)default(TAwaiter)) && (awaiter is IValueTaskAwaiter)) + else if ((null != (object)default(TAwaiter)) && (awaiter is IStateMachineBoxAwareAwaiter)) { try { - ((IValueTaskAwaiter)awaiter).AwaitUnsafeOnCompleted(box); + ((IStateMachineBoxAwareAwaiter)awaiter).AwaitUnsafeOnCompleted(box); } catch (Exception e) {