Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 06fb488

Browse files
stephentoubAnipik
authored andcommitted
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. Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>
1 parent 4ef69a4 commit 06fb488

File tree

3 files changed

+48
-11
lines changed

3 files changed

+48
-11
lines changed

src/Common/src/CoreLib/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public readonly struct ConfiguredValueTaskAwaitable
3333
[StructLayout(LayoutKind.Auto)]
3434
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
3535
#if CORECLR
36-
, IValueTaskAwaiter
36+
, IStateMachineBoxAwareAwaiter
3737
#endif
3838
{
3939
/// <summary>The value being awaited.</summary>
@@ -94,7 +94,7 @@ public void UnsafeOnCompleted(Action continuation)
9494
}
9595

9696
#if CORECLR
97-
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
97+
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
9898
{
9999
if (_value.ObjectIsTask)
100100
{
@@ -135,7 +135,7 @@ public readonly struct ConfiguredValueTaskAwaitable<TResult>
135135
[StructLayout(LayoutKind.Auto)]
136136
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
137137
#if CORECLR
138-
, IValueTaskAwaiter
138+
, IStateMachineBoxAwareAwaiter
139139
#endif
140140
{
141141
/// <summary>The value being awaited.</summary>
@@ -196,7 +196,7 @@ public void UnsafeOnCompleted(Action continuation)
196196
}
197197

198198
#if CORECLR
199-
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
199+
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
200200
{
201201
if (_value.ObjectIsTask)
202202
{

src/Common/src/CoreLib/System/Runtime/CompilerServices/ValueTaskAwaiter.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace System.Runtime.CompilerServices
1111
/// <summary>Provides an awaiter for a <see cref="ValueTask"/>.</summary>
1212
public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion
1313
#if CORECLR
14-
, IValueTaskAwaiter
14+
, IStateMachineBoxAwareAwaiter
1515
#endif
1616
{
1717
/// <summary>Shim used to invoke an <see cref="Action"/> passed as the state argument to a <see cref="Action{Object}"/>.</summary>
@@ -80,7 +80,7 @@ public void UnsafeOnCompleted(Action continuation)
8080
}
8181

8282
#if CORECLR
83-
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
83+
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
8484
{
8585
if (_value.ObjectIsTask)
8686
{
@@ -113,7 +113,7 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
113113
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
114114
public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
115115
#if CORECLR
116-
, IValueTaskAwaiter
116+
, IStateMachineBoxAwareAwaiter
117117
#endif
118118
{
119119
/// <summary>The value being awaited.</summary>
@@ -171,7 +171,7 @@ public void UnsafeOnCompleted(Action continuation)
171171
}
172172

173173
#if CORECLR
174-
void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
174+
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
175175
{
176176
if (_value.ObjectIsTask)
177177
{
@@ -190,8 +190,8 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
190190
}
191191

192192
#if CORECLR
193-
/// <summary>Internal interface used to enable optimizations from <see cref="AsyncTaskMethodBuilder"/> on <see cref="ValueTask"/>.</summary>>
194-
internal interface IValueTaskAwaiter
193+
/// <summary>Internal interface used to enable optimizations from <see cref="AsyncTaskMethodBuilder"/>.</summary>>
194+
internal interface IStateMachineBoxAwareAwaiter
195195
{
196196
/// <summary>Invoked to set <see cref="ITaskCompletionAction.Invoke"/> of the <paramref name="box"/> as the awaiter's continuation.</summary>
197197
/// <param name="box">The box object.</param>

src/Common/src/CoreLib/System/Runtime/CompilerServices/YieldAwaitable.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public readonly struct YieldAwaitable
4747
/// <summary>Provides an awaiter that switches into a target environment.</summary>
4848
/// <remarks>This type is intended for compiler use only.</remarks>
4949
public readonly struct YieldAwaiter : ICriticalNotifyCompletion
50+
#if CORECLR
51+
, IStateMachineBoxAwareAwaiter
52+
#endif
5053
{
5154
/// <summary>Gets whether a yield is not required.</summary>
5255
/// <remarks>This property is intended for compiler user rather than use directly in code.</remarks>
@@ -115,6 +118,41 @@ private static void QueueContinuation(Action continuation, bool flowContext)
115118
}
116119
}
117120

121+
#if CORECLR
122+
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
123+
{
124+
Debug.Assert(box != null);
125+
126+
// If tracing is enabled, delegate the Action-based implementation.
127+
if (TplEtwProvider.Log.IsEnabled())
128+
{
129+
QueueContinuation(box.MoveNextAction, flowContext: false);
130+
return;
131+
}
132+
133+
// Otherwise, this is the same logic as in QueueContinuation, except using
134+
// an IAsyncStateMachineBox instead of an Action, and only for flowContext:false.
135+
136+
SynchronizationContext syncCtx = SynchronizationContext.Current;
137+
if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
138+
{
139+
syncCtx.Post(s => ((IAsyncStateMachineBox)s).MoveNext(), box);
140+
}
141+
else
142+
{
143+
TaskScheduler scheduler = TaskScheduler.Current;
144+
if (scheduler == TaskScheduler.Default)
145+
{
146+
ThreadPool.UnsafeQueueUserWorkItem(s => ((IAsyncStateMachineBox)s).MoveNext(), box);
147+
}
148+
else
149+
{
150+
Task.Factory.StartNew(s => ((IAsyncStateMachineBox)s).MoveNext(), box, default, TaskCreationOptions.PreferFairness, scheduler);
151+
}
152+
}
153+
}
154+
#endif
155+
118156
private static Action OutputCorrelationEtwEvent(Action continuation)
119157
{
120158
#if CORERT
@@ -153,7 +191,6 @@ private static Action OutputCorrelationEtwEvent(Action continuation)
153191
private static readonly WaitCallback s_waitCallbackRunAction = RunAction;
154192
/// <summary>SendOrPostCallback that invokes the Action supplied as object state.</summary>
155193
private static readonly SendOrPostCallback s_sendOrPostCallbackRunAction = RunAction;
156-
157194
/// <summary>Runs an Action delegate provided as state.</summary>
158195
/// <param name="state">The Action delegate to invoke.</param>
159196
private static void RunAction(object state) { ((Action)state)(); }

0 commit comments

Comments
 (0)