From e7ead79fedc52e17f2cf9befd5c0f5091d70f909 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 28 Nov 2018 21:45:16 +0000 Subject: 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 --- .../ConfiguredValueTaskAwaitable.cs | 5 ++-- .../Runtime/CompilerServices/ValueTaskAwaiter.cs | 17 +++--------- .../src/System/Threading/ThreadPool.cs | 30 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 8f7b0c809c..b874af2cd1 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; @@ -111,7 +112,7 @@ namespace System.Runtime.CompilerServices } else if (obj != null) { - Unsafe.As(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, + Unsafe.As(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else @@ -222,7 +223,7 @@ namespace System.Runtime.CompilerServices } else if (obj != null) { - Unsafe.As>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, + Unsafe.As>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 02b5910b77..db14806c63 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; @@ -101,25 +102,13 @@ namespace System.Runtime.CompilerServices } else if (obj != null) { - Unsafe.As(obj).OnCompleted(s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + Unsafe.As(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true); } } - - /// Shim used to invoke of the supplied . - internal static readonly Action s_invokeAsyncStateMachineBox = state => - { - if (!(state is IAsyncStateMachineBox box)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state); - return; - } - - box.MoveNext(); - }; #endif } @@ -201,7 +190,7 @@ namespace System.Runtime.CompilerServices } else if (obj != null) { - Unsafe.As>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + Unsafe.As>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { diff --git a/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index 5026003395..cf44d850a4 100644 --- a/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -37,6 +37,18 @@ namespace System.Threading public static bool enableWorkerTracking; public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue(); + + /// Shim used to invoke of the supplied . + internal static readonly Action s_invokeAsyncStateMachineBox = state => + { + if (!(state is IAsyncStateMachineBox box)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state); + return; + } + + box.MoveNext(); + }; } [StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing @@ -1333,6 +1345,24 @@ namespace System.Threading ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack); } + // If the callback is the runtime-provided invocation of an IAsyncStateMachineBox, + // then we can queue the Task state directly to the ThreadPool instead of + // wrapping it in a QueueUserWorkItemCallback. + // + // This occurs when user code queues its provided continuation to the ThreadPool; + // internally we call UnsafeQueueUserWorkItemInternal directly for Tasks. + if (ReferenceEquals(callBack, ThreadPoolGlobals.s_invokeAsyncStateMachineBox)) + { + if (!(state is IAsyncStateMachineBox)) + { + // The provided state must be the internal IAsyncStateMachineBox (Task) type + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state); + } + + UnsafeQueueUserWorkItemInternal((object)state, preferLocal); + return true; + } + EnsureVMInitialized(); ThreadPoolGlobals.workQueue.Enqueue( -- cgit v1.2.3