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

Commit e7ead79

Browse files
benaadamsstephentoub
authored andcommitted
Queue ValueTaskAwaiter IAsyncStateMachineBox directly to ThreadPool (#21159)
* Queue ValueTaskAwaiter IAsyncStateMachineBox directly to ThreadPool * Invert the dependency * Move to UnsafeQueueUserWorkItem * MRVTSC queue null or Deafult EC to UnsafeQUWI * Revert MRVTSC change * Add comment and validation * Use s_invokeAsyncStateMachineBox for AsTask * nits * nits 2 * Rever ValueTask * nits
1 parent 204d2da commit e7ead79

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Diagnostics;
66
using System.Runtime.InteropServices;
7+
using System.Threading;
78
using System.Threading.Tasks;
89
using System.Threading.Tasks.Sources;
910

@@ -111,7 +112,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
111112
}
112113
else if (obj != null)
113114
{
114-
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
115+
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token,
115116
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
116117
}
117118
else
@@ -222,7 +223,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
222223
}
223224
else if (obj != null)
224225
{
225-
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
226+
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token,
226227
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
227228
}
228229
else

src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Diagnostics;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using System.Threading.Tasks.Sources;
89

@@ -101,25 +102,13 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
101102
}
102103
else if (obj != null)
103104
{
104-
Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
105+
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
105106
}
106107
else
107108
{
108109
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true);
109110
}
110111
}
111-
112-
/// <summary>Shim used to invoke <see cref="ITaskCompletionAction.Invoke"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
113-
internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
114-
{
115-
if (!(state is IAsyncStateMachineBox box))
116-
{
117-
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
118-
return;
119-
}
120-
121-
box.MoveNext();
122-
};
123112
#endif
124113
}
125114

@@ -201,7 +190,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
201190
}
202191
else if (obj != null)
203192
{
204-
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
193+
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
205194
}
206195
else
207196
{

src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ internal static class ThreadPoolGlobals
3737
public static bool enableWorkerTracking;
3838

3939
public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
40+
41+
/// <summary>Shim used to invoke <see cref="IAsyncStateMachineBox.MoveNext"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
42+
internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
43+
{
44+
if (!(state is IAsyncStateMachineBox box))
45+
{
46+
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
47+
return;
48+
}
49+
50+
box.MoveNext();
51+
};
4052
}
4153

4254
[StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing
@@ -1333,6 +1345,24 @@ public static bool UnsafeQueueUserWorkItem<TState>(Action<TState> callBack, TSta
13331345
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
13341346
}
13351347

1348+
// If the callback is the runtime-provided invocation of an IAsyncStateMachineBox,
1349+
// then we can queue the Task state directly to the ThreadPool instead of
1350+
// wrapping it in a QueueUserWorkItemCallback.
1351+
//
1352+
// This occurs when user code queues its provided continuation to the ThreadPool;
1353+
// internally we call UnsafeQueueUserWorkItemInternal directly for Tasks.
1354+
if (ReferenceEquals(callBack, ThreadPoolGlobals.s_invokeAsyncStateMachineBox))
1355+
{
1356+
if (!(state is IAsyncStateMachineBox))
1357+
{
1358+
// The provided state must be the internal IAsyncStateMachineBox (Task) type
1359+
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
1360+
}
1361+
1362+
UnsafeQueueUserWorkItemInternal((object)state, preferLocal);
1363+
return true;
1364+
}
1365+
13361366
EnsureVMInitialized();
13371367

13381368
ThreadPoolGlobals.workQueue.Enqueue(

0 commit comments

Comments
 (0)