From 6417499764b5f6d38409fe012637fde855be1b7f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 19 May 2020 15:06:09 +0100 Subject: [PATCH 1/4] Remove TheadPool initialization volatile --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 16 ++-------------- .../src/System/Threading/ThreadPool.cs | 17 +++++++++++++++-- .../src/System/ThrowHelper.cs | 7 +++++++ .../src/System/Threading/ThreadPool.Mono.cs | 6 +++--- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 9752c3cbde366..da171ada54699 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -302,23 +302,11 @@ bool compressStack public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => PostQueuedCompletionStatus(overlapped); - // The thread pool maintains a per-appdomain managed work queue. - // New thread pool entries are added in the managed queue. - // The VM is responsible for the actual growing/shrinking of - // threads. - private static void EnsureInitialized() - { - if (!ThreadPoolGlobals.threadPoolInitialized) - { - EnsureVMInitializedCore(); // separate out to help with inlining - } - } - [MethodImpl(MethodImplOptions.NoInlining)] - private static void EnsureVMInitializedCore() + internal static bool InitializeThreadPool() { InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking); - ThreadPoolGlobals.threadPoolInitialized = true; + return true; } // Native methods: diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index fe1021272a282..b3d042c995a28 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -24,8 +24,8 @@ namespace System.Threading { internal static class ThreadPoolGlobals { - public static volatile bool threadPoolInitialized; public static bool enableWorkerTracking; + public static bool ThreadPoolInitialized { get; } = ThreadPool.InitializeThreadPool(); public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue(); @@ -1070,6 +1070,19 @@ bool executeOnlyOnce return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)tm, executeOnlyOnce, false); } + // The thread pool maintains a per-appdomain managed work queue. + // New thread pool entries are added in the managed queue. + // The VM is responsible for the actual growing/shrinking of + // threads. + private static void EnsureInitialized() + { + // Inspecting the readonly static ThreadPoolInitialized should initialize the ThreadPool + if (!ThreadPoolGlobals.ThreadPoolInitialized) + { + ThrowHelper.ThrowInvalidOperationException_ThreadPoolNotInitialized(); + } + } + public static bool QueueUserWorkItem(WaitCallback callBack) => QueueUserWorkItem(callBack, null); @@ -1193,7 +1206,7 @@ internal static bool TryPopCustomWorkItem(object workItem) { Debug.Assert(null != workItem); return - ThreadPoolGlobals.threadPoolInitialized && // if not initialized, so there's no way this workitem was ever queued. + ThreadPoolGlobals.ThreadPoolInitialized && // if not initialized, so there's no way this workitem was ever queued. ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem); } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 30a11b61a0f55..ad99279a4a4a7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -416,6 +416,13 @@ internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() throw new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier); } + [DoesNotReturn] + internal static void ThrowInvalidOperationException_ThreadPoolNotInitialized() + { + // Should never occur at runtime. + throw new InvalidOperationException("ThreadPool Not Initialized"); + } + private static Exception GetArraySegmentCtorValidationFailedException(Array? array, int offset, int count) { if (array == null) diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs index 023574d218002..e8377b63b0ba1 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs @@ -8,10 +8,10 @@ namespace System.Threading { public static partial class ThreadPool { - private static void EnsureInitialized() + internal static bool InitializeThreadPool() { - ThreadPoolGlobals.threadPoolInitialized = true; ThreadPoolGlobals.enableWorkerTracking = false; + return true; } internal static void ReportThreadStatus(bool isWorking) @@ -46,4 +46,4 @@ public static bool BindHandle(SafeHandle osHandle) private static long PendingUnmanagedWorkItemCount => 0; } -} \ No newline at end of file +} From e500893d45dce097d74dba9140ac5f003dc0dc40 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 19 May 2020 15:32:48 +0100 Subject: [PATCH 2/4] Better ThreadPoolGlobals setup for Mono --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 7 ++++++- .../src/System/Threading/ThreadPool.cs | 5 +---- .../src/System/Threading/ThreadPool.Mono.cs | 12 ++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index da171ada54699..3449287f54e94 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -18,6 +18,12 @@ namespace System.Threading { + internal static partial class ThreadPoolGlobals + { + public static bool enableWorkerTracking; + public static bool ThreadPoolInitialized { get; } = ThreadPool.InitializeThreadPool(); + } + // // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback // on the stack to determine if a thread is a ThreadPool thread or not. We have a better way to do this for .NET 4.5, but @@ -302,7 +308,6 @@ bool compressStack public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => PostQueuedCompletionStatus(overlapped); - [MethodImpl(MethodImplOptions.NoInlining)] internal static bool InitializeThreadPool() { InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index b3d042c995a28..d762a73ae4d9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -22,11 +22,8 @@ namespace System.Threading { - internal static class ThreadPoolGlobals + internal static partial class ThreadPoolGlobals { - public static bool enableWorkerTracking; - public static bool ThreadPoolInitialized { get; } = ThreadPool.InitializeThreadPool(); - public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue(); /// Shim used to invoke of the supplied . diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs index e8377b63b0ba1..95184c9bbb4a5 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs @@ -6,14 +6,14 @@ namespace System.Threading { - public static partial class ThreadPool + internal static partial class ThreadPoolGlobals { - internal static bool InitializeThreadPool() - { - ThreadPoolGlobals.enableWorkerTracking = false; - return true; - } + public const bool enableWorkerTracking = false; + public static bool ThreadPoolInitialized => true; + } + public static partial class ThreadPool + { internal static void ReportThreadStatus(bool isWorking) { } From 2e2f9e3f95968f17ed80278ac20cf26723604766 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 19 May 2020 16:49:57 +0100 Subject: [PATCH 3/4] Feedback --- .../System/Threading/ThreadPool.CoreCLR.cs | 20 ++-- .../ConfiguredValueTaskAwaitable.cs | 4 +- .../CompilerServices/ValueTaskAwaiter.cs | 4 +- .../src/System/Threading/ThreadPool.cs | 93 ++++++++----------- .../src/System/ThrowHelper.cs | 7 -- .../src/System/Threading/ThreadPool.Mono.cs | 8 +- 6 files changed, 52 insertions(+), 84 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 3449287f54e94..719576c06d033 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -18,12 +18,6 @@ namespace System.Threading { - internal static partial class ThreadPoolGlobals - { - public static bool enableWorkerTracking; - public static bool ThreadPoolInitialized { get; } = ThreadPool.InitializeThreadPool(); - } - // // This type is necessary because VS 2010's debugger looks for a method named _ThreadPoolWaitCallbacck.PerformWaitCallback // on the stack to determine if a thread is a ThreadPool thread or not. We have a better way to do this for .NET 4.5, but @@ -198,6 +192,13 @@ public static partial class ThreadPool // Time in ms for which ThreadPoolWorkQueue.Dispatch keeps executing work items before returning to the OS private const uint DispatchQuantum = 30; + private static bool GetEnableWorkerTracking() + { + bool enableWorkerTracking = false; + InitializeVMTp(ref enableWorkerTracking); + return enableWorkerTracking; + } + internal static bool KeepDispatching(int startTickCount) { // Note: this function may incorrectly return false due to TickCount overflow @@ -308,12 +309,6 @@ bool compressStack public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => PostQueuedCompletionStatus(overlapped); - internal static bool InitializeThreadPool() - { - InitializeVMTp(ref ThreadPoolGlobals.enableWorkerTracking); - return true; - } - // Native methods: [MethodImpl(MethodImplOptions.InternalCall)] @@ -339,7 +334,6 @@ internal static bool InitializeThreadPool() internal static void NotifyWorkItemProgress() { - EnsureInitialized(); NotifyWorkItemProgressNative(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 78d70e7703c64..bf5edf4774c9e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -105,7 +105,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b } else if (obj != null) { - Unsafe.As(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, + Unsafe.As(obj).OnCompleted(ThreadPool.s_invokeAsyncStateMachineBox, box, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else @@ -210,7 +210,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b } else if (obj != null) { - Unsafe.As>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, + Unsafe.As>(obj).OnCompleted(ThreadPool.s_invokeAsyncStateMachineBox, box, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 07f9441518584..ca7a0a0ca742e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -95,7 +95,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b } else if (obj != null) { - Unsafe.As(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + Unsafe.As(obj).OnCompleted(ThreadPool.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { @@ -177,7 +177,7 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b } else if (obj != null) { - Unsafe.As>(obj).OnCompleted(ThreadPoolGlobals.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + Unsafe.As>(obj).OnCompleted(ThreadPool.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index d762a73ae4d9f..b77c33c9f9ece 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -22,23 +22,6 @@ namespace System.Threading { - internal static partial class ThreadPoolGlobals - { - 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 internal sealed class ThreadPoolWorkQueue { @@ -549,7 +532,7 @@ public long LocalCount /// internal static bool Dispatch() { - ThreadPoolWorkQueue outerWorkQueue = ThreadPoolGlobals.workQueue; + ThreadPoolWorkQueue outerWorkQueue = ThreadPool.s_workQueue; // // Save the start time @@ -624,7 +607,7 @@ internal static bool Dispatch() // // Execute the workitem outside of any finally blocks, so that it can be aborted if needed. // - if (ThreadPoolGlobals.enableWorkerTracking) + if (ThreadPool.s_enableWorkerTracking) { bool reportedStatus = false; try @@ -951,6 +934,33 @@ internal static void PerformWaitOrTimerCallback(_ThreadPoolWaitOrTimerCallback h public static partial class ThreadPool { + internal static readonly ThreadPoolWorkQueue s_workQueue = new ThreadPoolWorkQueue(); + internal static readonly bool s_enableWorkerTracking = GetEnableWorkerTracking(); + + /// Shim used to invoke of the supplied . + internal static readonly Action s_invokeAsyncStateMachineBox = new Action(Invoke.Instance.AsyncStateMachineBox); + + private class Invoke + { + // Invoking an instance method as a delegate is faster than a static method (statics need to go via a stub to handle `this`) + public static Invoke Instance { get; } = new Invoke(); + + private Invoke() { } + + // Method used rather than lambda as it shows up more clearly in profiler and stack traces + public void AsyncStateMachineBox(object? state) + { + if (!(state is IAsyncStateMachineBox box)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state); + return; + } + + box.MoveNext(); + } + } + + [CLSCompliant(false)] public static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, @@ -1067,19 +1077,6 @@ bool executeOnlyOnce return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)tm, executeOnlyOnce, false); } - // The thread pool maintains a per-appdomain managed work queue. - // New thread pool entries are added in the managed queue. - // The VM is responsible for the actual growing/shrinking of - // threads. - private static void EnsureInitialized() - { - // Inspecting the readonly static ThreadPoolInitialized should initialize the ThreadPool - if (!ThreadPoolGlobals.ThreadPoolInitialized) - { - ThrowHelper.ThrowInvalidOperationException_ThreadPoolNotInitialized(); - } - } - public static bool QueueUserWorkItem(WaitCallback callBack) => QueueUserWorkItem(callBack, null); @@ -1090,15 +1087,13 @@ public static bool QueueUserWorkItem(WaitCallback callBack, object? state) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack); } - EnsureInitialized(); - ExecutionContext? context = ExecutionContext.Capture(); object tpcallBack = (context == null || context.IsDefault) ? new QueueUserWorkItemCallbackDefaultContext(callBack!, state) : (object)new QueueUserWorkItemCallback(callBack!, state, context); - ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true); + s_workQueue.Enqueue(tpcallBack, forceGlobal: true); return true; } @@ -1110,15 +1105,13 @@ public static bool QueueUserWorkItem(Action callBack, TState sta ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack); } - EnsureInitialized(); - ExecutionContext? context = ExecutionContext.Capture(); object tpcallBack = (context == null || context.IsDefault) ? new QueueUserWorkItemCallbackDefaultContext(callBack!, state) : (object)new QueueUserWorkItemCallback(callBack!, state, context); - ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal); + s_workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal); return true; } @@ -1136,7 +1129,7 @@ public static bool UnsafeQueueUserWorkItem(Action callBack, TSta // // 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 (ReferenceEquals(callBack, ThreadPool.s_invokeAsyncStateMachineBox)) { if (!(state is IAsyncStateMachineBox)) { @@ -1148,9 +1141,7 @@ public static bool UnsafeQueueUserWorkItem(Action callBack, TSta return true; } - EnsureInitialized(); - - ThreadPoolGlobals.workQueue.Enqueue( + s_workQueue.Enqueue( new QueueUserWorkItemCallbackDefaultContext(callBack!, state), forceGlobal: !preferLocal); return true; @@ -1163,11 +1154,9 @@ public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, object? state) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack); } - EnsureInitialized(); - object tpcallBack = new QueueUserWorkItemCallbackDefaultContext(callBack!, state); - ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true); + s_workQueue.Enqueue(tpcallBack, forceGlobal: true); return true; } @@ -1193,25 +1182,21 @@ internal static void UnsafeQueueUserWorkItemInternal(object callBack, bool prefe { Debug.Assert((callBack is IThreadPoolWorkItem) ^ (callBack is Task)); - EnsureInitialized(); - - ThreadPoolGlobals.workQueue.Enqueue(callBack, forceGlobal: !preferLocal); + s_workQueue.Enqueue(callBack, forceGlobal: !preferLocal); } // This method tries to take the target callback out of the current thread's queue. internal static bool TryPopCustomWorkItem(object workItem) { Debug.Assert(null != workItem); - return - ThreadPoolGlobals.ThreadPoolInitialized && // if not initialized, so there's no way this workitem was ever queued. - ThreadPoolGlobals.workQueue.LocalFindAndPop(workItem); + return s_workQueue.LocalFindAndPop(workItem); } // Get all workitems. Called by TaskScheduler in its debugger hooks. internal static IEnumerable GetQueuedWorkItems() { // Enumerate global queue - foreach (object workItem in ThreadPoolGlobals.workQueue.workItems) + foreach (object workItem in s_workQueue.workItems) { yield return workItem; } @@ -1249,7 +1234,7 @@ internal static IEnumerable GetLocallyQueuedWorkItems() } } - internal static IEnumerable GetGloballyQueuedWorkItems() => ThreadPoolGlobals.workQueue.workItems; + internal static IEnumerable GetGloballyQueuedWorkItems() => s_workQueue.workItems; private static object[] ToObjectArray(IEnumerable workitems) { @@ -1295,7 +1280,7 @@ public static long PendingWorkItemCount { get { - ThreadPoolWorkQueue workQueue = ThreadPoolGlobals.workQueue; + ThreadPoolWorkQueue workQueue = s_workQueue; return workQueue.LocalCount + workQueue.GlobalCount + PendingUnmanagedWorkItemCount; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index ad99279a4a4a7..30a11b61a0f55 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -416,13 +416,6 @@ internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() throw new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier); } - [DoesNotReturn] - internal static void ThrowInvalidOperationException_ThreadPoolNotInitialized() - { - // Should never occur at runtime. - throw new InvalidOperationException("ThreadPool Not Initialized"); - } - private static Exception GetArraySegmentCtorValidationFailedException(Array? array, int offset, int count) { if (array == null) diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs index 95184c9bbb4a5..ae757df4df433 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs @@ -6,14 +6,10 @@ namespace System.Threading { - internal static partial class ThreadPoolGlobals - { - public const bool enableWorkerTracking = false; - public static bool ThreadPoolInitialized => true; - } - public static partial class ThreadPool { + private static bool GetEnableWorkerTracking() => false; + internal static void ReportThreadStatus(bool isWorking) { } From 310a2e81371660c51331ec5b605db2dd9b873f7e Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 19 May 2020 17:19:26 +0100 Subject: [PATCH 4/4] Move back to lambda --- .../src/System/Threading/ThreadPool.cs | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs index b77c33c9f9ece..de78cc1a7002f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -938,28 +938,16 @@ public static partial class ThreadPool internal static readonly bool s_enableWorkerTracking = GetEnableWorkerTracking(); /// Shim used to invoke of the supplied . - internal static readonly Action s_invokeAsyncStateMachineBox = new Action(Invoke.Instance.AsyncStateMachineBox); - - private class Invoke + internal static readonly Action s_invokeAsyncStateMachineBox = state => { - // Invoking an instance method as a delegate is faster than a static method (statics need to go via a stub to handle `this`) - public static Invoke Instance { get; } = new Invoke(); - - private Invoke() { } - - // Method used rather than lambda as it shows up more clearly in profiler and stack traces - public void AsyncStateMachineBox(object? state) + if (!(state is IAsyncStateMachineBox box)) { - if (!(state is IAsyncStateMachineBox box)) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state); - return; - } - - box.MoveNext(); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state); + return; } - } + box.MoveNext(); + }; [CLSCompliant(false)] public static RegisteredWaitHandle RegisterWaitForSingleObject(