Skip to content

Commit

Permalink
Add YieldAwaiter support to the async method builder delegate optimiz…
Browse files Browse the repository at this point in the history
…ation (dotnet#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.
  • Loading branch information
stephentoub authored Apr 6, 2018
1 parent 69b8b73 commit 721343d
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public readonly struct ConfiguredValueTaskAwaitable
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IValueTaskAwaiter
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>The value being awaited.</summary>
Expand Down Expand Up @@ -94,7 +94,7 @@ public void UnsafeOnCompleted(Action continuation)
}

#if CORECLR
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
if (_value.ObjectIsTask)
{
Expand Down Expand Up @@ -135,7 +135,7 @@ public readonly struct ConfiguredValueTaskAwaitable<TResult>
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IValueTaskAwaiter
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>The value being awaited.</summary>
Expand Down Expand Up @@ -196,7 +196,7 @@ public void UnsafeOnCompleted(Action continuation)
}

#if CORECLR
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
if (_value.ObjectIsTask)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace System.Runtime.CompilerServices
/// <summary>Provides an awaiter for a <see cref="ValueTask"/>.</summary>
public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IValueTaskAwaiter
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>Shim used to invoke an <see cref="Action"/> passed as the state argument to a <see cref="Action{Object}"/>.</summary>
Expand Down Expand Up @@ -80,7 +80,7 @@ public void UnsafeOnCompleted(Action continuation)
}

#if CORECLR
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
if (_value.ObjectIsTask)
{
Expand Down Expand Up @@ -113,7 +113,7 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
#if CORECLR
, IValueTaskAwaiter
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>The value being awaited.</summary>
Expand Down Expand Up @@ -171,7 +171,7 @@ public void UnsafeOnCompleted(Action continuation)
}

#if CORECLR
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
if (_value.ObjectIsTask)
{
Expand All @@ -190,8 +190,8 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
}

#if CORECLR
/// <summary>Internal interface used to enable optimizations from <see cref="AsyncTaskMethodBuilder"/> on <see cref="ValueTask"/>.</summary>>
internal interface IValueTaskAwaiter
/// <summary>Internal interface used to enable optimizations from <see cref="AsyncTaskMethodBuilder"/>.</summary>>
internal interface IStateMachineBoxAwareAwaiter
{
/// <summary>Invoked to set <see cref="ITaskCompletionAction.Invoke"/> of the <paramref name="box"/> as the awaiter's continuation.</summary>
/// <param name="box">The box object.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public readonly struct YieldAwaitable
/// <summary>Provides an awaiter that switches into a target environment.</summary>
/// <remarks>This type is intended for compiler use only.</remarks>
public readonly struct YieldAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>Gets whether a yield is not required.</summary>
/// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
Expand Down Expand Up @@ -115,6 +118,41 @@ private static void QueueContinuation(Action continuation, bool flowContext)
}
}

#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
Expand Down Expand Up @@ -153,7 +191,6 @@ private static Action OutputCorrelationEtwEvent(Action continuation)
private static readonly WaitCallback s_waitCallbackRunAction = RunAction;
/// <summary>SendOrPostCallback that invokes the Action supplied as object state.</summary>
private static readonly SendOrPostCallback s_sendOrPostCallbackRunAction = RunAction;

/// <summary>Runs an Action delegate provided as state.</summary>
/// <param name="state">The Action delegate to invoke.</param>
private static void RunAction(object state) { ((Action)state)(); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,11 @@ public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(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)
{
Expand Down

0 comments on commit 721343d

Please sign in to comment.