From 2d5b4c649ffe61ee125bc31bf848cc8a825b7daa Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Mar 2023 10:17:51 -0800 Subject: [PATCH 001/168] Split RegisteredWaitHandle.Portable.cs --- .../RegisteredWaitHandle.Portable.Core.cs | 235 ++++++++++++++++++ .../RegisteredWaitHandle.Portable.cs | 216 +--------------- 2 files changed, 236 insertions(+), 215 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs new file mode 100644 index 0000000000000..50cd9f2bb36a4 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs @@ -0,0 +1,235 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.Versioning; +using Microsoft.Win32.SafeHandles; + +namespace System.Threading +{ + /// + /// An object representing the registration of a via . + /// +#if !FEATURE_WASM_THREADS + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif + public sealed partial class RegisteredWaitHandle : MarshalByRefObject + { + + private static AutoResetEvent RentEvent() => + Interlocked.Exchange(ref s_cachedEvent, null) ?? + new AutoResetEvent(false); + + private static void ReturnEvent(AutoResetEvent resetEvent) + { + if (Interlocked.CompareExchange(ref s_cachedEvent, resetEvent, null) != null) + { + resetEvent.Dispose(); + } + } + + internal void RestartTimeout() + { + Debug.Assert(!IsInfiniteTimeout); + TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs; + } + + private bool UnregisterCore(WaitHandle waitObject) + { + // The registered wait handle must have been registered by this time, otherwise the instance is not handed out to + // the caller of the public variants of RegisterWaitForSingleObject + Debug.Assert(WaitThread != null); + + s_callbackLock.Acquire(); + bool needToRollBackRefCountOnException = false; + try + { + if (_unregisterCalled) + { + return false; + } + + UserUnregisterWaitHandle = waitObject?.SafeWaitHandle; + UserUnregisterWaitHandle?.DangerousAddRef(ref needToRollBackRefCountOnException); + + UserUnregisterWaitHandleValue = UserUnregisterWaitHandle?.DangerousGetHandle() ?? IntPtr.Zero; + + if (_unregistered) + { + SignalUserWaitHandle(); + return true; + } + + if (IsBlocking) + { + _callbacksComplete = RentEvent(); + } + else + { + _removed = RentEvent(); + } + } + catch (Exception) // Rollback state on exception + { + if (_removed != null) + { + ReturnEvent(_removed); + _removed = null; + } + else if (_callbacksComplete != null) + { + ReturnEvent(_callbacksComplete); + _callbacksComplete = null; + } + + UserUnregisterWaitHandleValue = IntPtr.Zero; + + if (needToRollBackRefCountOnException) + { + UserUnregisterWaitHandle?.DangerousRelease(); + } + + UserUnregisterWaitHandle = null; + throw; + } + finally + { + _unregisterCalled = true; + s_callbackLock.Release(); + } + + WaitThread!.UnregisterWait(this); + return true; + } + + /// + /// Signal if it has not been signaled yet and is a valid handle. + /// + private void SignalUserWaitHandle() + { + s_callbackLock.VerifyIsLocked(); + SafeWaitHandle? handle = UserUnregisterWaitHandle; + IntPtr handleValue = UserUnregisterWaitHandleValue; + try + { + if (handleValue != IntPtr.Zero && handleValue != InvalidHandleValue) + { + Debug.Assert(handleValue == handle!.DangerousGetHandle()); + EventWaitHandle.Set(handle); + } + } + finally + { + handle?.DangerousRelease(); + _callbacksComplete?.Set(); + _unregistered = true; + } + } + + /// + /// Perform the registered callback if the has not been signaled. + /// + /// Whether or not the wait timed out. + internal void PerformCallback(bool timedOut) + { +#if DEBUG + s_callbackLock.Acquire(); + try + { + Debug.Assert(_numRequestedCallbacks != 0); + } + finally + { + s_callbackLock.Release(); + } +#endif + + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback, timedOut); + CompleteCallbackRequest(); + } + + /// + /// Tell this handle that there is a callback queued on the thread pool for this handle. + /// + internal void RequestCallback() + { + s_callbackLock.Acquire(); + try + { + _numRequestedCallbacks++; + } + finally + { + s_callbackLock.Release(); + } + } + + /// + /// Called when the wait thread removes this handle registration. This will signal the user's event if there are no callbacks pending, + /// or note that the user's event must be signaled when the callbacks complete. + /// + internal void OnRemoveWait() + { + s_callbackLock.Acquire(); + try + { + _removed?.Set(); + if (_numRequestedCallbacks == 0) + { + SignalUserWaitHandle(); + } + else + { + _signalAfterCallbacksComplete = true; + } + } + finally + { + s_callbackLock.Release(); + } + } + + /// + /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it. + /// + private void CompleteCallbackRequest() + { + s_callbackLock.Acquire(); + try + { + --_numRequestedCallbacks; + if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete) + { + SignalUserWaitHandle(); + } + } + finally + { + s_callbackLock.Release(); + } + } + + /// + /// Wait for all queued callbacks and the full unregistration to complete. + /// + internal void WaitForCallbacks() + { + Debug.Assert(IsBlocking); + Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. + + _callbacksComplete!.WaitOne(); + ReturnEvent(_callbacksComplete); + _callbacksComplete = null; + } + + internal void WaitForRemoval() + { + Debug.Assert(!IsBlocking); + Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. + + _removed!.WaitOne(); + ReturnEvent(_removed); + _removed = null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index e216cbb78d55c..eb2ac734e6905 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -31,18 +31,6 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb private static AutoResetEvent? s_cachedEvent; - private static AutoResetEvent RentEvent() => - Interlocked.Exchange(ref s_cachedEvent, null) ?? - new AutoResetEvent(false); - - private static void ReturnEvent(AutoResetEvent resetEvent) - { - if (Interlocked.CompareExchange(ref s_cachedEvent, resetEvent, null) != null) - { - resetEvent.Dispose(); - } - } - private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); /// @@ -64,12 +52,6 @@ private static void ReturnEvent(AutoResetEvent resetEvent) internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; - internal void RestartTimeout() - { - Debug.Assert(!IsInfiniteTimeout); - TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs; - } - /// /// Whether or not the wait is a repeating wait. /// @@ -109,202 +91,6 @@ internal void RestartTimeout() /// internal PortableThreadPool.WaitThread? WaitThread { get; set; } - public bool Unregister(WaitHandle waitObject) - { - // The registered wait handle must have been registered by this time, otherwise the instance is not handed out to - // the caller of the public variants of RegisterWaitForSingleObject - Debug.Assert(WaitThread != null); - - s_callbackLock.Acquire(); - bool needToRollBackRefCountOnException = false; - try - { - if (_unregisterCalled) - { - return false; - } - - UserUnregisterWaitHandle = waitObject?.SafeWaitHandle; - UserUnregisterWaitHandle?.DangerousAddRef(ref needToRollBackRefCountOnException); - - UserUnregisterWaitHandleValue = UserUnregisterWaitHandle?.DangerousGetHandle() ?? IntPtr.Zero; - - if (_unregistered) - { - SignalUserWaitHandle(); - return true; - } - - if (IsBlocking) - { - _callbacksComplete = RentEvent(); - } - else - { - _removed = RentEvent(); - } - } - catch (Exception) // Rollback state on exception - { - if (_removed != null) - { - ReturnEvent(_removed); - _removed = null; - } - else if (_callbacksComplete != null) - { - ReturnEvent(_callbacksComplete); - _callbacksComplete = null; - } - - UserUnregisterWaitHandleValue = IntPtr.Zero; - - if (needToRollBackRefCountOnException) - { - UserUnregisterWaitHandle?.DangerousRelease(); - } - - UserUnregisterWaitHandle = null; - throw; - } - finally - { - _unregisterCalled = true; - s_callbackLock.Release(); - } - - WaitThread!.UnregisterWait(this); - return true; - } - - /// - /// Signal if it has not been signaled yet and is a valid handle. - /// - private void SignalUserWaitHandle() - { - s_callbackLock.VerifyIsLocked(); - SafeWaitHandle? handle = UserUnregisterWaitHandle; - IntPtr handleValue = UserUnregisterWaitHandleValue; - try - { - if (handleValue != IntPtr.Zero && handleValue != InvalidHandleValue) - { - Debug.Assert(handleValue == handle!.DangerousGetHandle()); - EventWaitHandle.Set(handle); - } - } - finally - { - handle?.DangerousRelease(); - _callbacksComplete?.Set(); - _unregistered = true; - } - } - - /// - /// Perform the registered callback if the has not been signaled. - /// - /// Whether or not the wait timed out. - internal void PerformCallback(bool timedOut) - { -#if DEBUG - s_callbackLock.Acquire(); - try - { - Debug.Assert(_numRequestedCallbacks != 0); - } - finally - { - s_callbackLock.Release(); - } -#endif - - _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback, timedOut); - CompleteCallbackRequest(); - } - - /// - /// Tell this handle that there is a callback queued on the thread pool for this handle. - /// - internal void RequestCallback() - { - s_callbackLock.Acquire(); - try - { - _numRequestedCallbacks++; - } - finally - { - s_callbackLock.Release(); - } - } - - /// - /// Called when the wait thread removes this handle registration. This will signal the user's event if there are no callbacks pending, - /// or note that the user's event must be signaled when the callbacks complete. - /// - internal void OnRemoveWait() - { - s_callbackLock.Acquire(); - try - { - _removed?.Set(); - if (_numRequestedCallbacks == 0) - { - SignalUserWaitHandle(); - } - else - { - _signalAfterCallbacksComplete = true; - } - } - finally - { - s_callbackLock.Release(); - } - } - - /// - /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it. - /// - private void CompleteCallbackRequest() - { - s_callbackLock.Acquire(); - try - { - --_numRequestedCallbacks; - if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete) - { - SignalUserWaitHandle(); - } - } - finally - { - s_callbackLock.Release(); - } - } - - /// - /// Wait for all queued callbacks and the full unregistration to complete. - /// - internal void WaitForCallbacks() - { - Debug.Assert(IsBlocking); - Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. - - _callbacksComplete!.WaitOne(); - ReturnEvent(_callbacksComplete); - _callbacksComplete = null; - } - - internal void WaitForRemoval() - { - Debug.Assert(!IsBlocking); - Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. - - _removed!.WaitOne(); - ReturnEvent(_removed); - _removed = null; - } + public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); } } From 5860353a19bd7913cd03d41048c0b82ce4b24d2d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Mar 2023 11:12:50 -0800 Subject: [PATCH 002/168] Split ThreadPoolBoundHandle.cs --- .../ThreadPoolBoundHandle.Portable.Core.cs | 126 ++++++++++++++++++ ...e.cs => ThreadPoolBoundHandle.Portable.cs} | 89 +------------ 2 files changed, 132 insertions(+), 83 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs rename src/libraries/System.Private.CoreLib/src/System/Threading/{ThreadPoolBoundHandle.cs => ThreadPoolBoundHandle.Portable.cs} (80%) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs new file mode 100644 index 0000000000000..e8cac62cdd2d6 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + // + // Implementation of ThreadPoolBoundHandle that sits on top of the CLR's ThreadPool and Overlapped infrastructure + // + + /// + /// Represents an I/O handle that is bound to the system thread pool and enables low-level + /// components to receive notifications for asynchronous I/O operations. + /// + public sealed partial class ThreadPoolBoundHandle : IDisposable + { + + private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + return BindHandleCore(handle); + } + + [CLSCompliant(false)] + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + + + [CLSCompliant(false)] + private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); + + private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(callback); + ObjectDisposedException.ThrowIf(_isDisposed, this); + + ThreadPoolBoundHandleOverlapped overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, preAllocated: null, flowExecutionContext); + overlapped._boundHandle = this; + return overlapped._nativeOverlapped; + } + + [CLSCompliant(false)] + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) + { + ArgumentNullException.ThrowIfNull(preAllocated); + ObjectDisposedException.ThrowIf(_isDisposed, this); + + preAllocated.AddRef(); + try + { + ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped; + + if (overlapped._boundHandle != null) + throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); + + overlapped._boundHandle = this; + + return overlapped._nativeOverlapped; + } + catch + { + preAllocated.Release(); + throw; + } + } + + [CLSCompliant(false)] + private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) + { + ArgumentNullException.ThrowIfNull(overlapped); + + // Note: we explicitly allow FreeNativeOverlapped calls after the ThreadPoolBoundHandle has been Disposed. + + ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped); + + if (wrapper._boundHandle != this) + throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); + + if (wrapper._preAllocated != null) + wrapper._preAllocated.Release(); + else + Overlapped.Free(overlapped); + } + + [CLSCompliant(false)] + private static unsafe object? GetNativeOverlappedStateCore(NativeOverlapped* overlapped) + { + ArgumentNullException.ThrowIfNull(overlapped); + + ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped); + Debug.Assert(wrapper._boundHandle != null); + return wrapper._userState; + } + + private static unsafe ThreadPoolBoundHandleOverlapped GetOverlappedWrapper(NativeOverlapped* overlapped) + { + ThreadPoolBoundHandleOverlapped wrapper; + try + { + wrapper = (ThreadPoolBoundHandleOverlapped)Overlapped.Unpack(overlapped); + } + catch (NullReferenceException ex) + { + throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped), ex); + } + + return wrapper; + } + + public void Dispose() + { + // .NET Native's version of ThreadPoolBoundHandle that wraps the Win32 ThreadPool holds onto + // native resources so it needs to be disposable. To match the contract, we are also disposable. + // We also implement a disposable state to mimic behavior between this implementation and + // .NET Native's version (code written against us, will also work against .NET Native's version). + _isDisposed = true; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs similarity index 80% rename from src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs index e4ec1fb941495..b264547f28d31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs @@ -71,15 +71,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// does not take ownership of , /// it remains the responsibility of the caller to call . /// - public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) - { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - return BindHandleCore(handle); - } + public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandleCore(handle); /// /// Returns an unmanaged pointer to a structure, specifying @@ -124,7 +116,7 @@ public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) /// [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + AllocateNativeOverlappedCore(callback, state, pinData); /// /// Returns an unmanaged pointer to a structure, specifying @@ -172,17 +164,7 @@ public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) /// [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); - - private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - ObjectDisposedException.ThrowIf(_isDisposed, this); - - ThreadPoolBoundHandleOverlapped overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, preAllocated: null, flowExecutionContext); - overlapped._boundHandle = this; - return overlapped._nativeOverlapped; - } + UnsafeAllocateNativeOverlappedCore(callback, state, pinData); /// /// Returns an unmanaged pointer to a structure, using the callback, @@ -213,29 +195,7 @@ public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) /// /// [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) - { - ArgumentNullException.ThrowIfNull(preAllocated); - ObjectDisposedException.ThrowIf(_isDisposed, this); - - preAllocated.AddRef(); - try - { - ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped; - - if (overlapped._boundHandle != null) - throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); - - overlapped._boundHandle = this; - - return overlapped._nativeOverlapped; - } - catch - { - preAllocated.Release(); - throw; - } - } + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => AllocateNativeOverlappedCore(preAllocated); /// /// Frees the unmanaged memory associated with a structure @@ -261,22 +221,7 @@ public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) /// This method was called after the was disposed. /// [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) - { - ArgumentNullException.ThrowIfNull(overlapped); - - // Note: we explicitly allow FreeNativeOverlapped calls after the ThreadPoolBoundHandle has been Disposed. - - ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped); - - if (wrapper._boundHandle != this) - throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); - - if (wrapper._preAllocated != null) - wrapper._preAllocated.Release(); - else - Overlapped.Free(overlapped); - } + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedCore(overlapped); /// /// Returns the user-provided object specified when the instance was @@ -295,29 +240,7 @@ public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) /// is . /// [CLSCompliant(false)] - public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) - { - ArgumentNullException.ThrowIfNull(overlapped); - - ThreadPoolBoundHandleOverlapped wrapper = GetOverlappedWrapper(overlapped); - Debug.Assert(wrapper._boundHandle != null); - return wrapper._userState; - } - - private static unsafe ThreadPoolBoundHandleOverlapped GetOverlappedWrapper(NativeOverlapped* overlapped) - { - ThreadPoolBoundHandleOverlapped wrapper; - try - { - wrapper = (ThreadPoolBoundHandleOverlapped)Overlapped.Unpack(overlapped); - } - catch (NullReferenceException ex) - { - throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped), ex); - } - - return wrapper; - } + public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); public void Dispose() { From c0283b6db5068eafbedfc8200bffaaba6b59b605 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Mar 2023 15:25:13 -0800 Subject: [PATCH 003/168] Create WindowsThreadPool.cs and update ThreadPool files --- .../Threading/ThreadPool.CoreCLR.Windows.cs | 4 + .../System/Threading/ThreadPool.CoreCLR.cs | 9 +- .../src/System/Threading/WindowsThreadPool.cs | 225 ++++++++++++++++++ .../System/Threading/ThreadPool.Windows.cs | 193 ++------------- 4 files changed, 260 insertions(+), 171 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs index 279abdf1baa20..29432a2b52bfb 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs @@ -9,6 +9,10 @@ namespace System.Threading { public static partial class ThreadPool { + private static readonly bool UseWindowsThreadPool = + Environment.GetEnvironmentVariable("DOTNET_ThreadPool_UseWindowsThreadPool") == "1" || + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", false); + [CLSCompliant(false)] [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 031ff0cda2c80..dfce7fb88b34e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -180,7 +180,14 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( internal static void RequestWorkerThread() { - PortableThreadPool.ThreadPoolInstance.RequestWorker(); + if (UseWindowsThreadPool) + { + WindowsThreadPool.RequestWorkerThread(); + } + else + { + PortableThreadPool.ThreadPoolInstance.RequestWorker(); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs new file mode 100644 index 0000000000000..716e2063ba960 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -0,0 +1,225 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + // + // Windows-specific implementation of ThreadPool + // + public static partial class WindowsThreadPool + { + internal const bool IsWorkerTrackingEnabledInConfig = false; + + // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that + // the runtime may use the thread for processing other work. + // + // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be + // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread + // injection. + internal static bool YieldFromDispatchLoop => true; + + /// + /// The maximum number of threads in the default thread pool on Windows 10 as computed by + /// TppComputeDefaultMaxThreads(TppMaxGlobalPool). + /// + /// + /// Note that Windows 8 and 8.1 used a different value: Math.Max(4 * Environment.ProcessorCount, 512). + /// + private static readonly int MaxThreadCount = Math.Max(8 * Environment.ProcessorCount, 768); + + private static IntPtr s_work; + + private class ThreadCountHolder + { + internal ThreadCountHolder() => Interlocked.Increment(ref s_threadCount); + ~ThreadCountHolder() => Interlocked.Decrement(ref s_threadCount); + } + + [ThreadStatic] + private static ThreadCountHolder t_threadCountHolder; + private static int s_threadCount; + + [StructLayout(LayoutKind.Sequential)] + private struct WorkingThreadCounter + { + private readonly Internal.PaddingFor32 pad1; + + public volatile int Count; + + private readonly Internal.PaddingFor32 pad2; + } + + // The number of threads executing work items in the Dispatch method + private static WorkingThreadCounter s_workingThreadCounter; + + private static readonly ThreadInt64PersistentCounter s_completedWorkItemCounter = new ThreadInt64PersistentCounter(); + + [ThreadStatic] + private static object? t_completionCountObject; + + internal static void InitializeForThreadPoolThread() => t_threadCountHolder = new ThreadCountHolder(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void IncrementCompletedWorkItemCount() => ThreadInt64PersistentCounter.Increment(GetOrCreateThreadLocalCompletionCountObject()); + + internal static object GetOrCreateThreadLocalCompletionCountObject() => + t_completionCountObject ?? CreateThreadLocalCompletionCountObject(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static object CreateThreadLocalCompletionCountObject() + { + Debug.Assert(t_completionCountObject == null); + + object threadLocalCompletionCountObject = s_completedWorkItemCounter.CreateThreadLocalCountObject(); + t_completionCountObject = threadLocalCompletionCountObject; + return threadLocalCompletionCountObject; + } + + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) + { + // Not supported at present + return false; + } + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) + { + // Note that worker threads and completion port threads share the same thread pool. + // The total number of threads cannot exceed MaxThreadCount. + workerThreads = MaxThreadCount; + completionPortThreads = MaxThreadCount; + } + + public static bool SetMinThreads(int workerThreads, int completionPortThreads) + { + // Not supported at present + return false; + } + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) + { + workerThreads = 0; + completionPortThreads = 0; + } + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) + { + // Make sure we return a non-negative value if thread pool defaults are changed + int availableThreads = Math.Max(MaxThreadCount - s_workingThreadCounter.Count, 0); + + workerThreads = availableThreads; + completionPortThreads = availableThreads; + } + + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount => s_threadCount; + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount => s_completedWorkItemCounter.Count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void NotifyWorkItemProgress() => IncrementCompletedWorkItemCount(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int _ /*currentTimeMs*/) + { + ThreadInt64PersistentCounter.Increment(threadLocalCompletionCountObject); + return true; + } + + internal static bool NotifyThreadBlocked() { return false; } + internal static void NotifyThreadUnblocked() { } + + [UnmanagedCallersOnly] + private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + Debug.Assert(s_work == work); + Interlocked.Increment(ref s_workingThreadCounter.Count); + ThreadPoolWorkQueue.Dispatch(); + Interlocked.Decrement(ref s_workingThreadCounter.Count); + // We reset the thread after executing each callback + wrapper.Exit(resetThread: false); + } + + internal static unsafe void RequestWorkerThread() + { + if (s_work == IntPtr.Zero) + { + IntPtr work = Interop.Kernel32.CreateThreadpoolWork(&DispatchCallback, IntPtr.Zero, IntPtr.Zero); + if (work == IntPtr.Zero) + throw new OutOfMemoryException(); + + if (Interlocked.CompareExchange(ref s_work, work, IntPtr.Zero) != IntPtr.Zero) + Interop.Kernel32.CloseThreadpoolWork(work); + } + + Interop.Kernel32.SubmitThreadpoolWork(s_work); + } + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(waitObject); + ArgumentNullException.ThrowIfNull(callBack); + + var callbackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext); + var registeredWaitHandle = new RegisteredWaitHandle(waitObject.SafeWaitHandle, callbackHelper, millisecondsTimeOutInterval, !executeOnlyOnce); + + registeredWaitHandle.RestartWait(); + return registeredWaitHandle; + } + + private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => + IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr); + + [CLSCompliant(false)] + [SupportedOSPlatform("windows")] + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) + { + if (overlapped == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); + } + + // OS doesn't signal handle, so do it here + overlapped->InternalLow = (IntPtr)0; + // Both types of callbacks are executed on the same thread pool + return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false); + } + + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] + [SupportedOSPlatform("windows")] + public static bool BindHandle(IntPtr osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + + [SupportedOSPlatform("windows")] + public static bool BindHandle(SafeHandle osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 20011637e77e6..c74d1130eb515 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -34,6 +34,10 @@ public sealed class RegisteredWaitHandle : MarshalByRefObject internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, uint millisecondsTimeout, bool repeating) { + if (!ThreadPool.UseWindowsThreadPool) + { + GC.SuppressFinalize(this); + } _lock = new Lock(); // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) @@ -197,6 +201,7 @@ private void FinishUnregisteringAsync(object? waitObject) ~RegisteredWaitHandle() { + Debug.Assert(ThreadPool.UseWindowsThreadPool); // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC Debug.Assert(!_gcHandle.IsAllocated); @@ -235,107 +240,22 @@ private void FinishUnregisteringAsync(object? waitObject) public static partial class ThreadPool { - internal const bool IsWorkerTrackingEnabledInConfig = false; - - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work. - // - // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be - // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread - // injection. - internal static bool YieldFromDispatchLoop => true; - - /// - /// The maximum number of threads in the default thread pool on Windows 10 as computed by - /// TppComputeDefaultMaxThreads(TppMaxGlobalPool). - /// - /// - /// Note that Windows 8 and 8.1 used a different value: Math.Max(4 * Environment.ProcessorCount, 512). - /// - private static readonly int MaxThreadCount = Math.Max(8 * Environment.ProcessorCount, 768); - - private static IntPtr s_work; - - private class ThreadCountHolder - { - internal ThreadCountHolder() => Interlocked.Increment(ref s_threadCount); - ~ThreadCountHolder() => Interlocked.Decrement(ref s_threadCount); - } - - [ThreadStatic] - private static ThreadCountHolder t_threadCountHolder; - private static int s_threadCount; - - [StructLayout(LayoutKind.Sequential)] - private struct WorkingThreadCounter - { - private readonly Internal.PaddingFor32 pad1; - - public volatile int Count; - - private readonly Internal.PaddingFor32 pad2; - } - - // The number of threads executing work items in the Dispatch method - private static WorkingThreadCounter s_workingThreadCounter; - - private static readonly ThreadInt64PersistentCounter s_completedWorkItemCounter = new ThreadInt64PersistentCounter(); - - [ThreadStatic] - private static object? t_completionCountObject; - - internal static void InitializeForThreadPoolThread() => t_threadCountHolder = new ThreadCountHolder(); + internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void IncrementCompletedWorkItemCount() => ThreadInt64PersistentCounter.Increment(GetOrCreateThreadLocalCompletionCountObject()); + internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); - internal static object GetOrCreateThreadLocalCompletionCountObject() => - t_completionCountObject ?? CreateThreadLocalCompletionCountObject(); + internal static object GetOrCreateThreadLocalCompletionCountObject() => WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject(); - [MethodImpl(MethodImplOptions.NoInlining)] - private static object CreateThreadLocalCompletionCountObject() - { - Debug.Assert(t_completionCountObject == null); + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads); - object threadLocalCompletionCountObject = s_completedWorkItemCounter.CreateThreadLocalCountObject(); - t_completionCountObject = threadLocalCompletionCountObject; - return threadLocalCompletionCountObject; - } + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMaxThreads(workerThreads, completionPortThreads); - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) - { - // Not supported at present - return false; - } + public static bool SetMinThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads); - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) - { - // Note that worker threads and completion port threads share the same thread pool. - // The total number of threads cannot exceed MaxThreadCount. - workerThreads = MaxThreadCount; - completionPortThreads = MaxThreadCount; - } + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMinThreads(workerThreads, completionPortThreads); - public static bool SetMinThreads(int workerThreads, int completionPortThreads) - { - // Not supported at present - return false; - } - - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) - { - workerThreads = 0; - completionPortThreads = 0; - } - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) - { - // Make sure we return a non-negative value if thread pool defaults are changed - int availableThreads = Math.Max(MaxThreadCount - s_workingThreadCounter.Count, 0); - - workerThreads = availableThreads; - completionPortThreads = availableThreads; - } + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(workerThreads, completionPortThreads); /// /// Gets the number of thread pool threads that currently exist. @@ -343,7 +263,7 @@ public static void GetAvailableThreads(out int workerThreads, out int completion /// /// For a thread pool implementation that may have different types of threads, the count includes all types. /// - public static int ThreadCount => s_threadCount; + public static int ThreadCount => WindowsThreadPool.ThreadCount; /// /// Gets the number of work items that have been processed so far. @@ -351,95 +271,28 @@ public static void GetAvailableThreads(out int workerThreads, out int completion /// /// For a thread pool implementation that may have different types of work items, the count includes all types. /// - public static long CompletedWorkItemCount => s_completedWorkItemCounter.Count; + public static long CompletedWorkItemCount => WindowsThreadPool.CompletedWorkItemCount; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void NotifyWorkItemProgress() => IncrementCompletedWorkItemCount(); + internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int _ /*currentTimeMs*/) - { - ThreadInt64PersistentCounter.Increment(threadLocalCompletionCountObject); - return true; - } + internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int _ /*currentTimeMs*/) => WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, _); - internal static bool NotifyThreadBlocked() { return false; } - internal static void NotifyThreadUnblocked() { } + internal static bool NotifyThreadBlocked() => WindowsThreadPool.NotifyThreadBlocked(); + internal static void NotifyThreadUnblocked() => WindowsThreadPool.NotifyThreadUnblocked(); - [UnmanagedCallersOnly] - private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) - { - var wrapper = ThreadPoolCallbackWrapper.Enter(); - Debug.Assert(s_work == work); - Interlocked.Increment(ref s_workingThreadCounter.Count); - ThreadPoolWorkQueue.Dispatch(); - Interlocked.Decrement(ref s_workingThreadCounter.Count); - // We reset the thread after executing each callback - wrapper.Exit(resetThread: false); - } - - internal static unsafe void RequestWorkerThread() - { - if (s_work == IntPtr.Zero) - { - IntPtr work = Interop.Kernel32.CreateThreadpoolWork(&DispatchCallback, IntPtr.Zero, IntPtr.Zero); - if (work == IntPtr.Zero) - throw new OutOfMemoryException(); - - if (Interlocked.CompareExchange(ref s_work, work, IntPtr.Zero) != IntPtr.Zero) - Interop.Kernel32.CloseThreadpoolWork(work); - } - - Interop.Kernel32.SubmitThreadpoolWork(s_work); - } - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - object state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(waitObject); - ArgumentNullException.ThrowIfNull(callBack); - - var callbackHelper = new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext); - var registeredWaitHandle = new RegisteredWaitHandle(waitObject.SafeWaitHandle, callbackHelper, millisecondsTimeOutInterval, !executeOnlyOnce); - - registeredWaitHandle.RestartWait(); - return registeredWaitHandle; - } - - private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => - IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr); + internal static unsafe void RequestWorkerThread() => WindowsThreadPool.RequestWorkerThread(); [CLSCompliant(false)] [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) - { - if (overlapped == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); - } - - // OS doesn't signal handle, so do it here - overlapped->InternalLow = (IntPtr)0; - // Both types of callbacks are executed on the same thread pool - return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false); - } + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) - { - throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle - } + public static bool BindHandle(IntPtr osHandle) => WindowsThreadPool.BindHandle(osHandle); [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) - { - throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle - } + public static bool BindHandle(SafeHandle osHandle) => WindowsThreadPool.BindHandle(osHandle); } } From 33083eeb8daba1cfb4ff6300290bb2d1b362cf22 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 16:46:53 -0700 Subject: [PATCH 004/168] Move Win32ThreadPoolBoundHandle.cs implementation to ThreadPoolBoundHandle.WindowsThreadPool.cs --- .../Threading/Win32ThreadPoolBoundHandle.cs | 154 +-------------- ...ThreadPoolBoundHandle.WindowsThreadPool.cs | 176 ++++++++++++++++++ 2 files changed, 184 insertions(+), 146 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs index f6e56c38081fb..00f851c03c6cc 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs @@ -13,7 +13,7 @@ namespace System.Threading // // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool // - public sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + public partial sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable { private readonly SafeHandle _handle; private readonly SafeThreadPoolIOHandle _threadPoolHandle; @@ -30,163 +30,25 @@ public SafeHandle Handle get { return _handle; } } - public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) - { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - SafeThreadPoolIOHandle threadPoolHandle = Interop.Kernel32.CreateThreadpoolIo(handle, &OnNativeIOCompleted, IntPtr.Zero, IntPtr.Zero); - if (threadPoolHandle.IsInvalid) - { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) // Bad handle - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) // Handle already bound or sync handle - throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); - - throw Win32Marshal.GetExceptionForWin32Error(errorCode); - } - - return new ThreadPoolBoundHandle(handle, threadPoolHandle); - } + public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandleCore(handle); [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + AllocateNativeOverlappedCore(callback, state, pinData); [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); - - private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - - AddRef(); - try - { - Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext); - overlapped->Data._boundHandle = this; - - Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); - - return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped); - } - catch - { - Release(); - throw; - } - } + UnsafeAllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false); [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) - { - ArgumentNullException.ThrowIfNull(preAllocated); - - bool addedRefToThis = false; - bool addedRefToPreAllocated = false; - try - { - addedRefToThis = AddRef(); - addedRefToPreAllocated = preAllocated.AddRef(); - - Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped->Data; - if (data._boundHandle != null) - throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); - - data._boundHandle = this; - - Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); - - return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped); - } - catch - { - if (addedRefToPreAllocated) - preAllocated.Release(); - if (addedRefToThis) - Release(); - throw; - } - } + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => + AllocateNativeOverlappedCore(preAllocated); [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) - { - ArgumentNullException.ThrowIfNull(overlapped); - - Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); - Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, this); - - if (!data._completed) - { - Interop.Kernel32.CancelThreadpoolIo(_threadPoolHandle); - Release(); - } - - data._boundHandle = null; - data._completed = false; - - if (data._preAllocated != null) - data._preAllocated.Release(); - else - Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); - } + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedCore(overlapped); [CLSCompliant(false)] - public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) - { - ArgumentNullException.ThrowIfNull(overlapped); - - Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); - Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, null); - - return data._state; - } - - private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle) - { - Win32ThreadPoolNativeOverlapped.OverlappedData data = overlapped->Data; - - if (data._boundHandle == null) - throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped)); - - if (expectedBoundHandle != null && data._boundHandle != expectedBoundHandle) - throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); - - return data; - } - - [UnmanagedCallersOnly] - private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, nuint numberOfBytesTransferred, IntPtr ioPtr) - { - var wrapper = ThreadPoolCallbackWrapper.Enter(); - Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; - - ThreadPoolBoundHandle boundHandle = overlapped->Data._boundHandle; - if (boundHandle == null) - throw new InvalidOperationException(SR.Argument_NativeOverlappedAlreadyFree); - - boundHandle.Release(); - - Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); - ThreadPool.IncrementCompletedWorkItemCount(); - wrapper.Exit(); - } - - private bool AddRef() - { - return _lifetime.AddRef(); - } - - private void Release() - { - _lifetime.Release(this); - } + public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); public void Dispose() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs new file mode 100644 index 0000000000000..b23a983c4ba99 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -0,0 +1,176 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; + +namespace System.Threading +{ + // + // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool + // + public partial sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + { + private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + SafeThreadPoolIOHandle threadPoolHandle = Interop.Kernel32.CreateThreadpoolIo(handle, &OnNativeIOCompleted, IntPtr.Zero, IntPtr.Zero); + if (threadPoolHandle.IsInvalid) + { + int errorCode = Marshal.GetLastWin32Error(); + if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) // Bad handle + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) // Handle already bound or sync handle + throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); + + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + + return new ThreadPoolBoundHandle(handle, threadPoolHandle); + } + + [CLSCompliant(false)] + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + + [CLSCompliant(false)] + private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); + + private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData, bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(callback); + + AddRef(); + try + { + Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext); + overlapped->Data._boundHandle = this; + + Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); + + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped); + } + catch + { + Release(); + throw; + } + } + + [CLSCompliant(false)] + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) + { + ArgumentNullException.ThrowIfNull(preAllocated); + + bool addedRefToThis = false; + bool addedRefToPreAllocated = false; + try + { + addedRefToThis = AddRef(); + addedRefToPreAllocated = preAllocated.AddRef(); + + Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped->Data; + if (data._boundHandle != null) + throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); + + data._boundHandle = this; + + Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); + + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped); + } + catch + { + if (addedRefToPreAllocated) + preAllocated.Release(); + if (addedRefToThis) + Release(); + throw; + } + } + + [CLSCompliant(false)] + private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) + { + ArgumentNullException.ThrowIfNull(overlapped); + + Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); + Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, this); + + if (!data._completed) + { + Interop.Kernel32.CancelThreadpoolIo(_threadPoolHandle); + Release(); + } + + data._boundHandle = null; + data._completed = false; + + if (data._preAllocated != null) + data._preAllocated.Release(); + else + Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); + } + + [CLSCompliant(false)] + private static unsafe object GetNativeOverlappedStateCore(NativeOverlapped* overlapped) + { + ArgumentNullException.ThrowIfNull(overlapped); + + Win32ThreadPoolNativeOverlapped* threadPoolOverlapped = Win32ThreadPoolNativeOverlapped.FromNativeOverlapped(overlapped); + Win32ThreadPoolNativeOverlapped.OverlappedData data = GetOverlappedData(threadPoolOverlapped, null); + + return data._state; + } + + private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle) + { + Win32ThreadPoolNativeOverlapped.OverlappedData data = overlapped->Data; + + if (data._boundHandle == null) + throw new ArgumentException(SR.Argument_NativeOverlappedAlreadyFree, nameof(overlapped)); + + if (expectedBoundHandle != null && data._boundHandle != expectedBoundHandle) + throw new ArgumentException(SR.Argument_NativeOverlappedWrongBoundHandle, nameof(overlapped)); + + return data; + } + + [UnmanagedCallersOnly] + private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, nuint numberOfBytesTransferred, IntPtr ioPtr) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; + + ThreadPoolBoundHandle boundHandle = overlapped->Data._boundHandle; + if (boundHandle == null) + throw new InvalidOperationException(SR.Argument_NativeOverlappedAlreadyFree); + + boundHandle.Release(); + + Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); + ThreadPool.IncrementCompletedWorkItemCount(); + wrapper.Exit(); + } + + private bool AddRef() + { + return _lifetime.AddRef(); + } + + private void Release() + { + _lifetime.Release(this); + } + } +} From c7925c9d607a4257c4bad3594680af2ffb583e8e Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 17:17:59 -0700 Subject: [PATCH 005/168] Move Win32ThreadPoolNativeOverlapped.cs implementation to ThreadPoolNativeOverlapped.WindowsThreadPool.cs --- .../Win32ThreadPoolNativeOverlapped.cs | 207 +---------------- ...dPoolNativeOverlapped.WindowsThreadPool.cs | 219 ++++++++++++++++++ 2 files changed, 226 insertions(+), 200 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs index 26f453b499f2f..e306bb6a20106 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs @@ -28,210 +28,17 @@ internal OverlappedData Data get { return s_dataArray[_dataIndex]; } } - internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) - { - Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew(); - try - { - overlapped->SetData(callback, state, pinData, preAllocated, flowExecutionControl); - } - catch - { - Free(overlapped); - throw; - } - return overlapped; - } - - private static unsafe Win32ThreadPoolNativeOverlapped* AllocateNew() - { - IntPtr freePtr; - Win32ThreadPoolNativeOverlapped* overlapped; - OverlappedData data; - - // Find a free Overlapped - while ((freePtr = Volatile.Read(ref s_freeList)) != IntPtr.Zero) - { - overlapped = (Win32ThreadPoolNativeOverlapped*)freePtr; - - if (Interlocked.CompareExchange(ref s_freeList, overlapped->_nextFree, freePtr) != freePtr) - continue; - - overlapped->_nextFree = IntPtr.Zero; - return overlapped; - } - - // None are free; allocate a new one. - overlapped = (Win32ThreadPoolNativeOverlapped*)NativeMemory.Alloc((nuint)sizeof(Win32ThreadPoolNativeOverlapped)); - *overlapped = default(Win32ThreadPoolNativeOverlapped); - - // Allocate a OverlappedData object, and an index at which to store it in _dataArray. - data = new OverlappedData(); - int dataIndex = Interlocked.Increment(ref s_dataCount) - 1; - - // Make sure we didn't wrap around. - if (dataIndex < 0) - Environment.FailFast("Too many outstanding Win32ThreadPoolNativeOverlapped instances"); - - while (true) - { - OverlappedData[]? dataArray = Volatile.Read(ref s_dataArray); - int currentLength = dataArray == null ? 0 : dataArray.Length; - - // If the current array is too small, create a new, larger one. - if (currentLength <= dataIndex) - { - int newLength = currentLength; - if (newLength == 0) - newLength = 128; - while (newLength <= dataIndex) - newLength = (newLength * 3) / 2; - - OverlappedData[]? newDataArray = dataArray; - Array.Resize(ref newDataArray, newLength); + internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) => + AllocateCore(callback, state, pinData, preAllocated, flowExecutionControl); - if (Interlocked.CompareExchange(ref s_dataArray, newDataArray, dataArray) != dataArray) - continue; // Someone else got the free one, try again + internal static unsafe void Free(Win32ThreadPoolNativeOverlapped* overlapped) => FreeCore(overlapped); - dataArray = newDataArray; - } + internal static unsafe NativeOverlapped* ToNativeOverlapped(Win32ThreadPoolNativeOverlapped* overlapped) => ToNativeOverlappedCore(overlapped); - // If we haven't stored this object in the array yet, do so now. Then we need to make another pass through - // the loop, in case another thread resized the array before we made this update. - if (s_dataArray[dataIndex] == null) - { - // Full fence so this write can't move past subsequent reads. - Interlocked.Exchange(ref dataArray![dataIndex], data); - continue; - } - - // We're already in the array, so we're done. - Debug.Assert(dataArray![dataIndex] == data); - overlapped->_dataIndex = dataIndex; - return overlapped; - } - } - - private void SetData(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionContext) - { - Debug.Assert(callback != null); - - OverlappedData data = Data; - - data._callback = callback; - data._state = state; - data._executionContext = flowExecutionContext ? ExecutionContext.Capture() : null; - data._preAllocated = preAllocated; - - // - // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to - // an instance of a blittable type to be pinned. - // - if (pinData != null) - { - object[]? objArray = pinData as object[]; - if (objArray != null && objArray.GetType() == typeof(object[])) - { - if (data._pinnedData == null || data._pinnedData.Length < objArray.Length) - Array.Resize(ref data._pinnedData, objArray.Length); - - for (int i = 0; i < objArray.Length; i++) - { - if (!data._pinnedData[i].IsAllocated) - data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); - else - data._pinnedData[i].Target = objArray[i]; - } - } - else - { - data._pinnedData ??= new GCHandle[1]; - - if (!data._pinnedData[0].IsAllocated) - data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned); - else - data._pinnedData[0].Target = pinData; - } - } - } - - internal static unsafe void Free(Win32ThreadPoolNativeOverlapped* overlapped) - { - // Reset all data. - overlapped->Data.Reset(); - overlapped->_overlapped = default(NativeOverlapped); + internal static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlapped(NativeOverlapped* overlapped) => FromNativeOverlappedCore(overlapped); - // Add to the free list. - while (true) - { - IntPtr freePtr = Volatile.Read(ref s_freeList); - overlapped->_nextFree = freePtr; - - if (Interlocked.CompareExchange(ref s_freeList, (IntPtr)overlapped, freePtr) == freePtr) - break; - } - } - - internal static unsafe NativeOverlapped* ToNativeOverlapped(Win32ThreadPoolNativeOverlapped* overlapped) - { - return (NativeOverlapped*)overlapped; - } - - internal static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlapped(NativeOverlapped* overlapped) - { - return (Win32ThreadPoolNativeOverlapped*)overlapped; - } - - internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) - { - OverlappedData data = overlapped->Data; - - Debug.Assert(!data._completed); - data._completed = true; - - if (data._executionContext == null) - { - Debug.Assert(data._callback != null, "Does CompleteWithCallback called after Reset?"); - data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); - return; - } - - ContextCallback callback = s_executionContextCallback; - if (callback == null) - s_executionContextCallback = callback = OnExecutionContextCallback; - - // Get an args object from the per-thread cache. - ExecutionContextCallbackArgs args = t_executionContextCallbackArgs; - args ??= new ExecutionContextCallbackArgs(); - - t_executionContextCallbackArgs = null; - - args._errorCode = errorCode; - args._bytesWritten = bytesWritten; - args._overlapped = overlapped; - args._data = data; - - ExecutionContext.Run(data._executionContext, callback, args); - } - - private static unsafe void OnExecutionContextCallback(object? state) - { - Debug.Assert(state != null); - ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state; - - uint errorCode = args._errorCode; - uint bytesWritten = args._bytesWritten; - Win32ThreadPoolNativeOverlapped* overlapped = args._overlapped; - OverlappedData data = args._data; - - // Put the args object back in the per-thread cache, now that we're done with it. - args._data = null; - t_executionContextCallbackArgs = args; - - Debug.Assert(data._callback != null, "Does OnExecutionContextCallback called after Reset?"); - data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); - } + internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) => CompleteWithCallbackCore(errorCode, bytesWritten, overlapped); - internal bool IsUserObject(byte[]? buffer) => ReferenceEquals(Data._pinnedData, buffer); + internal bool IsUserObject(byte[]? buffer) => IsUserObjectCore(buffer); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs new file mode 100644 index 0000000000000..68841bda351b8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + [StructLayout(LayoutKind.Sequential)] + internal partial struct Win32ThreadPoolNativeOverlapped + { + private static unsafe Win32ThreadPoolNativeOverlapped* AllocateCore(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) + { + Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew(); + try + { + overlapped->SetData(callback, state, pinData, preAllocated, flowExecutionControl); + } + catch + { + Free(overlapped); + throw; + } + return overlapped; + } + + private static unsafe Win32ThreadPoolNativeOverlapped* AllocateNew() + { + IntPtr freePtr; + Win32ThreadPoolNativeOverlapped* overlapped; + OverlappedData data; + + // Find a free Overlapped + while ((freePtr = Volatile.Read(ref s_freeList)) != IntPtr.Zero) + { + overlapped = (Win32ThreadPoolNativeOverlapped*)freePtr; + + if (Interlocked.CompareExchange(ref s_freeList, overlapped->_nextFree, freePtr) != freePtr) + continue; + + overlapped->_nextFree = IntPtr.Zero; + return overlapped; + } + + // None are free; allocate a new one. + overlapped = (Win32ThreadPoolNativeOverlapped*)NativeMemory.Alloc((nuint)sizeof(Win32ThreadPoolNativeOverlapped)); + *overlapped = default(Win32ThreadPoolNativeOverlapped); + + // Allocate a OverlappedData object, and an index at which to store it in _dataArray. + data = new OverlappedData(); + int dataIndex = Interlocked.Increment(ref s_dataCount) - 1; + + // Make sure we didn't wrap around. + if (dataIndex < 0) + Environment.FailFast("Too many outstanding Win32ThreadPoolNativeOverlapped instances"); + + while (true) + { + OverlappedData[]? dataArray = Volatile.Read(ref s_dataArray); + int currentLength = dataArray == null ? 0 : dataArray.Length; + + // If the current array is too small, create a new, larger one. + if (currentLength <= dataIndex) + { + int newLength = currentLength; + if (newLength == 0) + newLength = 128; + while (newLength <= dataIndex) + newLength = (newLength * 3) / 2; + + OverlappedData[]? newDataArray = dataArray; + Array.Resize(ref newDataArray, newLength); + + if (Interlocked.CompareExchange(ref s_dataArray, newDataArray, dataArray) != dataArray) + continue; // Someone else got the free one, try again + + dataArray = newDataArray; + } + + // If we haven't stored this object in the array yet, do so now. Then we need to make another pass through + // the loop, in case another thread resized the array before we made this update. + if (s_dataArray[dataIndex] == null) + { + // Full fence so this write can't move past subsequent reads. + Interlocked.Exchange(ref dataArray![dataIndex], data); + continue; + } + + // We're already in the array, so we're done. + Debug.Assert(dataArray![dataIndex] == data); + overlapped->_dataIndex = dataIndex; + return overlapped; + } + } + + private void SetData(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionContext) + { + Debug.Assert(callback != null); + + OverlappedData data = Data; + + data._callback = callback; + data._state = state; + data._executionContext = flowExecutionContext ? ExecutionContext.Capture() : null; + data._preAllocated = preAllocated; + + // + // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to + // an instance of a blittable type to be pinned. + // + if (pinData != null) + { + object[]? objArray = pinData as object[]; + if (objArray != null && objArray.GetType() == typeof(object[])) + { + if (data._pinnedData == null || data._pinnedData.Length < objArray.Length) + Array.Resize(ref data._pinnedData, objArray.Length); + + for (int i = 0; i < objArray.Length; i++) + { + if (!data._pinnedData[i].IsAllocated) + data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); + else + data._pinnedData[i].Target = objArray[i]; + } + } + else + { + data._pinnedData ??= new GCHandle[1]; + + if (!data._pinnedData[0].IsAllocated) + data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned); + else + data._pinnedData[0].Target = pinData; + } + } + } + + private static unsafe void FreeCore(Win32ThreadPoolNativeOverlapped* overlapped) + { + // Reset all data. + overlapped->Data.Reset(); + overlapped->_overlapped = default(NativeOverlapped); + + // Add to the free list. + while (true) + { + IntPtr freePtr = Volatile.Read(ref s_freeList); + overlapped->_nextFree = freePtr; + + if (Interlocked.CompareExchange(ref s_freeList, (IntPtr)overlapped, freePtr) == freePtr) + break; + } + } + + private static unsafe NativeOverlapped* ToNativeOverlappedCore(Win32ThreadPoolNativeOverlapped* overlapped) + { + return (NativeOverlapped*)overlapped; + } + + private static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlappedCore(NativeOverlapped* overlapped) + { + return (Win32ThreadPoolNativeOverlapped*)overlapped; + } + + private static unsafe void CompleteWithCallbackCore(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) + { + OverlappedData data = overlapped->Data; + + Debug.Assert(!data._completed); + data._completed = true; + + if (data._executionContext == null) + { + Debug.Assert(data._callback != null, "Does CompleteWithCallback called after Reset?"); + data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); + return; + } + + ContextCallback callback = s_executionContextCallback; + if (callback == null) + s_executionContextCallback = callback = OnExecutionContextCallback; + + // Get an args object from the per-thread cache. + ExecutionContextCallbackArgs args = t_executionContextCallbackArgs; + args ??= new ExecutionContextCallbackArgs(); + + t_executionContextCallbackArgs = null; + + args._errorCode = errorCode; + args._bytesWritten = bytesWritten; + args._overlapped = overlapped; + args._data = data; + + ExecutionContext.Run(data._executionContext, callback, args); + } + + private static unsafe void OnExecutionContextCallback(object? state) + { + Debug.Assert(state != null); + ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state; + + uint errorCode = args._errorCode; + uint bytesWritten = args._bytesWritten; + Win32ThreadPoolNativeOverlapped* overlapped = args._overlapped; + OverlappedData data = args._data; + + // Put the args object back in the per-thread cache, now that we're done with it. + args._data = null; + t_executionContextCallbackArgs = args; + + Debug.Assert(data._callback != null, "Does OnExecutionContextCallback called after Reset?"); + data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); + } + + private bool IsUserObjectCore(byte[]? buffer) => ReferenceEquals(Data._pinnedData, buffer); + } +} From 0d188a1731c9915de2ede676c4a9ddafa1604316 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 17:29:43 -0700 Subject: [PATCH 006/168] Move Win32ThreadPoolPreAllocatedOverlapped.cs implementation to ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs --- .../Win32ThreadPoolPreAllocatedOverlapped.cs | 22 ++-------- ...reAllocatedOverlapped.WindowsThreadPool.cs | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs index e57f82aa24a52..9c441a578fd03 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -17,25 +17,11 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje } [CLSCompliant(false)] - public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => UnsafeCreateCore(callback, state, pinData); - private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); - } - - internal bool AddRef() - { - return _lifetime.AddRef(); - } + internal bool AddRef() => AddRefCore(); - internal void Release() - { - _lifetime.Release(this); - } + internal void Release() => ReleaseCore(); public void Dispose() { @@ -59,6 +45,6 @@ unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) } } - internal unsafe bool IsUserObject(byte[]? buffer) => _overlapped->IsUserObject(buffer); + internal unsafe bool IsUserObject(byte[]? buffer) => IsUserObjectCore(buffer); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs new file mode 100644 index 0000000000000..62cb920630dd7 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Threading +{ + public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + [CLSCompliant(false)] + private static PreAllocatedOverlapped UnsafeCreateCore(IOCompletionCallback callback, object? state, object? pinData) => + new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); + + private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(callback); + + _overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + } + + private bool AddRefCore() + { + return _lifetime.AddRef(); + } + + private void ReleaseCore() + { + _lifetime.Release(this); + } + + unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + { + if (_overlapped != null) + { + if (disposed) + Win32ThreadPoolNativeOverlapped.Free(_overlapped); + else + *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlapped) = default(NativeOverlapped); + } + } + + private unsafe bool IsUserObjectCore(byte[]? buffer) => _overlapped->IsUserObject(buffer); + } +} From 2007a134904611e48e460d6f2c1b1221bee5385d Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Mar 2023 19:24:38 -0700 Subject: [PATCH 007/168] Fix some dependencies --- .../src/System.Private.CoreLib.Shared.projitems | 5 ++++- .../ThreadPoolBoundHandle.Portable.Core.cs | 16 +--------------- .../Threading/ThreadPoolBoundHandle.Portable.cs | 2 +- .../src/System/Threading/WindowsThreadPool.cs | 0 4 files changed, 6 insertions(+), 17 deletions(-) rename src/{coreclr => libraries}/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f28e4c0ac8eba..c5cde5124ca12 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2508,10 +2508,13 @@ - + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index e8cac62cdd2d6..edc3819704eef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -17,7 +17,7 @@ namespace System.Threading public sealed partial class ThreadPoolBoundHandle : IDisposable { - private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { ArgumentNullException.ThrowIfNull(handle); @@ -27,12 +27,10 @@ private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) return BindHandleCore(handle); } - [CLSCompliant(false)] private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); - [CLSCompliant(false)] private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); @@ -46,7 +44,6 @@ private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) return overlapped._nativeOverlapped; } - [CLSCompliant(false)] private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) { ArgumentNullException.ThrowIfNull(preAllocated); @@ -71,7 +68,6 @@ private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) } } - [CLSCompliant(false)] private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -89,7 +85,6 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) Overlapped.Free(overlapped); } - [CLSCompliant(false)] private static unsafe object? GetNativeOverlappedStateCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -113,14 +108,5 @@ private static unsafe ThreadPoolBoundHandleOverlapped GetOverlappedWrapper(Nativ return wrapper; } - - public void Dispose() - { - // .NET Native's version of ThreadPoolBoundHandle that wraps the Win32 ThreadPool holds onto - // native resources so it needs to be disposable. To match the contract, we are also disposable. - // We also implement a disposable state to mimic behavior between this implementation and - // .NET Native's version (code written against us, will also work against .NET Native's version). - _isDisposed = true; - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs index b264547f28d31..1bad4b3511c8b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs @@ -71,7 +71,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// does not take ownership of , /// it remains the responsibility of the caller to call . /// - public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandleCore(handle); + public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandlePortableCore(handle); /// /// Returns an unmanaged pointer to a structure, specifying diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs From a3d1aea8a871473aad595bd2d1b3c8c819370aeb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Mar 2023 21:37:20 -0700 Subject: [PATCH 008/168] Fix some dependencies --- .../System.Private.CoreLib.csproj | 7 + .../RegisteredWaitHandle.CoreCLR.Windows.cs | 182 +++++++ .../System/Threading/Thread.CoreCLR.Core.cs | 191 +++++++ .../src/System/Threading/Thread.CoreCLR.cs | 192 +------ .../Threading/ThreadPool.CoreCLR.Windows.cs | 2 +- .../System/Threading/ThreadPool.CoreCLR.cs | 2 +- .../Threading/Thread.NativeAot.Windows.cs | 447 +--------------- .../System/Threading/ThreadPool.Windows.cs | 226 +------- .../Win32ThreadPoolRegisteredWaitHandle.cs | 110 ++++ .../System.Private.CoreLib.Shared.projitems | 5 +- .../RegisteredWaitHandle.Portable.Core.cs | 3 +- .../RegisteredWaitHandle.Portable.cs | 2 +- .../RegisteredWaitHandle.WindowsThreadPool.cs | 161 ++++++ .../Threading/Thread.WindowsThreadPool.cs | 489 ++++++++++++++++++ .../ThreadPoolBoundHandle.Portable.Core.cs | 2 - .../System/Threading/ThreadPoolWorkQueue.cs | 4 +- .../src/System/Threading/WindowsThreadPool.cs | 8 +- 17 files changed, 1168 insertions(+), 865 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 1592a5b94bd26..c5437706518d7 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -111,6 +111,7 @@ $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src')) $(MSBuildThisFileDirectory)src + $(MSBuildThisFileDirectory)..\nativeaot\System.Private.CoreLib\src @@ -289,6 +290,12 @@ Common\Interop\Windows\OleAut32\Interop.VariantClear.cs + + + + + Interop\Windows\Kernel32\Interop.ThreadPool.cs + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs new file mode 100644 index 0000000000000..472b3158d6a70 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ +#if !FEATURE_WASM_THREADS + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif + public sealed partial class RegisteredWaitHandle : MarshalByRefObject + { + private readonly object _lock; + private SafeWaitHandle _waitHandle; + private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; + private readonly uint _millisecondsTimeout; + private bool _repeating; + private bool _unregistering; + + // Handle to this object to keep it alive + private GCHandle _gcHandle; + + // Pointer to the TP_WAIT structure + private IntPtr _tpWait; + + internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + uint millisecondsTimeout, bool repeating) + { + if (!ThreadPool.UseWindowsThreadPool) + { + GC.SuppressFinalize(this); + } + _lock = new Lock(); + + // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) + waitHandle.DangerousAddRef(); + _waitHandle = waitHandle; + + _callbackHelper = callbackHelper; + _millisecondsTimeout = millisecondsTimeout; + _repeating = repeating; + + // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked + _gcHandle = GCHandle.Alloc(this); + + _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); + + if (_tpWait == IntPtr.Zero) + { + _gcHandle.Free(); + throw new OutOfMemoryException(); + } + } + + internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + int millisecondsTimeout, bool repeating) + { + Thread.ThrowIfNoThreadStart(); + Handle = waitHandle.SafeWaitHandle; + Callback = callbackHelper; + TimeoutDurationMs = millisecondsTimeout; + Repeating = repeating; + if (!IsInfiniteTimeout) + { + RestartTimeout(); + } + } + + private static AutoResetEvent? s_cachedEvent; + + private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); + + /// + /// The callback to execute when the wait on either times out or completes. + /// + internal _ThreadPoolWaitOrTimerCallback Callback { get; } + + /// + /// The that was registered. + /// + internal SafeWaitHandle Handle { get; } + + /// + /// The time this handle times out at in ms. + /// + internal int TimeoutTimeMs { get; private set; } + + internal int TimeoutDurationMs { get; } + + internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; + + /// + /// Whether or not the wait is a repeating wait. + /// + internal bool Repeating { get; } + + /// + /// The the user passed in via . + /// + private SafeWaitHandle? UserUnregisterWaitHandle { get; set; } + + private IntPtr UserUnregisterWaitHandleValue { get; set; } + + private static IntPtr InvalidHandleValue => new IntPtr(-1); + + internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue; + + /// + /// The number of callbacks that are currently queued on the Thread Pool or executing. + /// + private int _numRequestedCallbacks; + + /// + /// Notes if we need to signal the user's unregister event after all callbacks complete. + /// + private bool _signalAfterCallbacksComplete; + + private bool _unregisterCalled; + + private bool _unregistered; + + private AutoResetEvent? _callbacksComplete; + + private AutoResetEvent? _removed; + + /// + /// The this was registered on. + /// + internal PortableThreadPool.WaitThread? WaitThread { get; set; } + + [UnmanagedCallersOnly] + internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) => + RegisteredWaitCallbackCore(instance, context, wait, waitResult); + + internal unsafe void RestartWait() => RestartWaitCore(); + + public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); + + ~RegisteredWaitHandle() + { + Debug.Assert(ThreadPool.UseWindowsThreadPool); + // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC + Debug.Assert(!_gcHandle.IsAllocated); + + // If this object gets resurrected and another thread calls Unregister, that creates a race condition. + // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. + // The _lock may be null in case of OOM in the constructor. + if ((_lock != null) && _lock.TryAcquire(0)) + { + try + { + if (!_unregistering) + { + _unregistering = true; + + if (_tpWait != IntPtr.Zero) + { + // There must be no in-flight callbacks; just dispose resources + Interop.Kernel32.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + } + + if (_waitHandle != null) + { + _waitHandle.DangerousRelease(); + _waitHandle = null; + } + } + } + finally + { + _lock.Release(); + } + } + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs new file mode 100644 index 0000000000000..c410887dfdfb5 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs @@ -0,0 +1,191 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Versioning; + +// PR-Comment: This implementation comes from Thread.CoreCLR.cs (src\coreclr\System.Private.CoreLib\src\System\Threading\Thread.CoreCLR.cs) + +namespace System.Threading +{ + + public sealed partial class Thread + { + /// Returns handle for interop with EE. The handle is guaranteed to be non-null. + private ThreadHandle GetNativeHandleCore() + { + IntPtr thread = _DONT_USE_InternalThread; + + // This should never happen under normal circumstances. + if (thread == IntPtr.Zero) + { + throw new ArgumentException(null, SR.Argument_InvalidHandle); + } + + return new ThreadHandle(thread); + } + + private unsafe void StartCore() + { + lock (this) + { + fixed (char* pThreadName = _name) + { + StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName); + } + } + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")] + private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName); + + // Called from the runtime + private void StartCallback() + { + StartHelper? startHelper = _startHelper; + Debug.Assert(startHelper != null); + _startHelper = null; + + startHelper.Run(); + } + + // Invoked by VM. Helper method to get a logical thread ID for StringBuilder (for + // correctness) and for FileStream's async code path (for perf, to avoid creating + // a Thread instance). + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr InternalGetCurrentThread(); + + /// + /// Suspends the current thread for timeout milliseconds. If timeout == 0, + /// forces the thread to give up the remainder of its timeslice. If timeout + /// == Timeout.Infinite, no timeout will occur. + /// + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void SleepInternal(int millisecondsTimeout); + + /// + /// Wait for a length of time proportional to 'iterations'. Each iteration is should + /// only take a few machine instructions. Calling this API is preferable to coding + /// a explicit busy loop because the hardware can be informed that it is busy waiting. + /// + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void SpinWaitInternal(int iterations); + + private static void SpinWaitCore(int iterations) => SpinWaitInternal(iterations); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_YieldThread")] + private static partial Interop.BOOL YieldInternal(); + + private static bool YieldCore() => YieldInternal() != Interop.BOOL.FALSE; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Thread GetCurrentThreadNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void Initialize(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void InternalFinalize(); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_InformThreadNameChange", StringMarshalling = StringMarshalling.Utf16)] + private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern bool IsBackgroundNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void SetBackgroundNative(bool isBackground); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern int GetPriorityNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void SetPriorityNative(int priority); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentOSThreadId")] + private static partial ulong GetCurrentOSThreadId(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern int GetThreadStateNative(); + + private ApartmentState GetApartmentStateCore() => +#if FEATURE_COMINTEROP_APARTMENT_SUPPORT + (ApartmentState)GetApartmentStateNative(); +#else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT + ApartmentState.Unknown; +#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + + /// + /// An unstarted thread can be marked to indicate that it will host a + /// single-threaded or multi-threaded apartment. + /// +#if FEATURE_COMINTEROP_APARTMENT_SUPPORT + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state); + + // Special case where we pass in Unknown and get back MTA. + // Once we CoUninitialize the thread, the OS will still + // report the thread as implicitly in the MTA if any + // other thread in the process is CoInitialized. + if ((state == System.Threading.ApartmentState.Unknown) && (retState == System.Threading.ApartmentState.MTA)) + { + return true; + } + + if (retState != state) + { + if (throwOnError) + { + string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); + throw new InvalidOperationException(msg); + } + + return false; + } + + return true; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern int GetApartmentStateNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern int SetApartmentStateNative(int state); +#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT + private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + if (state != ApartmentState.Unknown) + { + if (throwOnError) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + + return false; + } + + return true; + } +#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ResetThreadPoolThreadCore() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(IsThreadPoolThread); + + if (_mayNeedResetForThreadPool) + { + ResetThreadPoolThreadSlow(); + } + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 7a52db2be2de6..7376080d49530 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -68,95 +68,20 @@ public extern int ManagedThreadId } /// Returns handle for interop with EE. The handle is guaranteed to be non-null. - internal ThreadHandle GetNativeHandle() - { - IntPtr thread = _DONT_USE_InternalThread; - - // This should never happen under normal circumstances. - if (thread == IntPtr.Zero) - { - throw new ArgumentException(null, SR.Argument_InvalidHandle); - } - - return new ThreadHandle(thread); - } - - private unsafe void StartCore() - { - lock (this) - { - fixed (char* pThreadName = _name) - { - StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName); - } - } - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")] - private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName); + internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); - // Called from the runtime - private void StartCallback() - { - StartHelper? startHelper = _startHelper; - Debug.Assert(startHelper != null); - _startHelper = null; - - startHelper.Run(); - } + public static void SpinWait(int iterations) => SpinWaitCore(iterations); - // Invoked by VM. Helper method to get a logical thread ID for StringBuilder (for - // correctness) and for FileStream's async code path (for perf, to avoid creating - // a Thread instance). - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr InternalGetCurrentThread(); - - /// - /// Suspends the current thread for timeout milliseconds. If timeout == 0, - /// forces the thread to give up the remainder of its timeslice. If timeout - /// == Timeout.Infinite, no timeout will occur. - /// - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void SleepInternal(int millisecondsTimeout); - - /// - /// Wait for a length of time proportional to 'iterations'. Each iteration is should - /// only take a few machine instructions. Calling this API is preferable to coding - /// a explicit busy loop because the hardware can be informed that it is busy waiting. - /// - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void SpinWaitInternal(int iterations); - - public static void SpinWait(int iterations) => SpinWaitInternal(iterations); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_YieldThread")] - private static partial Interop.BOOL YieldInternal(); - - public static bool Yield() => YieldInternal() != Interop.BOOL.FALSE; - - [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern Thread GetCurrentThreadNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void Initialize(); + public static bool Yield() => YieldCore(); /// Clean up the thread when it goes away. ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InternalFinalize(); - partial void ThreadNameChanged(string? value) { InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_InformThreadNameChange", StringMarshalling = StringMarshalling.Utf16)] - private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len); - /// Returns true if the thread has been started and is not dead. public extern bool IsAlive { @@ -181,12 +106,6 @@ public bool IsBackground } } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool IsBackgroundNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void SetBackgroundNative(bool isBackground); - /// Returns true if the thread is a threadpool thread. public extern bool IsThreadPoolThread { @@ -210,84 +129,13 @@ public ThreadPriority Priority } } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern int GetPriorityNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void SetPriorityNative(int priority); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentOSThreadId")] - private static partial ulong GetCurrentOSThreadId(); - /// /// Return the thread state as a consistent set of bits. This is more /// general then IsAlive or IsBackground. /// public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); - [MethodImpl(MethodImplOptions.InternalCall)] - private extern int GetThreadStateNative(); - - public ApartmentState GetApartmentState() => -#if FEATURE_COMINTEROP_APARTMENT_SUPPORT - (ApartmentState)GetApartmentStateNative(); -#else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT - ApartmentState.Unknown; -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - - /// - /// An unstarted thread can be marked to indicate that it will host a - /// single-threaded or multi-threaded apartment. - /// -#if FEATURE_COMINTEROP_APARTMENT_SUPPORT - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) - { - ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state); - - // Special case where we pass in Unknown and get back MTA. - // Once we CoUninitialize the thread, the OS will still - // report the thread as implicitly in the MTA if any - // other thread in the process is CoInitialized. - if ((state == System.Threading.ApartmentState.Unknown) && (retState == System.Threading.ApartmentState.MTA)) - { - return true; - } - - if (retState != state) - { - if (throwOnError) - { - string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); - throw new InvalidOperationException(msg); - } - - return false; - } - - return true; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern int GetApartmentStateNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern int SetApartmentStateNative(int state); -#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT - private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) - { - if (state != ApartmentState.Unknown) - { - if (throwOnError) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); - } - - return false; - } - - return true; - } -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + public ApartmentState GetApartmentState() => GetApartmentStateCore(); #if FEATURE_COMINTEROP [MethodImpl(MethodImplOptions.InternalCall)] @@ -298,27 +146,6 @@ public void DisableComObjectEagerCleanup() } #endif // FEATURE_COMINTEROP - /// - /// Interrupts a thread that is inside a Wait(), Sleep() or Join(). If that - /// thread is not currently blocked in that manner, it will be interrupted - /// when it next begins to block. - /// - [MethodImpl(MethodImplOptions.InternalCall)] - public extern void Interrupt(); - - /// - /// Waits for the thread to die or for timeout milliseconds to elapse. - /// - /// - /// Returns true if the thread died, or false if the wait timed out. If - /// -1 is given as the parameter, no timeout will occur. - /// - /// if timeout < -1 (Timeout.Infinite) - /// if the thread is interrupted while waiting - /// if the thread has not been started yet - [MethodImpl(MethodImplOptions.InternalCall)] - public extern bool Join(int millisecondsTimeout); - /// /// Max value to be passed into for optimal delaying. This value is normalized to be /// appropriate for the processor. @@ -330,15 +157,6 @@ internal static int OptimalMaxSpinWaitsPerSpinIteration } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ResetThreadPoolThread() - { - Debug.Assert(this == CurrentThread); - Debug.Assert(IsThreadPoolThread); - - if (_mayNeedResetForThreadPool) - { - ResetThreadPoolThreadSlow(); - } - } + internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs index 29432a2b52bfb..db9a61efba468 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs @@ -9,7 +9,7 @@ namespace System.Threading { public static partial class ThreadPool { - private static readonly bool UseWindowsThreadPool = + internal static readonly bool UseWindowsThreadPool = Environment.GetEnvironmentVariable("DOTNET_ThreadPool_UseWindowsThreadPool") == "1" || AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", false); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index dfce7fb88b34e..6cf6fcf870270 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -191,7 +191,7 @@ internal static void RequestWorkerThread() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int currentTimeMs) + internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs) { return PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete( diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index 467e13cfd6033..627f23e62f18d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -28,460 +28,31 @@ public sealed partial class Thread private static volatile bool s_comInitializedOnFinalizerThread; - partial void PlatformSpecificInitialize(); + public ApartmentState GetApartmentState() => GetApartmentStateCore(); - // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start - private void PlatformSpecificInitializeExistingThread() - { - _osHandle = GetOSHandleForCurrentThread(); - } - - private static SafeWaitHandle GetOSHandleForCurrentThread() - { - IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); - IntPtr currentThreadHandle = Interop.Kernel32.GetCurrentThread(); - SafeWaitHandle threadHandle; - - if (Interop.Kernel32.DuplicateHandle(currentProcHandle, currentThreadHandle, currentProcHandle, - out threadHandle, 0, false, Interop.Kernel32.DUPLICATE_SAME_ACCESS)) - { - return threadHandle; - } - - // Throw an ApplicationException for compatibility with CoreCLR. First save the error code. - int errorCode = Marshal.GetLastWin32Error(); - var ex = new ApplicationException(); - ex.HResult = errorCode; - throw ex; - } - - private static ThreadPriority MapFromOSPriority(OSThreadPriority priority) - { - if (priority <= OSThreadPriority.Lowest) - { - // OS thread priorities in the [Idle,Lowest] range are mapped to ThreadPriority.Lowest - return ThreadPriority.Lowest; - } - switch (priority) - { - case OSThreadPriority.BelowNormal: - return ThreadPriority.BelowNormal; - - case OSThreadPriority.Normal: - return ThreadPriority.Normal; - - case OSThreadPriority.AboveNormal: - return ThreadPriority.AboveNormal; - - case OSThreadPriority.ErrorReturn: - Debug.Fail("GetThreadPriority failed"); - return ThreadPriority.Normal; - } - // Handle OSThreadPriority.ErrorReturn value before this check! - if (priority >= OSThreadPriority.Highest) - { - // OS thread priorities in the [Highest,TimeCritical] range are mapped to ThreadPriority.Highest - return ThreadPriority.Highest; - } - Debug.Fail("Unreachable"); - return ThreadPriority.Normal; - } - - private static OSThreadPriority MapToOSPriority(ThreadPriority priority) - { - switch (priority) - { - case ThreadPriority.Lowest: - return OSThreadPriority.Lowest; - - case ThreadPriority.BelowNormal: - return OSThreadPriority.BelowNormal; - - case ThreadPriority.Normal: - return OSThreadPriority.Normal; - - case ThreadPriority.AboveNormal: - return OSThreadPriority.AboveNormal; - - case ThreadPriority.Highest: - return OSThreadPriority.Highest; - - default: - Debug.Fail("Unreachable"); - return OSThreadPriority.Normal; - } - } - - private ThreadPriority GetPriorityLive() - { - Debug.Assert(!_osHandle.IsInvalid); - return MapFromOSPriority(Interop.Kernel32.GetThreadPriority(_osHandle)); - } - - private bool SetPriorityLive(ThreadPriority priority) - { - Debug.Assert(!_osHandle.IsInvalid); - return Interop.Kernel32.SetThreadPriority(_osHandle, (int)MapToOSPriority(priority)); - } - - [UnmanagedCallersOnly] - private static void OnThreadExit() - { - Thread? currentThread = t_currentThread; - if (currentThread != null) - { - StopThread(currentThread); - } - } - - private bool JoinInternal(int millisecondsTimeout) - { - // This method assumes the thread has been started - Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); - SafeWaitHandle waitHandle = _osHandle; - - // If an OS thread is terminated and its Thread object is resurrected, _osHandle may be finalized and closed - if (waitHandle.IsClosed) - { - return true; - } - - // Handle race condition with the finalizer - try - { - waitHandle.DangerousAddRef(); - } - catch (ObjectDisposedException) - { - return true; - } - - try - { - int result; - - if (millisecondsTimeout == 0) - { - result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); - } - else - { - result = WaitHandle.WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout); - } - - return result == (int)Interop.Kernel32.WAIT_OBJECT_0; - } - finally - { - waitHandle.DangerousRelease(); - } - } - - private unsafe bool CreateThread(GCHandle thisThreadHandle) - { - const int AllocationGranularity = 0x10000; // 64 KiB - - int stackSize = _startHelper._maxStackSize; - if ((0 < stackSize) && (stackSize < AllocationGranularity)) - { - // If StackSizeParamIsAReservation flag is set and the reserve size specified by CreateThread's - // dwStackSize parameter is less than or equal to the initially committed stack size specified in - // the executable header, the reserve size will be set to the initially committed size rounded up - // to the nearest multiple of 1 MiB. In all cases the reserve size is rounded up to the nearest - // multiple of the system's allocation granularity (typically 64 KiB). - // - // To prevent overreservation of stack memory for small stackSize values, we increase stackSize to - // the allocation granularity. We assume that the SizeOfStackCommit field of IMAGE_OPTIONAL_HEADER - // is strictly smaller than the allocation granularity (the field's default value is 4 KiB); - // otherwise, at least 1 MiB of memory will be reserved. Note that the desktop CLR increases - // stackSize to 256 KiB if it is smaller than that. - stackSize = AllocationGranularity; - } - - _osHandle = Interop.Kernel32.CreateThread(IntPtr.Zero, (IntPtr)stackSize, - &ThreadEntryPoint, (IntPtr)thisThreadHandle, - Interop.Kernel32.CREATE_SUSPENDED | Interop.Kernel32.STACK_SIZE_PARAM_IS_A_RESERVATION, - out _); - - if (_osHandle.IsInvalid) - { - return false; - } - - // CoreCLR ignores OS errors while setting the priority, so do we - SetPriorityLive(_priority); - - Interop.Kernel32.ResumeThread(_osHandle); - return true; - } - - /// - /// This is an entry point for managed threads created by application - /// - [UnmanagedCallersOnly] - private static uint ThreadEntryPoint(IntPtr parameter) - { - StartThread(parameter); - return 0; - } - - public ApartmentState GetApartmentState() - { - if (this != CurrentThread) - { - if (HasStarted()) - throw new ThreadStateException(); - return _initialApartmentState; - } - - switch (GetCurrentApartmentType()) - { - case ApartmentType.STA: - return ApartmentState.STA; - case ApartmentType.MTA: - return ApartmentState.MTA; - default: - return ApartmentState.Unknown; - } - } - - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) - { - ApartmentState retState; - - if (this != CurrentThread) - { - using (LockHolder.Hold(_lock)) - { - if (HasStarted()) - throw new ThreadStateException(); - - // Compat: Disallow resetting the initial apartment state - if (_initialApartmentState == ApartmentState.Unknown) - _initialApartmentState = state; - - retState = _initialApartmentState; - } - } - else - { - - if ((t_comState & ComState.Locked) == 0) - { - if (state != ApartmentState.Unknown) - { - InitializeCom(state); - } - else - { - UninitializeCom(); - } - } - - // Clear the cache and check whether new state matches the desired state - t_apartmentType = ApartmentType.Unknown; - - retState = GetApartmentState(); - } - - if (retState != state) - { - if (throwOnError) - { - string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); - throw new InvalidOperationException(msg); - } - - return false; - } - - return true; - } - - private void InitializeComOnNewThread() - { - InitializeCom(_initialApartmentState); - } - - internal static void InitializeComForFinalizerThread() - { - InitializeCom(); - - // Prevent re-initialization of COM model on finalizer thread - t_comState |= ComState.Locked; - - s_comInitializedOnFinalizerThread = true; - } - - private static void InitializeComForThreadPoolThread() - { - // Initialized COM - take advantage of implicit MTA initialized by the finalizer thread - SpinWait sw = new SpinWait(); - while (!s_comInitializedOnFinalizerThread) - { - RuntimeImports.RhInitializeFinalizerThread(); - sw.SpinOnce(0); - } - - // Prevent re-initialization of COM model on threadpool threads - t_comState |= ComState.Locked; - } - - private static void InitializeCom(ApartmentState state = ApartmentState.MTA) - { - if ((t_comState & ComState.InitializedByUs) != 0) - return; - -#if ENABLE_WINRT - int hr = Interop.WinRT.RoInitialize( - (state == ApartmentState.STA) ? Interop.WinRT.RO_INIT_SINGLETHREADED - : Interop.WinRT.RO_INIT_MULTITHREADED); -#else - int hr = Interop.Ole32.CoInitializeEx(IntPtr.Zero, - (state == ApartmentState.STA) ? Interop.Ole32.COINIT_APARTMENTTHREADED - : Interop.Ole32.COINIT_MULTITHREADED); -#endif - if (hr < 0) - { - // RPC_E_CHANGED_MODE indicates this thread has been already initialized with a different - // concurrency model. We stay away and let whoever else initialized the COM to be in control. - if (hr == HResults.RPC_E_CHANGED_MODE) - return; - - // CoInitializeEx returns E_NOTIMPL on Windows Nano Server for STA - if (hr == HResults.E_NOTIMPL) - throw new PlatformNotSupportedException(); - - throw new OutOfMemoryException(); - } - - t_comState |= ComState.InitializedByUs; - - // If the thread has already been CoInitialized to the proper mode, then - // we don't want to leave an outstanding CoInit so we CoUninit. - if (hr > 0) - UninitializeCom(); - } - - private static void UninitializeCom() - { - if ((t_comState & ComState.InitializedByUs) == 0) - return; - -#if ENABLE_WINRT - Interop.WinRT.RoUninitialize(); -#else - Interop.Ole32.CoUninitialize(); -#endif - - t_comState &= ~ComState.InitializedByUs; - } + internal static void InitializeComForFinalizerThread() => InitializeComForFinalizerThreadCore(); // TODO: https://github.com/dotnet/runtime/issues/22161 - public void DisableComObjectEagerCleanup() { } - - private static Thread InitializeExistingThreadPoolThread() - { - ThreadPool.InitializeForThreadPoolThread(); - - InitializeComForThreadPoolThread(); - - Thread thread = CurrentThread; - thread.SetThreadStateBit(ThreadPoolThread); - return thread; - } + public void DisableComObjectEagerCleanup() => DisableComObjectEagerCleanupCore(); // Use ThreadPoolCallbackWrapper instead of calling this function directly [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Thread EnsureThreadPoolThreadInitialized() - { - Thread? thread = t_currentThread; - if (thread != null && thread.GetThreadStateBit(ThreadPoolThread)) - return thread; - return InitializeExistingThreadPoolThread(); - } + internal static Thread EnsureThreadPoolThreadInitialized() => EnsureThreadPoolThreadInitializedCore(); - public void Interrupt() { throw new PlatformNotSupportedException(); } + public void Interrupt() => InterruptCore(); // // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. // This should be used by code that's expected to be called inside the STA message pump, so that it won't // reenter itself. In an ASTA, this should only be the CCW implementations of IUnknown and IInspectable. // - internal static void SuppressReentrantWaits() - { - t_reentrantWaitSuppressionCount++; - } + internal static void SuppressReentrantWaits() => SuppressReentrantWaitsCore(); - internal static void RestoreReentrantWaits() - { - Debug.Assert(t_reentrantWaitSuppressionCount > 0); - t_reentrantWaitSuppressionCount--; - } - - internal static bool ReentrantWaitsEnabled => - GetCurrentApartmentType() == ApartmentType.STA && t_reentrantWaitSuppressionCount == 0; - - internal static ApartmentType GetCurrentApartmentType() - { - ApartmentType currentThreadType = t_apartmentType; - if (currentThreadType != ApartmentType.Unknown) - return currentThreadType; - - Interop.APTTYPE aptType; - Interop.APTTYPEQUALIFIER aptTypeQualifier; - int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier); - - ApartmentType type = ApartmentType.Unknown; + internal static void RestoreReentrantWaits() => RestoreReentrantWaitsCore(); - switch (result) - { - case HResults.CO_E_NOTINITIALIZED: - type = ApartmentType.None; - break; + internal static bool ReentrantWaitsEnabled => ReentrantWaitsEnabledCore(); - case HResults.S_OK: - switch (aptType) - { - case Interop.APTTYPE.APTTYPE_STA: - case Interop.APTTYPE.APTTYPE_MAINSTA: - type = ApartmentType.STA; - break; - - case Interop.APTTYPE.APTTYPE_MTA: - type = ApartmentType.MTA; - break; - - case Interop.APTTYPE.APTTYPE_NA: - switch (aptTypeQualifier) - { - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: - type = ApartmentType.MTA; - break; - - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: - type = ApartmentType.STA; - break; - - default: - Debug.Fail("NA apartment without NA qualifier"); - break; - } - break; - } - break; - - default: - Debug.Fail("bad return from CoGetApartmentType"); - break; - } - - if (type != ApartmentType.Unknown) - t_apartmentType = type; - return type; - } + internal static ApartmentType GetCurrentApartmentType() => GetCurrentApartmentTypeCore(); internal enum ApartmentType : byte { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index c74d1130eb515..aa9659d6870c2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -13,230 +13,6 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // -#if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] -#endif - public sealed class RegisteredWaitHandle : MarshalByRefObject - { - private readonly Lock _lock; - private SafeWaitHandle _waitHandle; - private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; - private readonly uint _millisecondsTimeout; - private bool _repeating; - private bool _unregistering; - - // Handle to this object to keep it alive - private GCHandle _gcHandle; - - // Pointer to the TP_WAIT structure - private IntPtr _tpWait; - - internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, - uint millisecondsTimeout, bool repeating) - { - if (!ThreadPool.UseWindowsThreadPool) - { - GC.SuppressFinalize(this); - } - _lock = new Lock(); - - // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) - waitHandle.DangerousAddRef(); - _waitHandle = waitHandle; - - _callbackHelper = callbackHelper; - _millisecondsTimeout = millisecondsTimeout; - _repeating = repeating; - - // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked - _gcHandle = GCHandle.Alloc(this); - - _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); - - if (_tpWait == IntPtr.Zero) - { - _gcHandle.Free(); - throw new OutOfMemoryException(); - } - } - - [UnmanagedCallersOnly] - internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) - { - var wrapper = ThreadPoolCallbackWrapper.Enter(); - GCHandle handle = (GCHandle)context; - RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; - Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); - - bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); - registeredWaitHandle.PerformCallback(timedOut); - ThreadPool.IncrementCompletedWorkItemCount(); - wrapper.Exit(); - } - - private void PerformCallback(bool timedOut) - { - bool lockAcquired; - var spinner = new SpinWait(); - - // Prevent the race condition with Unregister and the previous PerformCallback call, which may still be - // holding the _lock. - while (!(lockAcquired = _lock.TryAcquire(0)) && !Volatile.Read(ref _unregistering)) - { - spinner.SpinOnce(); - } - - // If another thread is running Unregister, no need to restart the timer or clean up - if (lockAcquired) - { - try - { - if (!_unregistering) - { - if (_repeating) - { - // Allow this wait to fire again. Restart the timer before executing the callback. - RestartWait(); - } - else - { - // This wait will not be fired again. Free the GC handle to allow the GC to collect this object. - Debug.Assert(_gcHandle.IsAllocated); - _gcHandle.Free(); - } - } - } - finally - { - _lock.Release(); - } - } - - _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper, timedOut); - } - - internal unsafe void RestartWait() - { - long timeout; - long* pTimeout = null; // Null indicates infinite timeout - - if (_millisecondsTimeout != Timeout.UnsignedInfinite) - { - timeout = -10000L * _millisecondsTimeout; - pTimeout = &timeout; - } - - // We can use DangerousGetHandle because of DangerousAddRef in the constructor - Interop.Kernel32.SetThreadpoolWait(_tpWait, _waitHandle.DangerousGetHandle(), (IntPtr)pTimeout); - } - - public bool Unregister(WaitHandle waitObject) - { - // Hold the lock during the synchronous part of Unregister (as in CoreCLR) - using (LockHolder.Hold(_lock)) - { - if (!_unregistering) - { - // Ensure callbacks will not call SetThreadpoolWait anymore - _unregistering = true; - - // Cease queueing more callbacks - Interop.Kernel32.SetThreadpoolWait(_tpWait, IntPtr.Zero, IntPtr.Zero); - - // Should we wait for callbacks synchronously? Note that we treat the zero handle as the asynchronous case. - SafeWaitHandle? safeWaitHandle = waitObject?.SafeWaitHandle; - bool blocking = ((safeWaitHandle != null) && (safeWaitHandle.DangerousGetHandle() == new IntPtr(-1))); - - if (blocking) - { - FinishUnregistering(); - } - else - { - // Wait for callbacks and dispose resources asynchronously - ThreadPool.QueueUserWorkItem(FinishUnregisteringAsync, safeWaitHandle); - } - - return true; - } - } - return false; - } - - private void FinishUnregistering() - { - Debug.Assert(_unregistering); - - // Wait for outstanding wait callbacks to complete - Interop.Kernel32.WaitForThreadpoolWaitCallbacks(_tpWait, false); - - // Now it is safe to dispose resources - Interop.Kernel32.CloseThreadpoolWait(_tpWait); - _tpWait = IntPtr.Zero; - - if (_gcHandle.IsAllocated) - { - _gcHandle.Free(); - } - - Debug.Assert(_waitHandle != null); - _waitHandle.DangerousRelease(); - _waitHandle = null; - - GC.SuppressFinalize(this); - } - - private void FinishUnregisteringAsync(object? waitObject) - { - FinishUnregistering(); - - // Signal the provided wait object - SafeWaitHandle? safeWaitHandle = (SafeWaitHandle?)waitObject; - - if ((safeWaitHandle != null) && !safeWaitHandle.IsInvalid) - { - Interop.Kernel32.SetEvent(safeWaitHandle); - } - } - - ~RegisteredWaitHandle() - { - Debug.Assert(ThreadPool.UseWindowsThreadPool); - // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC - Debug.Assert(!_gcHandle.IsAllocated); - - // If this object gets resurrected and another thread calls Unregister, that creates a race condition. - // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. - // The _lock may be null in case of OOM in the constructor. - if ((_lock != null) && _lock.TryAcquire(0)) - { - try - { - if (!_unregistering) - { - _unregistering = true; - - if (_tpWait != IntPtr.Zero) - { - // There must be no in-flight callbacks; just dispose resources - Interop.Kernel32.CloseThreadpoolWait(_tpWait); - _tpWait = IntPtr.Zero; - } - - if (_waitHandle != null) - { - _waitHandle.DangerousRelease(); - _waitHandle = null; - } - } - } - finally - { - _lock.Release(); - } - } - } - } public static partial class ThreadPool { @@ -277,7 +53,7 @@ public static partial class ThreadPool internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int _ /*currentTimeMs*/) => WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, _); + internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int _ /*currentTimeMs*/) => WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, _); internal static bool NotifyThreadBlocked() => WindowsThreadPool.NotifyThreadBlocked(); internal static void NotifyThreadUnblocked() => WindowsThreadPool.NotifyThreadUnblocked(); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs new file mode 100644 index 0000000000000..a460216520458 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + // + // Windows-specific implementation of ThreadPool + // +#if !FEATURE_WASM_THREADS + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif + public sealed partial class RegisteredWaitHandle : MarshalByRefObject + { + private readonly object _lock; + private SafeWaitHandle _waitHandle; + private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; + private readonly uint _millisecondsTimeout; + private bool _repeating; + private bool _unregistering; + + // Handle to this object to keep it alive + private GCHandle _gcHandle; + + // Pointer to the TP_WAIT structure + private IntPtr _tpWait; + + internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + uint millisecondsTimeout, bool repeating) + { + if (!ThreadPool.UseWindowsThreadPool) + { + GC.SuppressFinalize(this); + } + _lock = new Lock(); + + // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) + waitHandle.DangerousAddRef(); + _waitHandle = waitHandle; + + _callbackHelper = callbackHelper; + _millisecondsTimeout = millisecondsTimeout; + _repeating = repeating; + + // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked + _gcHandle = GCHandle.Alloc(this); + + _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); + + if (_tpWait == IntPtr.Zero) + { + _gcHandle.Free(); + throw new OutOfMemoryException(); + } + } + + [UnmanagedCallersOnly] + internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) => + RegisteredWaitCallbackCore(instance, context, wait, waitResult); + + internal unsafe void RestartWait() => RestartWaitCore(); + + public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); + + ~RegisteredWaitHandle() + { + Debug.Assert(ThreadPool.UseWindowsThreadPool); + // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC + Debug.Assert(!_gcHandle.IsAllocated); + + // If this object gets resurrected and another thread calls Unregister, that creates a race condition. + // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. + // The _lock may be null in case of OOM in the constructor. + if ((_lock != null) && _lock.TryAcquire(0)) + { + try + { + if (!_unregistering) + { + _unregistering = true; + + if (_tpWait != IntPtr.Zero) + { + // There must be no in-flight callbacks; just dispose resources + Interop.Kernel32.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + } + + if (_waitHandle != null) + { + _waitHandle.DangerousRelease(); + _waitHandle = null; + } + } + } + finally + { + _lock.Release(); + } + } + } + } + +} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index c5cde5124ca12..b2ddd5a9bbba5 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2507,8 +2507,9 @@ - + + @@ -2577,4 +2578,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs index 50cd9f2bb36a4..74c0d37c0360f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs @@ -15,7 +15,6 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - private static AutoResetEvent RentEvent() => Interlocked.Exchange(ref s_cachedEvent, null) ?? new AutoResetEvent(false); @@ -34,7 +33,7 @@ internal void RestartTimeout() TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs; } - private bool UnregisterCore(WaitHandle waitObject) + private bool UnregisterPortableCore(WaitHandle waitObject) { // The registered wait handle must have been registered by this time, otherwise the instance is not handed out to // the caller of the public variants of RegisterWaitForSingleObject diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index eb2ac734e6905..4355edafd3877 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -91,6 +91,6 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb /// internal PortableThreadPool.WaitThread? WaitThread { get; set; } - public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); + public bool Unregister(WaitHandle waitObject) => UnregisterPortableCore(waitObject); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs new file mode 100644 index 0000000000000..129a6a332d8ea --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + // + // Windows-specific implementation of ThreadPool + // +#if !FEATURE_WASM_THREADS + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif + public sealed partial class RegisteredWaitHandle : MarshalByRefObject + { + [UnmanagedCallersOnly] + private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) + { + var wrapper = ThreadPoolCallbackWrapper.Enter(); + GCHandle handle = (GCHandle)context; + RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; + Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); + + bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); + registeredWaitHandle.PerformCallback(timedOut); + ThreadPool.IncrementCompletedWorkItemCount(); + wrapper.Exit(); + } + + private void PerformCallbackCore(bool timedOut) + { + bool lockAcquired; + var spinner = new SpinWait(); + + // Prevent the race condition with Unregister and the previous PerformCallback call, which may still be + // holding the _lock. + while (!(lockAcquired = _lock.TryAcquire(0)) && !Volatile.Read(ref _unregistering)) + { + spinner.SpinOnce(); + } + + // If another thread is running Unregister, no need to restart the timer or clean up + if (lockAcquired) + { + try + { + if (!_unregistering) + { + if (_repeating) + { + // Allow this wait to fire again. Restart the timer before executing the callback. + RestartWait(); + } + else + { + // This wait will not be fired again. Free the GC handle to allow the GC to collect this object. + Debug.Assert(_gcHandle.IsAllocated); + _gcHandle.Free(); + } + } + } + finally + { + _lock.Release(); + } + } + + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper, timedOut); + } + + private unsafe void RestartWaitCore() + { + long timeout; + long* pTimeout = null; // Null indicates infinite timeout + + if (_millisecondsTimeout != Timeout.UnsignedInfinite) + { + timeout = -10000L * _millisecondsTimeout; + pTimeout = &timeout; + } + + // We can use DangerousGetHandle because of DangerousAddRef in the constructor + Interop.Kernel32.SetThreadpoolWait(_tpWait, _waitHandle.DangerousGetHandle(), (IntPtr)pTimeout); + } + + private bool UnregisterCore(WaitHandle waitObject) + { + // Hold the lock during the synchronous part of Unregister (as in CoreCLR) + using (LockHolder.Hold(_lock)) + { + if (!_unregistering) + { + // Ensure callbacks will not call SetThreadpoolWait anymore + _unregistering = true; + + // Cease queueing more callbacks + Interop.Kernel32.SetThreadpoolWait(_tpWait, IntPtr.Zero, IntPtr.Zero); + + // Should we wait for callbacks synchronously? Note that we treat the zero handle as the asynchronous case. + SafeWaitHandle? safeWaitHandle = waitObject?.SafeWaitHandle; + bool blocking = ((safeWaitHandle != null) && (safeWaitHandle.DangerousGetHandle() == new IntPtr(-1))); + + if (blocking) + { + FinishUnregistering(); + } + else + { + // Wait for callbacks and dispose resources asynchronously + ThreadPool.QueueUserWorkItem(FinishUnregisteringAsync, safeWaitHandle); + } + + return true; + } + } + return false; + } + + private void FinishUnregistering() + { + Debug.Assert(_unregistering); + + // Wait for outstanding wait callbacks to complete + Interop.Kernel32.WaitForThreadpoolWaitCallbacks(_tpWait, false); + + // Now it is safe to dispose resources + Interop.Kernel32.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + + if (_gcHandle.IsAllocated) + { + _gcHandle.Free(); + } + + Debug.Assert(_waitHandle != null); + _waitHandle.DangerousRelease(); + _waitHandle = null; + + GC.SuppressFinalize(this); + } + + private void FinishUnregisteringAsync(object? waitObject) + { + FinishUnregistering(); + + // Signal the provided wait object + SafeWaitHandle? safeWaitHandle = (SafeWaitHandle?)waitObject; + + if ((safeWaitHandle != null) && !safeWaitHandle.IsInvalid) + { + Interop.Kernel32.SetEvent(safeWaitHandle); + } + } + } + +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs new file mode 100644 index 0000000000000..7c75403a0e6c6 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs @@ -0,0 +1,489 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// PR-Comment: This implementation comes from Thread.NativeAot.Windows.cs, +// not sure if it should be called Thread.WindowsThreadPool.cs or Thread.NativeAot.WindowsThreadPool.cs or something else + +namespace System.Threading +{ + using OSThreadPriority = Interop.Kernel32.ThreadPriority; + + public sealed partial class Thread + { + partial void PlatformSpecificInitialize(); + + // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start + private void PlatformSpecificInitializeExistingThread() + { + _osHandle = GetOSHandleForCurrentThread(); + } + + private static SafeWaitHandle GetOSHandleForCurrentThread() + { + IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); + IntPtr currentThreadHandle = Interop.Kernel32.GetCurrentThread(); + SafeWaitHandle threadHandle; + + if (Interop.Kernel32.DuplicateHandle(currentProcHandle, currentThreadHandle, currentProcHandle, + out threadHandle, 0, false, Interop.Kernel32.DUPLICATE_SAME_ACCESS)) + { + return threadHandle; + } + + // Throw an ApplicationException for compatibility with CoreCLR. First save the error code. + int errorCode = Marshal.GetLastWin32Error(); + var ex = new ApplicationException(); + ex.HResult = errorCode; + throw ex; + } + + private static ThreadPriority MapFromOSPriority(OSThreadPriority priority) + { + if (priority <= OSThreadPriority.Lowest) + { + // OS thread priorities in the [Idle,Lowest] range are mapped to ThreadPriority.Lowest + return ThreadPriority.Lowest; + } + switch (priority) + { + case OSThreadPriority.BelowNormal: + return ThreadPriority.BelowNormal; + + case OSThreadPriority.Normal: + return ThreadPriority.Normal; + + case OSThreadPriority.AboveNormal: + return ThreadPriority.AboveNormal; + + case OSThreadPriority.ErrorReturn: + Debug.Fail("GetThreadPriority failed"); + return ThreadPriority.Normal; + } + // Handle OSThreadPriority.ErrorReturn value before this check! + if (priority >= OSThreadPriority.Highest) + { + // OS thread priorities in the [Highest,TimeCritical] range are mapped to ThreadPriority.Highest + return ThreadPriority.Highest; + } + Debug.Fail("Unreachable"); + return ThreadPriority.Normal; + } + + private static OSThreadPriority MapToOSPriority(ThreadPriority priority) + { + switch (priority) + { + case ThreadPriority.Lowest: + return OSThreadPriority.Lowest; + + case ThreadPriority.BelowNormal: + return OSThreadPriority.BelowNormal; + + case ThreadPriority.Normal: + return OSThreadPriority.Normal; + + case ThreadPriority.AboveNormal: + return OSThreadPriority.AboveNormal; + + case ThreadPriority.Highest: + return OSThreadPriority.Highest; + + default: + Debug.Fail("Unreachable"); + return OSThreadPriority.Normal; + } + } + + private ThreadPriority GetPriorityLive() + { + Debug.Assert(!_osHandle.IsInvalid); + return MapFromOSPriority(Interop.Kernel32.GetThreadPriority(_osHandle)); + } + + private bool SetPriorityLive(ThreadPriority priority) + { + Debug.Assert(!_osHandle.IsInvalid); + return Interop.Kernel32.SetThreadPriority(_osHandle, (int)MapToOSPriority(priority)); + } + + [UnmanagedCallersOnly] + private static void OnThreadExit() + { + Thread? currentThread = t_currentThread; + if (currentThread != null) + { + StopThread(currentThread); + } + } + + private bool JoinInternal(int millisecondsTimeout) + { + // This method assumes the thread has been started + Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); + SafeWaitHandle waitHandle = _osHandle; + + // If an OS thread is terminated and its Thread object is resurrected, _osHandle may be finalized and closed + if (waitHandle.IsClosed) + { + return true; + } + + // Handle race condition with the finalizer + try + { + waitHandle.DangerousAddRef(); + } + catch (ObjectDisposedException) + { + return true; + } + + try + { + int result; + + if (millisecondsTimeout == 0) + { + result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); + } + else + { + result = WaitHandle.WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout); + } + + return result == (int)Interop.Kernel32.WAIT_OBJECT_0; + } + finally + { + waitHandle.DangerousRelease(); + } + } + + private unsafe bool CreateThread(GCHandle thisThreadHandle) + { + const int AllocationGranularity = 0x10000; // 64 KiB + + int stackSize = _startHelper._maxStackSize; + if ((0 < stackSize) && (stackSize < AllocationGranularity)) + { + // If StackSizeParamIsAReservation flag is set and the reserve size specified by CreateThread's + // dwStackSize parameter is less than or equal to the initially committed stack size specified in + // the executable header, the reserve size will be set to the initially committed size rounded up + // to the nearest multiple of 1 MiB. In all cases the reserve size is rounded up to the nearest + // multiple of the system's allocation granularity (typically 64 KiB). + // + // To prevent overreservation of stack memory for small stackSize values, we increase stackSize to + // the allocation granularity. We assume that the SizeOfStackCommit field of IMAGE_OPTIONAL_HEADER + // is strictly smaller than the allocation granularity (the field's default value is 4 KiB); + // otherwise, at least 1 MiB of memory will be reserved. Note that the desktop CLR increases + // stackSize to 256 KiB if it is smaller than that. + stackSize = AllocationGranularity; + } + + _osHandle = Interop.Kernel32.CreateThread(IntPtr.Zero, (IntPtr)stackSize, + &ThreadEntryPoint, (IntPtr)thisThreadHandle, + Interop.Kernel32.CREATE_SUSPENDED | Interop.Kernel32.STACK_SIZE_PARAM_IS_A_RESERVATION, + out _); + + if (_osHandle.IsInvalid) + { + return false; + } + + // CoreCLR ignores OS errors while setting the priority, so do we + SetPriorityLive(_priority); + + Interop.Kernel32.ResumeThread(_osHandle); + return true; + } + + /// + /// This is an entry point for managed threads created by application + /// + [UnmanagedCallersOnly] + private static uint ThreadEntryPoint(IntPtr parameter) + { + StartThread(parameter); + return 0; + } + + private ApartmentState GetApartmentStateCore() + { + if (this != CurrentThread) + { + if (HasStarted()) + throw new ThreadStateException(); + return _initialApartmentState; + } + + switch (GetCurrentApartmentType()) + { + case ApartmentType.STA: + return ApartmentState.STA; + case ApartmentType.MTA: + return ApartmentState.MTA; + default: + return ApartmentState.Unknown; + } + } + + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + ApartmentState retState; + + if (this != CurrentThread) + { + using (LockHolder.Hold(_lock)) + { + if (HasStarted()) + throw new ThreadStateException(); + + // Compat: Disallow resetting the initial apartment state + if (_initialApartmentState == ApartmentState.Unknown) + _initialApartmentState = state; + + retState = _initialApartmentState; + } + } + else + { + + if ((t_comState & ComState.Locked) == 0) + { + if (state != ApartmentState.Unknown) + { + InitializeCom(state); + } + else + { + UninitializeCom(); + } + } + + // Clear the cache and check whether new state matches the desired state + t_apartmentType = ApartmentType.Unknown; + + retState = GetApartmentState(); + } + + if (retState != state) + { + if (throwOnError) + { + string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); + throw new InvalidOperationException(msg); + } + + return false; + } + + return true; + } + + private void InitializeComOnNewThread() + { + InitializeCom(_initialApartmentState); + } + + private static void InitializeComForFinalizerThreadCore() + { + InitializeCom(); + + // Prevent re-initialization of COM model on finalizer thread + t_comState |= ComState.Locked; + + s_comInitializedOnFinalizerThread = true; + } + + private static void InitializeComForThreadPoolThread() + { + // Initialized COM - take advantage of implicit MTA initialized by the finalizer thread + SpinWait sw = new SpinWait(); + while (!s_comInitializedOnFinalizerThread) + { + RuntimeImports.RhInitializeFinalizerThread(); + sw.SpinOnce(0); + } + + // Prevent re-initialization of COM model on threadpool threads + t_comState |= ComState.Locked; + } + + private static void InitializeCom(ApartmentState state = ApartmentState.MTA) + { + if ((t_comState & ComState.InitializedByUs) != 0) + return; + +#if ENABLE_WINRT + int hr = Interop.WinRT.RoInitialize( + (state == ApartmentState.STA) ? Interop.WinRT.RO_INIT_SINGLETHREADED + : Interop.WinRT.RO_INIT_MULTITHREADED); +#else + int hr = Interop.Ole32.CoInitializeEx(IntPtr.Zero, + (state == ApartmentState.STA) ? Interop.Ole32.COINIT_APARTMENTTHREADED + : Interop.Ole32.COINIT_MULTITHREADED); +#endif + if (hr < 0) + { + // RPC_E_CHANGED_MODE indicates this thread has been already initialized with a different + // concurrency model. We stay away and let whoever else initialized the COM to be in control. + if (hr == HResults.RPC_E_CHANGED_MODE) + return; + + // CoInitializeEx returns E_NOTIMPL on Windows Nano Server for STA + if (hr == HResults.E_NOTIMPL) + throw new PlatformNotSupportedException(); + + throw new OutOfMemoryException(); + } + + t_comState |= ComState.InitializedByUs; + + // If the thread has already been CoInitialized to the proper mode, then + // we don't want to leave an outstanding CoInit so we CoUninit. + if (hr > 0) + UninitializeCom(); + } + + private static void UninitializeCom() + { + if ((t_comState & ComState.InitializedByUs) == 0) + return; + +#if ENABLE_WINRT + Interop.WinRT.RoUninitialize(); +#else + Interop.Ole32.CoUninitialize(); +#endif + + t_comState &= ~ComState.InitializedByUs; + } + + // TODO: https://github.com/dotnet/runtime/issues/22161 + public void DisableComObjectEagerCleanupCore() { } + + private static Thread InitializeExistingThreadPoolThread() + { + ThreadPool.InitializeForThreadPoolThread(); + + InitializeComForThreadPoolThread(); + + Thread thread = CurrentThread; + thread.SetThreadStateBit(ThreadPoolThread); + return thread; + } + + // Use ThreadPoolCallbackWrapper instead of calling this function directly + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Thread EnsureThreadPoolThreadInitializedCore() + { + Thread? thread = t_currentThread; + if (thread != null && thread.GetThreadStateBit(ThreadPoolThread)) + return thread; + return InitializeExistingThreadPoolThread(); + } + + private void InterruptCore() { throw new PlatformNotSupportedException(); } + + // + // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. + // This should be used by code that's expected to be called inside the STA message pump, so that it won't + // reenter itself. In an ASTA, this should only be the CCW implementations of IUnknown and IInspectable. + // + private static void SuppressReentrantWaitsCore() + { + t_reentrantWaitSuppressionCount++; + } + + private static void RestoreReentrantWaitsCore() + { + Debug.Assert(t_reentrantWaitSuppressionCount > 0); + t_reentrantWaitSuppressionCount--; + } + + private static bool ReentrantWaitsEnabledCore => + GetCurrentApartmentType() == ApartmentType.STA && t_reentrantWaitSuppressionCount == 0; + + private static ApartmentType GetCurrentApartmentTypeCore() + { + ApartmentType currentThreadType = t_apartmentType; + if (currentThreadType != ApartmentType.Unknown) + return currentThreadType; + + Interop.APTTYPE aptType; + Interop.APTTYPEQUALIFIER aptTypeQualifier; + int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier); + + ApartmentType type = ApartmentType.Unknown; + + switch (result) + { + case HResults.CO_E_NOTINITIALIZED: + type = ApartmentType.None; + break; + + case HResults.S_OK: + switch (aptType) + { + case Interop.APTTYPE.APTTYPE_STA: + case Interop.APTTYPE.APTTYPE_MAINSTA: + type = ApartmentType.STA; + break; + + case Interop.APTTYPE.APTTYPE_MTA: + type = ApartmentType.MTA; + break; + + case Interop.APTTYPE.APTTYPE_NA: + switch (aptTypeQualifier) + { + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: + type = ApartmentType.MTA; + break; + + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: + type = ApartmentType.STA; + break; + + default: + Debug.Fail("NA apartment without NA qualifier"); + break; + } + break; + } + break; + + default: + Debug.Fail("bad return from CoGetApartmentType"); + break; + } + + if (type != ApartmentType.Unknown) + t_apartmentType = type; + return type; + } + + internal enum ApartmentType : byte + { + Unknown = 0, + None, + STA, + MTA + } + + [Flags] + internal enum ComState : byte + { + InitializedByUs = 1, + Locked = 2, + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index edc3819704eef..85eec7b0fa701 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -16,7 +16,6 @@ namespace System.Threading /// public sealed partial class ThreadPoolBoundHandle : IDisposable { - private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { ArgumentNullException.ThrowIfNull(handle); @@ -30,7 +29,6 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); - private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 420d26d917a21..c41f7e37280d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1032,7 +1032,7 @@ internal sealed class ThreadPoolWorkQueueThreadLocals public readonly ThreadPoolWorkQueue workQueue; public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue; public readonly Thread currentThread; - public readonly object? threadLocalCompletionCountObject; + public readonly object threadLocalCompletionCountObject; public readonly Random.XoshiroImpl random = new Random.XoshiroImpl(); public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) @@ -1042,7 +1042,7 @@ public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue(); ThreadPoolWorkQueue.WorkStealingQueueList.Add(workStealingQueue); currentThread = Thread.CurrentThread; - threadLocalCompletionCountObject = ThreadPool.GetOrCreateThreadLocalCompletionCountObject(); + threadLocalCompletionCountObject = ThreadPool.GetOrCreateThreadLocalCompletionCountObject()!; } public void TransferLocalWork() diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 716e2063ba960..9ce37d35008df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -13,7 +13,7 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // - public static partial class WindowsThreadPool + public static class WindowsThreadPool { internal const bool IsWorkerTrackingEnabledInConfig = false; @@ -43,7 +43,7 @@ private class ThreadCountHolder } [ThreadStatic] - private static ThreadCountHolder t_threadCountHolder; + private static ThreadCountHolder? t_threadCountHolder; private static int s_threadCount; [StructLayout(LayoutKind.Sequential)] @@ -137,7 +137,7 @@ public static void GetAvailableThreads(out int workerThreads, out int completion internal static void NotifyWorkItemProgress() => IncrementCompletedWorkItemCount(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int _ /*currentTimeMs*/) + internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int _ /*currentTimeMs*/) { ThreadInt64PersistentCounter.Increment(threadLocalCompletionCountObject); return true; @@ -206,7 +206,7 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp // OS doesn't signal handle, so do it here overlapped->InternalLow = (IntPtr)0; // Both types of callbacks are executed on the same thread pool - return UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false); + return ThreadPool.UnsafeQueueUserWorkItem(NativeOverlappedCallback, (nint)overlapped, preferLocal: false); } [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] From 50d81552a3dcb9aff1586af7b9f98813f4406d11 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Mar 2023 21:42:20 -0700 Subject: [PATCH 009/168] Fix some build errors --- .../System.Private.CoreLib.csproj | 5 +++-- .../RegisteredWaitHandle.CoreCLR.Windows.cs | 16 +++++++------- .../System/Threading/Thread.CoreCLR.Core.cs | 8 ++++++- .../src/System/Threading/Thread.CoreCLR.cs | 21 ++++++++++++++++++- .../Threading/Thread.NativeAot.Windows.cs | 19 ++--------------- .../System.Private.CoreLib.Shared.projitems | 1 + .../PortableThreadPool.WaitThread.cs | 4 ++-- .../RegisteredWaitHandle.Portable.Core.cs | 2 +- .../RegisteredWaitHandle.WindowsThreadPool.cs | 1 - .../Threading/Thread.WindowsThreadPool.cs | 7 +++---- 10 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index c5437706518d7..27076449cb3af 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -232,7 +232,8 @@ - + + @@ -292,7 +293,7 @@ - + Interop\Windows\Kernel32\Interop.ThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs index 472b3158d6a70..dca0013bf6efd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs @@ -15,9 +15,9 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - private readonly object _lock; - private SafeWaitHandle _waitHandle; - private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; + private readonly object? _lock; + private SafeWaitHandle? _waitHandle; + private readonly _ThreadPoolWaitOrTimerCallback? _callbackHelper; private readonly uint _millisecondsTimeout; private bool _repeating; private bool _unregistering; @@ -35,7 +35,7 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO { GC.SuppressFinalize(this); } - _lock = new Lock(); + _lock = new object(); // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) waitHandle.DangerousAddRef(); @@ -78,12 +78,12 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb /// /// The callback to execute when the wait on either times out or completes. /// - internal _ThreadPoolWaitOrTimerCallback Callback { get; } + internal _ThreadPoolWaitOrTimerCallback? Callback { get; } // PR-Comment: must be nullable as it has one constructor which exits without initializing /// /// The that was registered. /// - internal SafeWaitHandle Handle { get; } + internal SafeWaitHandle? Handle { get; } // PR-Comment: must be nullable as it has one constructor which exits without initializing /// /// The time this handle times out at in ms. @@ -150,7 +150,7 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int // If this object gets resurrected and another thread calls Unregister, that creates a race condition. // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. // The _lock may be null in case of OOM in the constructor. - if ((_lock != null) && _lock.TryAcquire(0)) + if ((_lock != null) && Monitor.TryEnter(_lock)) { try { @@ -174,7 +174,7 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int } finally { - _lock.Release(); + Monitor.Exit(_lock); } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs index c410887dfdfb5..e1b2df3217f30 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs @@ -115,7 +115,7 @@ private void StartCallback() [MethodImpl(MethodImplOptions.InternalCall)] private extern int GetThreadStateNative(); - private ApartmentState GetApartmentStateCore() => + private ApartmentState GetApartmentStatePortableCore() => #if FEATURE_COMINTEROP_APARTMENT_SUPPORT (ApartmentState)GetApartmentStateNative(); #else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT @@ -176,6 +176,12 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO } #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void InterruptCore(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern bool JoinCore(int millisecondsTimeout); + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ResetThreadPoolThreadCore() { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 7376080d49530..2afdd128767bc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -135,7 +135,7 @@ public ThreadPriority Priority /// public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); - public ApartmentState GetApartmentState() => GetApartmentStateCore(); + public ApartmentState GetApartmentState() => GetApartmentStatePortableCore(); #if FEATURE_COMINTEROP [MethodImpl(MethodImplOptions.InternalCall)] @@ -146,6 +146,25 @@ public void DisableComObjectEagerCleanup() } #endif // FEATURE_COMINTEROP + /// + /// Interrupts a thread that is inside a Wait(), Sleep() or Join(). If that + /// thread is not currently blocked in that manner, it will be interrupted + /// when it next begins to block. + /// + public extern void Interrupt() => InterruptCore(); + + /// + /// Waits for the thread to die or for timeout milliseconds to elapse. + /// + /// + /// Returns true if the thread died, or false if the wait timed out. If + /// -1 is given as the parameter, no timeout will occur. + /// + /// if timeout < -1 (Timeout.Infinite) + /// if the thread is interrupted while waiting + /// if the thread has not been started yet + public extern bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); + /// /// Max value to be passed into for optimal delaying. This value is normalized to be /// appropriate for the processor. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index 627f23e62f18d..6b38dc85200c3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -7,6 +7,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +// PR-Comment: This would work as a Win32Thread.Windows.cs + namespace System.Threading { using OSThreadPriority = Interop.Kernel32.ThreadPriority; @@ -50,23 +52,6 @@ public sealed partial class Thread internal static void RestoreReentrantWaits() => RestoreReentrantWaitsCore(); - internal static bool ReentrantWaitsEnabled => ReentrantWaitsEnabledCore(); - internal static ApartmentType GetCurrentApartmentType() => GetCurrentApartmentTypeCore(); - - internal enum ApartmentType : byte - { - Unknown = 0, - None, - STA, - MTA - } - - [Flags] - internal enum ComState : byte - { - InitializedByUs = 1, - Locked = 2, - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index b2ddd5a9bbba5..6dc6f5a835aad 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2031,6 +2031,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs index 34f7b3e1f54bf..af20c2c2d4ae4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs @@ -347,7 +347,7 @@ private int ProcessRemovals() _numUserWaits = numUserWaits - 1; _pendingRemoves[i] = null; - waitHandleToRemove.Handle.DangerousRelease(); + waitHandleToRemove.Handle!.DangerousRelease(); // PR-Comment: Make Handle non-nullable if all instances of .Handle are used without checking for null } _numPendingRemoves = 0; @@ -401,7 +401,7 @@ public bool RegisterWaitHandle(RegisteredWaitHandle handle) } bool success = false; - handle.Handle.DangerousAddRef(ref success); + handle.Handle!.DangerousAddRef(ref success); // PR-Comment: Make Handle non-nullable if all instances of .Handle are used without checking for null Debug.Assert(success); _registeredWaits[_numUserWaits] = handle; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs index 74c0d37c0360f..813a254d41961 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs @@ -143,7 +143,7 @@ internal void PerformCallback(bool timedOut) } #endif - _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback, timedOut); + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback!, timedOut); CompleteCallbackRequest(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 129a6a332d8ea..33763c770c4a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -18,7 +18,6 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - [UnmanagedCallersOnly] private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { var wrapper = ThreadPoolCallbackWrapper.Enter(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs index 7c75403a0e6c6..942f929b14be0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs @@ -233,6 +233,7 @@ private ApartmentState GetApartmentStateCore() } } + /* private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) { ApartmentState retState; @@ -285,6 +286,7 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) return true; } + */ private void InitializeComOnNewThread() { @@ -365,9 +367,6 @@ private static void UninitializeCom() t_comState &= ~ComState.InitializedByUs; } - // TODO: https://github.com/dotnet/runtime/issues/22161 - public void DisableComObjectEagerCleanupCore() { } - private static Thread InitializeExistingThreadPoolThread() { ThreadPool.InitializeForThreadPoolThread(); @@ -407,7 +406,7 @@ private static void RestoreReentrantWaitsCore() t_reentrantWaitSuppressionCount--; } - private static bool ReentrantWaitsEnabledCore => + private static bool ReentrantWaitsEnabled => GetCurrentApartmentType() == ApartmentType.STA && t_reentrantWaitSuppressionCount == 0; private static ApartmentType GetCurrentApartmentTypeCore() From 911f41585e8a1296225f4beb4aac712d3eb620ae Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Mar 2023 22:11:37 -0700 Subject: [PATCH 010/168] Split Thread.NativeAot.cs and create Thread.CoreCLR.Windows.cs --- .../System/Threading/Thread.CoreCLR.Core.cs | 116 +++ .../Threading/Thread.CoreCLR.Windows.cs | 96 +++ .../src/System/Threading/Thread.CoreCLR.cs | 114 --- .../Threading/Thread.NativeAot.Windows.cs | 464 +++++++++++- .../src/System/Threading/Thread.NativeAot.cs | 479 +----------- .../Threading/Thread.WindowsThreadPool.cs | 711 +++++++++--------- 6 files changed, 1035 insertions(+), 945 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs index e1b2df3217f30..ddbb21e72cb6c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs @@ -12,9 +12,125 @@ namespace System.Threading { + internal readonly struct ThreadHandle + { + private readonly IntPtr _ptr; + + internal ThreadHandle(IntPtr pThread) + { + _ptr = pThread; + } + } public sealed partial class Thread { + /*========================================================================= + ** Data accessed from managed code that needs to be defined in + ** ThreadBaseObject to maintain alignment between the two classes. + ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h + =========================================================================*/ + internal ExecutionContext? _executionContext; // this call context follows the logical thread + internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext + + private string? _name; + private StartHelper? _startHelper; + + /*========================================================================= + ** The base implementation of Thread is all native. The following fields + ** should never be used in the C# code. They are here to define the proper + ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS + ** YOU MODIFY ThreadBaseObject in vm\object.h + =========================================================================*/ +#pragma warning disable CA1823, 169 // These fields are not used from managed. + // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit + // fields on 64-bit platforms, where they will be sorted together. + + private IntPtr _DONT_USE_InternalThread; // Pointer + private int _priority; // INT32 + + // The following field is required for interop with the VS Debugger + // Prior to making any changes to this field, please reach out to the VS Debugger + // team to make sure that your changes are not going to prevent the debugger + // from working. + private int _managedThreadId; // INT32 +#pragma warning restore CA1823, 169 + + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background + // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, + // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. + private bool _mayNeedResetForThreadPool; + + public extern int ManagedThreadId + { + [Intrinsic] + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// Returns true if the thread has been started and is not dead. + public extern bool IsAlive + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Return whether or not this thread is a background thread. Background + /// threads do not affect when the Execution Engine shuts down. + /// + public bool IsBackground + { + get => IsBackgroundNative(); + set + { + SetBackgroundNative(value); + if (!value) + { + _mayNeedResetForThreadPool = true; + } + } + } + + /// Returns true if the thread is a threadpool thread. + public extern bool IsThreadPoolThread + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + [MethodImpl(MethodImplOptions.InternalCall)] + internal set; + } + + /// Returns the priority of the thread. + public ThreadPriority Priority + { + get => (ThreadPriority)GetPriorityNative(); + set + { + SetPriorityNative((int)value); + if (value != ThreadPriority.Normal) + { + _mayNeedResetForThreadPool = true; + } + } + } + + /// + /// Return the thread state as a consistent set of bits. This is more + /// general then IsAlive or IsBackground. + /// + public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); + + + /// + /// Max value to be passed into for optimal delaying. This value is normalized to be + /// appropriate for the processor. + /// + internal static int OptimalMaxSpinWaitsPerSpinIteration + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + /// Returns handle for interop with EE. The handle is guaranteed to be non-null. private ThreadHandle GetNativeHandleCore() { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs new file mode 100644 index 0000000000000..92d46b60c72d3 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Globalization; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Versioning; + +namespace System.Threading +{ + public sealed partial class Thread + { + + private Thread() + { + if (ThreadPool.UseWindowsThreadPool) + { + _managedThreadId = System.Threading.ManagedThreadId.GetCurrentThreadId(); + + PlatformSpecificInitialize(); + RegisterThreadExitCallback(); + } + } + + internal void SetWaitSleepJoinState() => SetWaitSleepJoinStateCore(); + + internal void ClearWaitSleepJoinState() => ClearWaitSleepJoinStateCrore(); + + public bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); + + internal static void SpinWaitInternal(int iterations) => SpinWaitInternalCore(iterations); + + public static void SpinWait(int iterations) => SpinWaitCore(); + + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. + public static bool Yield() => YieldCore(); + + internal static void IncrementRunningForeground() => IncrementRunningForegroundCore(); + + internal static void DecrementRunningForeground() => DecrementRunningForegroundCore(); + + internal static void WaitForForegroundThreads() => WaitForForegroundThreadsCore(); + + /// Returns handle for interop with EE. The handle is guaranteed to be non-null. + internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); + + public static void SpinWait(int iterations) => SpinWaitCore(iterations); + + public static bool Yield() => YieldCore(); + + /// Clean up the thread when it goes away. + ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. + + partial void ThreadNameChanged(string? value) + { + InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); + } + + public ApartmentState GetApartmentState() => GetApartmentStatePortableCore(); + +#if FEATURE_COMINTEROP + [MethodImpl(MethodImplOptions.InternalCall)] + public extern void DisableComObjectEagerCleanup(); +#else // !FEATURE_COMINTEROP + public void DisableComObjectEagerCleanup() + { + } +#endif // FEATURE_COMINTEROP + + /// + /// Interrupts a thread that is inside a Wait(), Sleep() or Join(). If that + /// thread is not currently blocked in that manner, it will be interrupted + /// when it next begins to block. + /// + public void Interrupt() => InterruptCore(); + + /// + /// Waits for the thread to die or for timeout milliseconds to elapse. + /// + /// + /// Returns true if the thread died, or false if the wait timed out. If + /// -1 is given as the parameter, no timeout will occur. + /// + /// if timeout < -1 (Timeout.Infinite) + /// if the thread is interrupted while waiting + /// if the thread has not been started yet + public bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 2afdd128767bc..3a1c969b8371b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -10,63 +10,12 @@ namespace System.Threading { - internal readonly struct ThreadHandle - { - private readonly IntPtr _ptr; - - internal ThreadHandle(IntPtr pThread) - { - _ptr = pThread; - } - } public sealed partial class Thread { - /*========================================================================= - ** Data accessed from managed code that needs to be defined in - ** ThreadBaseObject to maintain alignment between the two classes. - ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h - =========================================================================*/ - internal ExecutionContext? _executionContext; // this call context follows the logical thread - internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext - - private string? _name; - private StartHelper? _startHelper; - - /*========================================================================= - ** The base implementation of Thread is all native. The following fields - ** should never be used in the C# code. They are here to define the proper - ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS - ** YOU MODIFY ThreadBaseObject in vm\object.h - =========================================================================*/ -#pragma warning disable CA1823, 169 // These fields are not used from managed. - // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit - // fields on 64-bit platforms, where they will be sorted together. - - private IntPtr _DONT_USE_InternalThread; // Pointer - private int _priority; // INT32 - - // The following field is required for interop with the VS Debugger - // Prior to making any changes to this field, please reach out to the VS Debugger - // team to make sure that your changes are not going to prevent the debugger - // from working. - private int _managedThreadId; // INT32 -#pragma warning restore CA1823, 169 - - // This is used for a quick check on thread pool threads after running a work item to determine if the name, background - // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, - // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. - private bool _mayNeedResetForThreadPool; private Thread() { } - public extern int ManagedThreadId - { - [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - /// Returns handle for interop with EE. The handle is guaranteed to be non-null. internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); @@ -82,59 +31,6 @@ partial void ThreadNameChanged(string? value) InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); } - /// Returns true if the thread has been started and is not dead. - public extern bool IsAlive - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - - /// - /// Return whether or not this thread is a background thread. Background - /// threads do not affect when the Execution Engine shuts down. - /// - public bool IsBackground - { - get => IsBackgroundNative(); - set - { - SetBackgroundNative(value); - if (!value) - { - _mayNeedResetForThreadPool = true; - } - } - } - - /// Returns true if the thread is a threadpool thread. - public extern bool IsThreadPoolThread - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - [MethodImpl(MethodImplOptions.InternalCall)] - internal set; - } - - /// Returns the priority of the thread. - public ThreadPriority Priority - { - get => (ThreadPriority)GetPriorityNative(); - set - { - SetPriorityNative((int)value); - if (value != ThreadPriority.Normal) - { - _mayNeedResetForThreadPool = true; - } - } - } - - /// - /// Return the thread state as a consistent set of bits. This is more - /// general then IsAlive or IsBackground. - /// - public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); - public ApartmentState GetApartmentState() => GetApartmentStatePortableCore(); #if FEATURE_COMINTEROP @@ -165,16 +61,6 @@ public void DisableComObjectEagerCleanup() /// if the thread has not been started yet public extern bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); - /// - /// Max value to be passed into for optimal delaying. This value is normalized to be - /// appropriate for the processor. - /// - internal static int OptimalMaxSpinWaitsPerSpinIteration - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index 6b38dc85200c3..467e13cfd6033 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -7,8 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// PR-Comment: This would work as a Win32Thread.Windows.cs - namespace System.Threading { using OSThreadPriority = Interop.Kernel32.ThreadPriority; @@ -30,28 +28,474 @@ public sealed partial class Thread private static volatile bool s_comInitializedOnFinalizerThread; - public ApartmentState GetApartmentState() => GetApartmentStateCore(); + partial void PlatformSpecificInitialize(); + + // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start + private void PlatformSpecificInitializeExistingThread() + { + _osHandle = GetOSHandleForCurrentThread(); + } + + private static SafeWaitHandle GetOSHandleForCurrentThread() + { + IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); + IntPtr currentThreadHandle = Interop.Kernel32.GetCurrentThread(); + SafeWaitHandle threadHandle; + + if (Interop.Kernel32.DuplicateHandle(currentProcHandle, currentThreadHandle, currentProcHandle, + out threadHandle, 0, false, Interop.Kernel32.DUPLICATE_SAME_ACCESS)) + { + return threadHandle; + } + + // Throw an ApplicationException for compatibility with CoreCLR. First save the error code. + int errorCode = Marshal.GetLastWin32Error(); + var ex = new ApplicationException(); + ex.HResult = errorCode; + throw ex; + } + + private static ThreadPriority MapFromOSPriority(OSThreadPriority priority) + { + if (priority <= OSThreadPriority.Lowest) + { + // OS thread priorities in the [Idle,Lowest] range are mapped to ThreadPriority.Lowest + return ThreadPriority.Lowest; + } + switch (priority) + { + case OSThreadPriority.BelowNormal: + return ThreadPriority.BelowNormal; + + case OSThreadPriority.Normal: + return ThreadPriority.Normal; + + case OSThreadPriority.AboveNormal: + return ThreadPriority.AboveNormal; + + case OSThreadPriority.ErrorReturn: + Debug.Fail("GetThreadPriority failed"); + return ThreadPriority.Normal; + } + // Handle OSThreadPriority.ErrorReturn value before this check! + if (priority >= OSThreadPriority.Highest) + { + // OS thread priorities in the [Highest,TimeCritical] range are mapped to ThreadPriority.Highest + return ThreadPriority.Highest; + } + Debug.Fail("Unreachable"); + return ThreadPriority.Normal; + } + + private static OSThreadPriority MapToOSPriority(ThreadPriority priority) + { + switch (priority) + { + case ThreadPriority.Lowest: + return OSThreadPriority.Lowest; + + case ThreadPriority.BelowNormal: + return OSThreadPriority.BelowNormal; + + case ThreadPriority.Normal: + return OSThreadPriority.Normal; + + case ThreadPriority.AboveNormal: + return OSThreadPriority.AboveNormal; + + case ThreadPriority.Highest: + return OSThreadPriority.Highest; + + default: + Debug.Fail("Unreachable"); + return OSThreadPriority.Normal; + } + } + + private ThreadPriority GetPriorityLive() + { + Debug.Assert(!_osHandle.IsInvalid); + return MapFromOSPriority(Interop.Kernel32.GetThreadPriority(_osHandle)); + } + + private bool SetPriorityLive(ThreadPriority priority) + { + Debug.Assert(!_osHandle.IsInvalid); + return Interop.Kernel32.SetThreadPriority(_osHandle, (int)MapToOSPriority(priority)); + } + + [UnmanagedCallersOnly] + private static void OnThreadExit() + { + Thread? currentThread = t_currentThread; + if (currentThread != null) + { + StopThread(currentThread); + } + } + + private bool JoinInternal(int millisecondsTimeout) + { + // This method assumes the thread has been started + Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); + SafeWaitHandle waitHandle = _osHandle; + + // If an OS thread is terminated and its Thread object is resurrected, _osHandle may be finalized and closed + if (waitHandle.IsClosed) + { + return true; + } + + // Handle race condition with the finalizer + try + { + waitHandle.DangerousAddRef(); + } + catch (ObjectDisposedException) + { + return true; + } + + try + { + int result; + + if (millisecondsTimeout == 0) + { + result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); + } + else + { + result = WaitHandle.WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout); + } + + return result == (int)Interop.Kernel32.WAIT_OBJECT_0; + } + finally + { + waitHandle.DangerousRelease(); + } + } + + private unsafe bool CreateThread(GCHandle thisThreadHandle) + { + const int AllocationGranularity = 0x10000; // 64 KiB + + int stackSize = _startHelper._maxStackSize; + if ((0 < stackSize) && (stackSize < AllocationGranularity)) + { + // If StackSizeParamIsAReservation flag is set and the reserve size specified by CreateThread's + // dwStackSize parameter is less than or equal to the initially committed stack size specified in + // the executable header, the reserve size will be set to the initially committed size rounded up + // to the nearest multiple of 1 MiB. In all cases the reserve size is rounded up to the nearest + // multiple of the system's allocation granularity (typically 64 KiB). + // + // To prevent overreservation of stack memory for small stackSize values, we increase stackSize to + // the allocation granularity. We assume that the SizeOfStackCommit field of IMAGE_OPTIONAL_HEADER + // is strictly smaller than the allocation granularity (the field's default value is 4 KiB); + // otherwise, at least 1 MiB of memory will be reserved. Note that the desktop CLR increases + // stackSize to 256 KiB if it is smaller than that. + stackSize = AllocationGranularity; + } + + _osHandle = Interop.Kernel32.CreateThread(IntPtr.Zero, (IntPtr)stackSize, + &ThreadEntryPoint, (IntPtr)thisThreadHandle, + Interop.Kernel32.CREATE_SUSPENDED | Interop.Kernel32.STACK_SIZE_PARAM_IS_A_RESERVATION, + out _); + + if (_osHandle.IsInvalid) + { + return false; + } + + // CoreCLR ignores OS errors while setting the priority, so do we + SetPriorityLive(_priority); + + Interop.Kernel32.ResumeThread(_osHandle); + return true; + } + + /// + /// This is an entry point for managed threads created by application + /// + [UnmanagedCallersOnly] + private static uint ThreadEntryPoint(IntPtr parameter) + { + StartThread(parameter); + return 0; + } + + public ApartmentState GetApartmentState() + { + if (this != CurrentThread) + { + if (HasStarted()) + throw new ThreadStateException(); + return _initialApartmentState; + } + + switch (GetCurrentApartmentType()) + { + case ApartmentType.STA: + return ApartmentState.STA; + case ApartmentType.MTA: + return ApartmentState.MTA; + default: + return ApartmentState.Unknown; + } + } + + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + ApartmentState retState; + + if (this != CurrentThread) + { + using (LockHolder.Hold(_lock)) + { + if (HasStarted()) + throw new ThreadStateException(); + + // Compat: Disallow resetting the initial apartment state + if (_initialApartmentState == ApartmentState.Unknown) + _initialApartmentState = state; + + retState = _initialApartmentState; + } + } + else + { - internal static void InitializeComForFinalizerThread() => InitializeComForFinalizerThreadCore(); + if ((t_comState & ComState.Locked) == 0) + { + if (state != ApartmentState.Unknown) + { + InitializeCom(state); + } + else + { + UninitializeCom(); + } + } + + // Clear the cache and check whether new state matches the desired state + t_apartmentType = ApartmentType.Unknown; + + retState = GetApartmentState(); + } + + if (retState != state) + { + if (throwOnError) + { + string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); + throw new InvalidOperationException(msg); + } + + return false; + } + + return true; + } + + private void InitializeComOnNewThread() + { + InitializeCom(_initialApartmentState); + } + + internal static void InitializeComForFinalizerThread() + { + InitializeCom(); + + // Prevent re-initialization of COM model on finalizer thread + t_comState |= ComState.Locked; + + s_comInitializedOnFinalizerThread = true; + } + + private static void InitializeComForThreadPoolThread() + { + // Initialized COM - take advantage of implicit MTA initialized by the finalizer thread + SpinWait sw = new SpinWait(); + while (!s_comInitializedOnFinalizerThread) + { + RuntimeImports.RhInitializeFinalizerThread(); + sw.SpinOnce(0); + } + + // Prevent re-initialization of COM model on threadpool threads + t_comState |= ComState.Locked; + } + + private static void InitializeCom(ApartmentState state = ApartmentState.MTA) + { + if ((t_comState & ComState.InitializedByUs) != 0) + return; + +#if ENABLE_WINRT + int hr = Interop.WinRT.RoInitialize( + (state == ApartmentState.STA) ? Interop.WinRT.RO_INIT_SINGLETHREADED + : Interop.WinRT.RO_INIT_MULTITHREADED); +#else + int hr = Interop.Ole32.CoInitializeEx(IntPtr.Zero, + (state == ApartmentState.STA) ? Interop.Ole32.COINIT_APARTMENTTHREADED + : Interop.Ole32.COINIT_MULTITHREADED); +#endif + if (hr < 0) + { + // RPC_E_CHANGED_MODE indicates this thread has been already initialized with a different + // concurrency model. We stay away and let whoever else initialized the COM to be in control. + if (hr == HResults.RPC_E_CHANGED_MODE) + return; + + // CoInitializeEx returns E_NOTIMPL on Windows Nano Server for STA + if (hr == HResults.E_NOTIMPL) + throw new PlatformNotSupportedException(); + + throw new OutOfMemoryException(); + } + + t_comState |= ComState.InitializedByUs; + + // If the thread has already been CoInitialized to the proper mode, then + // we don't want to leave an outstanding CoInit so we CoUninit. + if (hr > 0) + UninitializeCom(); + } + + private static void UninitializeCom() + { + if ((t_comState & ComState.InitializedByUs) == 0) + return; + +#if ENABLE_WINRT + Interop.WinRT.RoUninitialize(); +#else + Interop.Ole32.CoUninitialize(); +#endif + + t_comState &= ~ComState.InitializedByUs; + } // TODO: https://github.com/dotnet/runtime/issues/22161 - public void DisableComObjectEagerCleanup() => DisableComObjectEagerCleanupCore(); + public void DisableComObjectEagerCleanup() { } + + private static Thread InitializeExistingThreadPoolThread() + { + ThreadPool.InitializeForThreadPoolThread(); + + InitializeComForThreadPoolThread(); + + Thread thread = CurrentThread; + thread.SetThreadStateBit(ThreadPoolThread); + return thread; + } // Use ThreadPoolCallbackWrapper instead of calling this function directly [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Thread EnsureThreadPoolThreadInitialized() => EnsureThreadPoolThreadInitializedCore(); + internal static Thread EnsureThreadPoolThreadInitialized() + { + Thread? thread = t_currentThread; + if (thread != null && thread.GetThreadStateBit(ThreadPoolThread)) + return thread; + return InitializeExistingThreadPoolThread(); + } - public void Interrupt() => InterruptCore(); + public void Interrupt() { throw new PlatformNotSupportedException(); } // // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. // This should be used by code that's expected to be called inside the STA message pump, so that it won't // reenter itself. In an ASTA, this should only be the CCW implementations of IUnknown and IInspectable. // - internal static void SuppressReentrantWaits() => SuppressReentrantWaitsCore(); + internal static void SuppressReentrantWaits() + { + t_reentrantWaitSuppressionCount++; + } + + internal static void RestoreReentrantWaits() + { + Debug.Assert(t_reentrantWaitSuppressionCount > 0); + t_reentrantWaitSuppressionCount--; + } + + internal static bool ReentrantWaitsEnabled => + GetCurrentApartmentType() == ApartmentType.STA && t_reentrantWaitSuppressionCount == 0; + + internal static ApartmentType GetCurrentApartmentType() + { + ApartmentType currentThreadType = t_apartmentType; + if (currentThreadType != ApartmentType.Unknown) + return currentThreadType; + + Interop.APTTYPE aptType; + Interop.APTTYPEQUALIFIER aptTypeQualifier; + int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier); + + ApartmentType type = ApartmentType.Unknown; + + switch (result) + { + case HResults.CO_E_NOTINITIALIZED: + type = ApartmentType.None; + break; + + case HResults.S_OK: + switch (aptType) + { + case Interop.APTTYPE.APTTYPE_STA: + case Interop.APTTYPE.APTTYPE_MAINSTA: + type = ApartmentType.STA; + break; + + case Interop.APTTYPE.APTTYPE_MTA: + type = ApartmentType.MTA; + break; + + case Interop.APTTYPE.APTTYPE_NA: + switch (aptTypeQualifier) + { + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: + type = ApartmentType.MTA; + break; + + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: + type = ApartmentType.STA; + break; + + default: + Debug.Fail("NA apartment without NA qualifier"); + break; + } + break; + } + break; + + default: + Debug.Fail("bad return from CoGetApartmentType"); + break; + } + + if (type != ApartmentType.Unknown) + t_apartmentType = type; + return type; + } - internal static void RestoreReentrantWaits() => RestoreReentrantWaitsCore(); + internal enum ApartmentType : byte + { + Unknown = 0, + None, + STA, + MTA + } - internal static ApartmentType GetCurrentApartmentType() => GetCurrentApartmentTypeCore(); + [Flags] + internal enum ComState : byte + { + InitializedByUs = 1, + Locked = 2, + } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index cda114276746c..94e24d6adb944 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -13,36 +13,6 @@ namespace System.Threading { public sealed partial class Thread { - // Extra bits used in _threadState - private const ThreadState ThreadPoolThread = (ThreadState)0x1000; - - // Bits of _threadState that are returned by the ThreadState property - private const ThreadState PublicThreadStateMask = (ThreadState)0x1FF; - - internal ExecutionContext? _executionContext; - internal SynchronizationContext? _synchronizationContext; - - private volatile int _threadState = (int)ThreadState.Unstarted; - private ThreadPriority _priority; - private ManagedThreadId _managedThreadId; - private string? _name; - private StartHelper? _startHelper; - private Exception? _startException; - - // Protects starting the thread and setting its priority - private Lock _lock = new Lock(); - - // This is used for a quick check on thread pool threads after running a work item to determine if the name, background - // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, - // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. - private bool _mayNeedResetForThreadPool; - - // so far the only place we initialize it is `WaitForForegroundThreads` - // and only in the case when there are running foreground threads - // by the moment of `StartupCodeHelpers.Shutdown()` invocation - private static ManualResetEvent s_allDone; - - private static int s_foregroundRunningCount; private Thread() { @@ -52,454 +22,23 @@ private Thread() RegisterThreadExitCallback(); } - private void Initialize() - { - _priority = ThreadPriority.Normal; - _managedThreadId = new ManagedThreadId(); - - PlatformSpecificInitialize(); - RegisterThreadExitCallback(); - } - - private static unsafe void RegisterThreadExitCallback() - { - RuntimeImports.RhSetThreadExitCallback(&OnThreadExit); - } - - internal static ulong CurrentOSThreadId - { - get - { - return RuntimeImports.RhCurrentOSThreadId(); - } - } - - // Slow path executed once per thread - [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThread() - { - Debug.Assert(t_currentThread == null); - - var currentThread = new Thread(); - Debug.Assert(currentThread._threadState == (int)ThreadState.Unstarted); - - ThreadState state = 0; - - // The main thread is foreground, other ones are background - if (currentThread._managedThreadId.Id != System.Threading.ManagedThreadId.IdMainThread) - { - state |= ThreadState.Background; - } - else - { - Interlocked.Increment(ref s_foregroundRunningCount); - } - - currentThread._threadState = (int)(state | ThreadState.Running); - currentThread.PlatformSpecificInitializeExistingThread(); - currentThread._priority = currentThread.GetPriorityLive(); - t_currentThread = currentThread; - - return currentThread; - } - - /// - /// Returns true if the underlying OS thread has been created and started execution of managed code. - /// - private bool HasStarted() - { - return !GetThreadStateBit(ThreadState.Unstarted); - } - - public bool IsAlive - { - get - { - return ((ThreadState)_threadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0; - } - } - - private bool IsDead() - { - return ((ThreadState)_threadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; - } - - public bool IsBackground - { - get - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - return GetThreadStateBit(ThreadState.Background); - } - set - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - // we changing foreground count only for started threads - // on thread start we count its state in `StartThread` - if (value) - { - int threadState = SetThreadStateBit(ThreadState.Background); - // was foreground and has started - if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == 0) - { - DecrementRunningForeground(); - } - } - else - { - int threadState = ClearThreadStateBit(ThreadState.Background); - // was background and has started - if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == (int)ThreadState.Background) - { - IncrementRunningForeground(); - _mayNeedResetForThreadPool = true; - } - } - } - } - - public bool IsThreadPoolThread - { - get - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - return GetThreadStateBit(ThreadPoolThread); - } - internal set - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - if (value) - { - SetThreadStateBit(ThreadPoolThread); - } - else - { - ClearThreadStateBit(ThreadPoolThread); - } - } - } - - public int ManagedThreadId - { - [Intrinsic] - get => _managedThreadId.Id; - } - - // TODO: Inform the debugger and the profiler - // private void ThreadNameChanged(string? value) {} - - public ThreadPriority Priority - { - get - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_Priority); - } - if (!HasStarted()) - { - // The thread has not been started yet; return the value assigned to the Priority property. - // Race condition with setting the priority or starting the thread is OK, we may return an old value. - return _priority; - } - // The priority might have been changed by external means. Obtain the actual value from the OS - // rather than using the value saved in _priority. - return GetPriorityLive(); - } - set - { - if ((value < ThreadPriority.Lowest) || (ThreadPriority.Highest < value)) - { - throw new ArgumentOutOfRangeException(SR.Argument_InvalidFlag); - } - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_Priority); - } - - // Prevent race condition with starting this thread - using (LockHolder.Hold(_lock)) - { - if (HasStarted() && !SetPriorityLive(value)) - { - throw new ThreadStateException(SR.ThreadState_SetPriorityFailed); - } - _priority = value; - } - - if (value != ThreadPriority.Normal) - { - _mayNeedResetForThreadPool = true; - } - } - } + internal void SetWaitSleepJoinState() => SetWaitSleepJoinStateCore(); - public ThreadState ThreadState => ((ThreadState)_threadState & PublicThreadStateMask); + internal void ClearWaitSleepJoinState() => ClearWaitSleepJoinStateCrore(); - private bool GetThreadStateBit(ThreadState bit) - { - Debug.Assert((bit & ThreadState.Stopped) == 0, "ThreadState.Stopped bit may be stale; use GetThreadState instead."); - return (_threadState & (int)bit) != 0; - } + public bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); - private int SetThreadStateBit(ThreadState bit) - { - int oldState, newState; - do - { - oldState = _threadState; - newState = oldState | (int)bit; - } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); - return oldState; - } + internal static void SpinWaitInternal(int iterations) => SpinWaitInternalCore(iterations); - private int ClearThreadStateBit(ThreadState bit) - { - int oldState, newState; - do - { - oldState = _threadState; - newState = oldState & ~(int)bit; - } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); - return oldState; - } - - internal void SetWaitSleepJoinState() - { - Debug.Assert(this == CurrentThread); - Debug.Assert(!GetThreadStateBit(ThreadState.WaitSleepJoin)); - - SetThreadStateBit(ThreadState.WaitSleepJoin); - } - - internal void ClearWaitSleepJoinState() - { - Debug.Assert(this == CurrentThread); - Debug.Assert(GetThreadStateBit(ThreadState.WaitSleepJoin)); - - ClearThreadStateBit(ThreadState.WaitSleepJoin); - } - - private static int VerifyTimeoutMilliseconds(int millisecondsTimeout) - { - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), - millisecondsTimeout, - SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - } - return millisecondsTimeout; - } - - public bool Join(int millisecondsTimeout) - { - VerifyTimeoutMilliseconds(millisecondsTimeout); - if (GetThreadStateBit(ThreadState.Unstarted)) - { - throw new ThreadStateException(SR.ThreadState_NotStarted); - } - return JoinInternal(millisecondsTimeout); - } - - /// - /// Max value to be passed into for optimal delaying. Currently, the value comes from - /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be - /// appropriate for the processor. - /// TODO: See issue https://github.com/dotnet/corert/issues/4430 - /// - internal const int OptimalMaxSpinWaitsPerSpinIteration = 8; - - // Max iterations to be done in RhSpinWait. - // RhSpinWait does not switch GC modes and we want to avoid native spinning in coop mode for too long. - private const int SpinWaitCoopThreshold = 1024; - - internal static void SpinWaitInternal(int iterations) - { - Debug.Assert(iterations <= SpinWaitCoopThreshold); - if (iterations > 0) - { - RuntimeImports.RhSpinWait(iterations); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - private static void LongSpinWait(int iterations) - { - RuntimeImports.RhLongSpinWait(iterations); - } - - public static void SpinWait(int iterations) - { - if (iterations > SpinWaitCoopThreshold) - { - LongSpinWait(iterations); - } - else - { - SpinWaitInternal(iterations); - } - } + public static void SpinWait(int iterations) => SpinWaitCore(); [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - public static bool Yield() => RuntimeImports.RhYield(); - - private void StartCore() - { - using (LockHolder.Hold(_lock)) - { - if (!GetThreadStateBit(ThreadState.Unstarted)) - { - throw new ThreadStateException(SR.ThreadState_AlreadyStarted); - } + public static bool Yield() => YieldCore(); - bool waitingForThreadStart = false; - GCHandle threadHandle = GCHandle.Alloc(this); + internal static void IncrementRunningForeground() => IncrementRunningForegroundCore(); - try - { - if (!CreateThread(threadHandle)) - { - throw new OutOfMemoryException(); - } + internal static void DecrementRunningForeground() => DecrementRunningForegroundCore(); - // Skip cleanup if any asynchronous exception happens while waiting for the thread start - waitingForThreadStart = true; - - // Wait until the new thread either dies or reports itself as started - while (GetThreadStateBit(ThreadState.Unstarted) && !JoinInternal(0)) - { - Yield(); - } - - waitingForThreadStart = false; - } - finally - { - Debug.Assert(!waitingForThreadStart, "Leaked threadHandle"); - if (!waitingForThreadStart) - { - threadHandle.Free(); - } - } - - if (GetThreadStateBit(ThreadState.Unstarted)) - { - Exception? startException = _startException; - _startException = null; - - throw new ThreadStartException(startException ?? new OutOfMemoryException()); - } - - Debug.Assert(_startException == null); - } - } - - private static void StartThread(IntPtr parameter) - { - GCHandle threadHandle = (GCHandle)parameter; - Thread thread = (Thread)threadHandle.Target!; - - try - { - t_currentThread = thread; - System.Threading.ManagedThreadId.SetForCurrentThread(thread._managedThreadId); - thread.InitializeComOnNewThread(); - } - catch (Exception e) - { - thread._startException = e; - -#if TARGET_UNIX - // This should go away once OnThreadExit stops using t_currentThread to signal - // shutdown of the thread on Unix. - thread._stopped!.Set(); -#endif - // Terminate the current thread. The creator thread will throw a ThreadStartException. - return; - } - - // Report success to the creator thread, which will free threadHandle - int state = thread.ClearThreadStateBit(ThreadState.Unstarted); - if ((state & (int)ThreadState.Background) == 0) - { - IncrementRunningForeground(); - } - - try - { - StartHelper? startHelper = thread._startHelper; - Debug.Assert(startHelper != null); - thread._startHelper = null; - - startHelper.Run(); - } - finally - { - thread.SetThreadStateBit(ThreadState.Stopped); - } - } - - private static void StopThread(Thread thread) - { - if ((thread._threadState & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) - { - thread.SetThreadStateBit(ThreadState.Stopped); - } - - int state = thread.ClearThreadStateBit(ThreadState.Background); - if ((state & (int)ThreadState.Background) == 0) - { - DecrementRunningForeground(); - } - } - - internal static void IncrementRunningForeground() - { - Interlocked.Increment(ref s_foregroundRunningCount); - } - - internal static void DecrementRunningForeground() - { - if (Interlocked.Decrement(ref s_foregroundRunningCount) == 0) - { - // Interlocked.Decrement issues full memory barrier - // so most recent write to s_allDone should be visible here - s_allDone?.Set(); - } - } - - internal static void WaitForForegroundThreads() - { - Thread.CurrentThread.IsBackground = true; - // last read/write inside `IsBackground` issues full barrier no matter of logic flow - // so we can just read `s_foregroundRunningCount` - if (s_foregroundRunningCount == 0) - { - // current thread is the last foreground thread, so let the runtime finish - return; - } - Volatile.Write(ref s_allDone, new ManualResetEvent(false)); - // other foreground threads could have their job finished meanwhile - // Volatile.Write above issues release barrier - // but we need acquire barrier to observe most recent write to s_foregroundRunningCount - if (Volatile.Read(ref s_foregroundRunningCount) == 0) - { - return; - } - s_allDone.WaitOne(); - } + internal static void WaitForForegroundThreads() => WaitForForegroundThreadsCore(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs index 942f929b14be0..bcfcf50958633 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs @@ -3,486 +3,495 @@ using Microsoft.Win32.SafeHandles; using System.Diagnostics; +using System.Globalization; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - -// PR-Comment: This implementation comes from Thread.NativeAot.Windows.cs, -// not sure if it should be called Thread.WindowsThreadPool.cs or Thread.NativeAot.WindowsThreadPool.cs or something else +using System.Runtime.Versioning; namespace System.Threading { - using OSThreadPriority = Interop.Kernel32.ThreadPriority; - public sealed partial class Thread { - partial void PlatformSpecificInitialize(); + // Extra bits used in _threadState + private const ThreadState ThreadPoolThread = (ThreadState)0x1000; - // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start - private void PlatformSpecificInitializeExistingThread() - { - _osHandle = GetOSHandleForCurrentThread(); - } + // Bits of _threadState that are returned by the ThreadState property + private const ThreadState PublicThreadStateMask = (ThreadState)0x1FF; - private static SafeWaitHandle GetOSHandleForCurrentThread() - { - IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); - IntPtr currentThreadHandle = Interop.Kernel32.GetCurrentThread(); - SafeWaitHandle threadHandle; + internal ExecutionContext? _executionContext; + internal SynchronizationContext? _synchronizationContext; - if (Interop.Kernel32.DuplicateHandle(currentProcHandle, currentThreadHandle, currentProcHandle, - out threadHandle, 0, false, Interop.Kernel32.DUPLICATE_SAME_ACCESS)) - { - return threadHandle; - } + private volatile int _threadState = (int)ThreadState.Unstarted; + private ThreadPriority _priority; + private ManagedThreadId _managedThreadId; + private string? _name; + private StartHelper? _startHelper; + private Exception? _startException; - // Throw an ApplicationException for compatibility with CoreCLR. First save the error code. - int errorCode = Marshal.GetLastWin32Error(); - var ex = new ApplicationException(); - ex.HResult = errorCode; - throw ex; - } + // Protects starting the thread and setting its priority + private Lock _lock = new Lock(); + + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background + // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, + // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. + private bool _mayNeedResetForThreadPool; - private static ThreadPriority MapFromOSPriority(OSThreadPriority priority) + // so far the only place we initialize it is `WaitForForegroundThreads` + // and only in the case when there are running foreground threads + // by the moment of `StartupCodeHelpers.Shutdown()` invocation + private static ManualResetEvent s_allDone; + + private static int s_foregroundRunningCount; + + private void Initialize() { - if (priority <= OSThreadPriority.Lowest) - { - // OS thread priorities in the [Idle,Lowest] range are mapped to ThreadPriority.Lowest - return ThreadPriority.Lowest; - } - switch (priority) - { - case OSThreadPriority.BelowNormal: - return ThreadPriority.BelowNormal; + _priority = ThreadPriority.Normal; + _managedThreadId = new ManagedThreadId(); - case OSThreadPriority.Normal: - return ThreadPriority.Normal; + PlatformSpecificInitialize(); + RegisterThreadExitCallback(); + } - case OSThreadPriority.AboveNormal: - return ThreadPriority.AboveNormal; + private static unsafe void RegisterThreadExitCallback() + { + RuntimeImports.RhSetThreadExitCallback(&OnThreadExit); + } - case OSThreadPriority.ErrorReturn: - Debug.Fail("GetThreadPriority failed"); - return ThreadPriority.Normal; - } - // Handle OSThreadPriority.ErrorReturn value before this check! - if (priority >= OSThreadPriority.Highest) + internal static ulong CurrentOSThreadId + { + get { - // OS thread priorities in the [Highest,TimeCritical] range are mapped to ThreadPriority.Highest - return ThreadPriority.Highest; + return RuntimeImports.RhCurrentOSThreadId(); } - Debug.Fail("Unreachable"); - return ThreadPriority.Normal; } - private static OSThreadPriority MapToOSPriority(ThreadPriority priority) + // Slow path executed once per thread + [MethodImpl(MethodImplOptions.NoInlining)] + private static Thread InitializeCurrentThread() { - switch (priority) - { - case ThreadPriority.Lowest: - return OSThreadPriority.Lowest; - - case ThreadPriority.BelowNormal: - return OSThreadPriority.BelowNormal; + Debug.Assert(t_currentThread == null); - case ThreadPriority.Normal: - return OSThreadPriority.Normal; + var currentThread = new Thread(); + Debug.Assert(currentThread._threadState == (int)ThreadState.Unstarted); - case ThreadPriority.AboveNormal: - return OSThreadPriority.AboveNormal; + ThreadState state = 0; - case ThreadPriority.Highest: - return OSThreadPriority.Highest; - - default: - Debug.Fail("Unreachable"); - return OSThreadPriority.Normal; + // The main thread is foreground, other ones are background + if (currentThread._managedThreadId.Id != System.Threading.ManagedThreadId.IdMainThread) + { + state |= ThreadState.Background; + } + else + { + Interlocked.Increment(ref s_foregroundRunningCount); } - } - private ThreadPriority GetPriorityLive() - { - Debug.Assert(!_osHandle.IsInvalid); - return MapFromOSPriority(Interop.Kernel32.GetThreadPriority(_osHandle)); + currentThread._threadState = (int)(state | ThreadState.Running); + currentThread.PlatformSpecificInitializeExistingThread(); + currentThread._priority = currentThread.GetPriorityLive(); + t_currentThread = currentThread; + + return currentThread; } - private bool SetPriorityLive(ThreadPriority priority) + /// + /// Returns true if the underlying OS thread has been created and started execution of managed code. + /// + private bool HasStarted() { - Debug.Assert(!_osHandle.IsInvalid); - return Interop.Kernel32.SetThreadPriority(_osHandle, (int)MapToOSPriority(priority)); + return !GetThreadStateBit(ThreadState.Unstarted); } - [UnmanagedCallersOnly] - private static void OnThreadExit() + public bool IsAlive { - Thread? currentThread = t_currentThread; - if (currentThread != null) + get { - StopThread(currentThread); + return ((ThreadState)_threadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0; } } - private bool JoinInternal(int millisecondsTimeout) + private bool IsDead() { - // This method assumes the thread has been started - Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); - SafeWaitHandle waitHandle = _osHandle; - - // If an OS thread is terminated and its Thread object is resurrected, _osHandle may be finalized and closed - if (waitHandle.IsClosed) - { - return true; - } + return ((ThreadState)_threadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; + } - // Handle race condition with the finalizer - try + public bool IsBackground + { + get { - waitHandle.DangerousAddRef(); + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + return GetThreadStateBit(ThreadState.Background); } - catch (ObjectDisposedException) + set { - return true; - } - - try - { - int result; - - if (millisecondsTimeout == 0) + if (IsDead()) { - result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + // we changing foreground count only for started threads + // on thread start we count its state in `StartThread` + if (value) + { + int threadState = SetThreadStateBit(ThreadState.Background); + // was foreground and has started + if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == 0) + { + DecrementRunningForeground(); + } } else { - result = WaitHandle.WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout); + int threadState = ClearThreadStateBit(ThreadState.Background); + // was background and has started + if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == (int)ThreadState.Background) + { + IncrementRunningForeground(); + _mayNeedResetForThreadPool = true; + } } - - return result == (int)Interop.Kernel32.WAIT_OBJECT_0; - } - finally - { - waitHandle.DangerousRelease(); } } - private unsafe bool CreateThread(GCHandle thisThreadHandle) + public bool IsThreadPoolThread { - const int AllocationGranularity = 0x10000; // 64 KiB - - int stackSize = _startHelper._maxStackSize; - if ((0 < stackSize) && (stackSize < AllocationGranularity)) + get { - // If StackSizeParamIsAReservation flag is set and the reserve size specified by CreateThread's - // dwStackSize parameter is less than or equal to the initially committed stack size specified in - // the executable header, the reserve size will be set to the initially committed size rounded up - // to the nearest multiple of 1 MiB. In all cases the reserve size is rounded up to the nearest - // multiple of the system's allocation granularity (typically 64 KiB). - // - // To prevent overreservation of stack memory for small stackSize values, we increase stackSize to - // the allocation granularity. We assume that the SizeOfStackCommit field of IMAGE_OPTIONAL_HEADER - // is strictly smaller than the allocation granularity (the field's default value is 4 KiB); - // otherwise, at least 1 MiB of memory will be reserved. Note that the desktop CLR increases - // stackSize to 256 KiB if it is smaller than that. - stackSize = AllocationGranularity; + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + return GetThreadStateBit(ThreadPoolThread); } - - _osHandle = Interop.Kernel32.CreateThread(IntPtr.Zero, (IntPtr)stackSize, - &ThreadEntryPoint, (IntPtr)thisThreadHandle, - Interop.Kernel32.CREATE_SUSPENDED | Interop.Kernel32.STACK_SIZE_PARAM_IS_A_RESERVATION, - out _); - - if (_osHandle.IsInvalid) + internal set { - return false; + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + if (value) + { + SetThreadStateBit(ThreadPoolThread); + } + else + { + ClearThreadStateBit(ThreadPoolThread); + } } - - // CoreCLR ignores OS errors while setting the priority, so do we - SetPriorityLive(_priority); - - Interop.Kernel32.ResumeThread(_osHandle); - return true; } - /// - /// This is an entry point for managed threads created by application - /// - [UnmanagedCallersOnly] - private static uint ThreadEntryPoint(IntPtr parameter) + public int ManagedThreadId { - StartThread(parameter); - return 0; + [Intrinsic] + get => _managedThreadId.Id; } - private ApartmentState GetApartmentStateCore() - { - if (this != CurrentThread) - { - if (HasStarted()) - throw new ThreadStateException(); - return _initialApartmentState; - } - - switch (GetCurrentApartmentType()) - { - case ApartmentType.STA: - return ApartmentState.STA; - case ApartmentType.MTA: - return ApartmentState.MTA; - default: - return ApartmentState.Unknown; - } - } + // TODO: Inform the debugger and the profiler + // private void ThreadNameChanged(string? value) {} - /* - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + public ThreadPriority Priority { - ApartmentState retState; - - if (this != CurrentThread) + get { - using (LockHolder.Hold(_lock)) + if (IsDead()) { - if (HasStarted()) - throw new ThreadStateException(); - - // Compat: Disallow resetting the initial apartment state - if (_initialApartmentState == ApartmentState.Unknown) - _initialApartmentState = state; - - retState = _initialApartmentState; + throw new ThreadStateException(SR.ThreadState_Dead_Priority); } + if (!HasStarted()) + { + // The thread has not been started yet; return the value assigned to the Priority property. + // Race condition with setting the priority or starting the thread is OK, we may return an old value. + return _priority; + } + // The priority might have been changed by external means. Obtain the actual value from the OS + // rather than using the value saved in _priority. + return GetPriorityLive(); } - else + set { + if ((value < ThreadPriority.Lowest) || (ThreadPriority.Highest < value)) + { + throw new ArgumentOutOfRangeException(SR.Argument_InvalidFlag); + } + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_Priority); + } - if ((t_comState & ComState.Locked) == 0) + // Prevent race condition with starting this thread + using (LockHolder.Hold(_lock)) { - if (state != ApartmentState.Unknown) - { - InitializeCom(state); - } - else + if (HasStarted() && !SetPriorityLive(value)) { - UninitializeCom(); + throw new ThreadStateException(SR.ThreadState_SetPriorityFailed); } + _priority = value; } - // Clear the cache and check whether new state matches the desired state - t_apartmentType = ApartmentType.Unknown; - - retState = GetApartmentState(); - } - - if (retState != state) - { - if (throwOnError) + if (value != ThreadPriority.Normal) { - string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); - throw new InvalidOperationException(msg); + _mayNeedResetForThreadPool = true; } - - return false; } + } + + public ThreadState ThreadState => ((ThreadState)_threadState & PublicThreadStateMask); - return true; + private bool GetThreadStateBit(ThreadState bit) + { + Debug.Assert((bit & ThreadState.Stopped) == 0, "ThreadState.Stopped bit may be stale; use GetThreadState instead."); + return (_threadState & (int)bit) != 0; } - */ - private void InitializeComOnNewThread() + private int SetThreadStateBit(ThreadState bit) { - InitializeCom(_initialApartmentState); + int oldState, newState; + do + { + oldState = _threadState; + newState = oldState | (int)bit; + } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); + return oldState; } - private static void InitializeComForFinalizerThreadCore() + private int ClearThreadStateBit(ThreadState bit) { - InitializeCom(); + int oldState, newState; + do + { + oldState = _threadState; + newState = oldState & ~(int)bit; + } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); + return oldState; + } - // Prevent re-initialization of COM model on finalizer thread - t_comState |= ComState.Locked; + private void SetWaitSleepJoinStateCore() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(!GetThreadStateBit(ThreadState.WaitSleepJoin)); - s_comInitializedOnFinalizerThread = true; + SetThreadStateBit(ThreadState.WaitSleepJoin); } - private static void InitializeComForThreadPoolThread() + private void ClearWaitSleepJoinStateCore() { - // Initialized COM - take advantage of implicit MTA initialized by the finalizer thread - SpinWait sw = new SpinWait(); - while (!s_comInitializedOnFinalizerThread) - { - RuntimeImports.RhInitializeFinalizerThread(); - sw.SpinOnce(0); - } + Debug.Assert(this == CurrentThread); + Debug.Assert(GetThreadStateBit(ThreadState.WaitSleepJoin)); - // Prevent re-initialization of COM model on threadpool threads - t_comState |= ComState.Locked; + ClearThreadStateBit(ThreadState.WaitSleepJoin); } - private static void InitializeCom(ApartmentState state = ApartmentState.MTA) + private static int VerifyTimeoutMilliseconds(int millisecondsTimeout) { - if ((t_comState & ComState.InitializedByUs) != 0) - return; + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException( + nameof(millisecondsTimeout), + millisecondsTimeout, + SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + } + return millisecondsTimeout; + } -#if ENABLE_WINRT - int hr = Interop.WinRT.RoInitialize( - (state == ApartmentState.STA) ? Interop.WinRT.RO_INIT_SINGLETHREADED - : Interop.WinRT.RO_INIT_MULTITHREADED); -#else - int hr = Interop.Ole32.CoInitializeEx(IntPtr.Zero, - (state == ApartmentState.STA) ? Interop.Ole32.COINIT_APARTMENTTHREADED - : Interop.Ole32.COINIT_MULTITHREADED); -#endif - if (hr < 0) + private bool JoinCore(int millisecondsTimeout) + { + VerifyTimeoutMilliseconds(millisecondsTimeout); + if (GetThreadStateBit(ThreadState.Unstarted)) { - // RPC_E_CHANGED_MODE indicates this thread has been already initialized with a different - // concurrency model. We stay away and let whoever else initialized the COM to be in control. - if (hr == HResults.RPC_E_CHANGED_MODE) - return; + throw new ThreadStateException(SR.ThreadState_NotStarted); + } + return JoinInternal(millisecondsTimeout); + } + + /// + /// Max value to be passed into for optimal delaying. Currently, the value comes from + /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be + /// appropriate for the processor. + /// TODO: See issue https://github.com/dotnet/corert/issues/4430 + /// + internal const int OptimalMaxSpinWaitsPerSpinIteration = 8; - // CoInitializeEx returns E_NOTIMPL on Windows Nano Server for STA - if (hr == HResults.E_NOTIMPL) - throw new PlatformNotSupportedException(); + // Max iterations to be done in RhSpinWait. + // RhSpinWait does not switch GC modes and we want to avoid native spinning in coop mode for too long. + private const int SpinWaitCoopThreshold = 1024; - throw new OutOfMemoryException(); + private static void SpinWaitInternalCore(int iterations) + { + Debug.Assert(iterations <= SpinWaitCoopThreshold); + if (iterations > 0) + { + RuntimeImports.RhSpinWait(iterations); } + } - t_comState |= ComState.InitializedByUs; + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. + private static void LongSpinWait(int iterations) + { + RuntimeImports.RhLongSpinWait(iterations); + } - // If the thread has already been CoInitialized to the proper mode, then - // we don't want to leave an outstanding CoInit so we CoUninit. - if (hr > 0) - UninitializeCom(); + private static void SpinWaitCore(int iterations) + { + if (iterations > SpinWaitCoopThreshold) + { + LongSpinWait(iterations); + } + else + { + SpinWaitInternal(iterations); + } } - private static void UninitializeCom() + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. + private static bool YieldCore() => RuntimeImports.RhYield(); + + private void StartCore() { - if ((t_comState & ComState.InitializedByUs) == 0) - return; + using (LockHolder.Hold(_lock)) + { + if (!GetThreadStateBit(ThreadState.Unstarted)) + { + throw new ThreadStateException(SR.ThreadState_AlreadyStarted); + } -#if ENABLE_WINRT - Interop.WinRT.RoUninitialize(); -#else - Interop.Ole32.CoUninitialize(); -#endif + bool waitingForThreadStart = false; + GCHandle threadHandle = GCHandle.Alloc(this); - t_comState &= ~ComState.InitializedByUs; - } + try + { + if (!CreateThread(threadHandle)) + { + throw new OutOfMemoryException(); + } - private static Thread InitializeExistingThreadPoolThread() - { - ThreadPool.InitializeForThreadPoolThread(); + // Skip cleanup if any asynchronous exception happens while waiting for the thread start + waitingForThreadStart = true; - InitializeComForThreadPoolThread(); + // Wait until the new thread either dies or reports itself as started + while (GetThreadStateBit(ThreadState.Unstarted) && !JoinInternal(0)) + { + Yield(); + } - Thread thread = CurrentThread; - thread.SetThreadStateBit(ThreadPoolThread); - return thread; - } + waitingForThreadStart = false; + } + finally + { + Debug.Assert(!waitingForThreadStart, "Leaked threadHandle"); + if (!waitingForThreadStart) + { + threadHandle.Free(); + } + } - // Use ThreadPoolCallbackWrapper instead of calling this function directly - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Thread EnsureThreadPoolThreadInitializedCore() - { - Thread? thread = t_currentThread; - if (thread != null && thread.GetThreadStateBit(ThreadPoolThread)) - return thread; - return InitializeExistingThreadPoolThread(); - } + if (GetThreadStateBit(ThreadState.Unstarted)) + { + Exception? startException = _startException; + _startException = null; - private void InterruptCore() { throw new PlatformNotSupportedException(); } + throw new ThreadStartException(startException ?? new OutOfMemoryException()); + } - // - // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. - // This should be used by code that's expected to be called inside the STA message pump, so that it won't - // reenter itself. In an ASTA, this should only be the CCW implementations of IUnknown and IInspectable. - // - private static void SuppressReentrantWaitsCore() - { - t_reentrantWaitSuppressionCount++; + Debug.Assert(_startException == null); + } } - private static void RestoreReentrantWaitsCore() + private static void StartThread(IntPtr parameter) { - Debug.Assert(t_reentrantWaitSuppressionCount > 0); - t_reentrantWaitSuppressionCount--; - } + GCHandle threadHandle = (GCHandle)parameter; + Thread thread = (Thread)threadHandle.Target!; - private static bool ReentrantWaitsEnabled => - GetCurrentApartmentType() == ApartmentType.STA && t_reentrantWaitSuppressionCount == 0; + try + { + t_currentThread = thread; + System.Threading.ManagedThreadId.SetForCurrentThread(thread._managedThreadId); + thread.InitializeComOnNewThread(); + } + catch (Exception e) + { + thread._startException = e; - private static ApartmentType GetCurrentApartmentTypeCore() - { - ApartmentType currentThreadType = t_apartmentType; - if (currentThreadType != ApartmentType.Unknown) - return currentThreadType; +#if TARGET_UNIX + // This should go away once OnThreadExit stops using t_currentThread to signal + // shutdown of the thread on Unix. + thread._stopped!.Set(); +#endif + // Terminate the current thread. The creator thread will throw a ThreadStartException. + return; + } - Interop.APTTYPE aptType; - Interop.APTTYPEQUALIFIER aptTypeQualifier; - int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier); + // Report success to the creator thread, which will free threadHandle + int state = thread.ClearThreadStateBit(ThreadState.Unstarted); + if ((state & (int)ThreadState.Background) == 0) + { + IncrementRunningForeground(); + } - ApartmentType type = ApartmentType.Unknown; + try + { + StartHelper? startHelper = thread._startHelper; + Debug.Assert(startHelper != null); + thread._startHelper = null; - switch (result) + startHelper.Run(); + } + finally { - case HResults.CO_E_NOTINITIALIZED: - type = ApartmentType.None; - break; + thread.SetThreadStateBit(ThreadState.Stopped); + } + } - case HResults.S_OK: - switch (aptType) - { - case Interop.APTTYPE.APTTYPE_STA: - case Interop.APTTYPE.APTTYPE_MAINSTA: - type = ApartmentType.STA; - break; - - case Interop.APTTYPE.APTTYPE_MTA: - type = ApartmentType.MTA; - break; - - case Interop.APTTYPE.APTTYPE_NA: - switch (aptTypeQualifier) - { - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: - type = ApartmentType.MTA; - break; - - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: - case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: - type = ApartmentType.STA; - break; - - default: - Debug.Fail("NA apartment without NA qualifier"); - break; - } - break; - } - break; + private static void StopThread(Thread thread) + { + if ((thread._threadState & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) + { + thread.SetThreadStateBit(ThreadState.Stopped); + } - default: - Debug.Fail("bad return from CoGetApartmentType"); - break; + int state = thread.ClearThreadStateBit(ThreadState.Background); + if ((state & (int)ThreadState.Background) == 0) + { + DecrementRunningForeground(); } + } - if (type != ApartmentType.Unknown) - t_apartmentType = type; - return type; + private static void IncrementRunningForegroundCore() + { + Interlocked.Increment(ref s_foregroundRunningCount); } - internal enum ApartmentType : byte + private static void DecrementRunningForegroundCore() { - Unknown = 0, - None, - STA, - MTA + if (Interlocked.Decrement(ref s_foregroundRunningCount) == 0) + { + // Interlocked.Decrement issues full memory barrier + // so most recent write to s_allDone should be visible here + s_allDone?.Set(); + } } - [Flags] - internal enum ComState : byte + private static void WaitForForegroundThreadsCore() { - InitializedByUs = 1, - Locked = 2, + Thread.CurrentThread.IsBackground = true; + // last read/write inside `IsBackground` issues full barrier no matter of logic flow + // so we can just read `s_foregroundRunningCount` + if (s_foregroundRunningCount == 0) + { + // current thread is the last foreground thread, so let the runtime finish + return; + } + Volatile.Write(ref s_allDone, new ManualResetEvent(false)); + // other foreground threads could have their job finished meanwhile + // Volatile.Write above issues release barrier + // but we need acquire barrier to observe most recent write to s_foregroundRunningCount + if (Volatile.Read(ref s_foregroundRunningCount) == 0) + { + return; + } + s_allDone.WaitOne(); } } } From e0fb07cb348df74821e96764facb97465a0e4c12 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Mar 2023 14:42:45 -0700 Subject: [PATCH 011/168] More errors fixed --- .../System.Private.CoreLib.csproj | 1 + .../System/Threading/Thread.CoreCLR.Core.cs | 39 +++----- .../Threading/Thread.CoreCLR.Windows.cs | 99 +++++++++++++++---- .../src/System/Threading/Thread.CoreCLR.cs | 63 +++++++++++- .../src/System/Threading/Thread.NativeAot.cs | 66 +++++++++++++ .../Threading/Thread.WindowsThreadPool.cs | 52 ++++------ .../src/System/Threading/Thread.cs | 12 ++- 7 files changed, 250 insertions(+), 82 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 27076449cb3af..fcf1165362049 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -293,6 +293,7 @@ + Interop\Windows\Kernel32\Interop.ThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs index ddbb21e72cb6c..d645c157effce 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs @@ -32,9 +32,6 @@ public sealed partial class Thread internal ExecutionContext? _executionContext; // this call context follows the logical thread internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext - private string? _name; - private StartHelper? _startHelper; - /*========================================================================= ** The base implementation of Thread is all native. The following fields ** should never be used in the C# code. They are here to define the proper @@ -55,11 +52,6 @@ public sealed partial class Thread private int _managedThreadId; // INT32 #pragma warning restore CA1823, 169 - // This is used for a quick check on thread pool threads after running a work item to determine if the name, background - // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, - // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. - private bool _mayNeedResetForThreadPool; - public extern int ManagedThreadId { [Intrinsic] @@ -67,8 +59,7 @@ public extern int ManagedThreadId get; } - /// Returns true if the thread has been started and is not dead. - public extern bool IsAlive + private extern bool IsAlivePortableCore { [MethodImpl(MethodImplOptions.InternalCall)] get; @@ -78,7 +69,7 @@ public extern bool IsAlive /// Return whether or not this thread is a background thread. Background /// threads do not affect when the Execution Engine shuts down. /// - public bool IsBackground + private bool IsBackgroundPortableCore { get => IsBackgroundNative(); set @@ -92,16 +83,16 @@ public bool IsBackground } /// Returns true if the thread is a threadpool thread. - public extern bool IsThreadPoolThread + private extern bool IsThreadPoolThreadPortableCore { [MethodImpl(MethodImplOptions.InternalCall)] get; [MethodImpl(MethodImplOptions.InternalCall)] - internal set; + set; } /// Returns the priority of the thread. - public ThreadPriority Priority + private ThreadPriority PriorityPortableCore { get => (ThreadPriority)GetPriorityNative(); set @@ -118,14 +109,14 @@ public ThreadPriority Priority /// Return the thread state as a consistent set of bits. This is more /// general then IsAlive or IsBackground. /// - public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); + private ThreadState ThreadStatePortableCore => (ThreadState)GetThreadStateNative(); /// /// Max value to be passed into for optimal delaying. This value is normalized to be /// appropriate for the processor. /// - internal static int OptimalMaxSpinWaitsPerSpinIteration + private static int OptimalMaxSpinWaitsPerSpinIterationPortableCore { [MethodImpl(MethodImplOptions.InternalCall)] get; @@ -145,7 +136,8 @@ private ThreadHandle GetNativeHandleCore() return new ThreadHandle(thread); } - private unsafe void StartCore() + // private unsafe void StartCore() - PR-Comment: Don't know if this rename it's appropriate + private unsafe void StartCLRCore() { lock (this) { @@ -188,24 +180,23 @@ private void StartCallback() /// only take a few machine instructions. Calling this API is preferable to coding /// a explicit busy loop because the hardware can be informed that it is busy waiting. /// - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void SpinWaitInternal(int iterations); + private static extern void SpinWaitInternalPortableCore(int iterations); - private static void SpinWaitCore(int iterations) => SpinWaitInternal(iterations); + private static void SpinWaitPortableCore(int iterations) => SpinWaitInternal(iterations); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_YieldThread")] private static partial Interop.BOOL YieldInternal(); - private static bool YieldCore() => YieldInternal() != Interop.BOOL.FALSE; + private static bool YieldPortableCore() => YieldInternal() != Interop.BOOL.FALSE; [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative(); + private static Thread InitializeCurrentThreadPortableCore() => t_currentThread = GetCurrentThreadNative(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern Thread GetCurrentThreadNative(); [MethodImpl(MethodImplOptions.InternalCall)] - private extern void Initialize(); + private extern void InitializePortableCore(); [MethodImpl(MethodImplOptions.InternalCall)] private extern void InternalFinalize(); @@ -296,7 +287,7 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO private extern void InterruptCore(); [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool JoinCore(int millisecondsTimeout); + private extern bool JoinPortableCore(int millisecondsTimeout); [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ResetThreadPoolThreadCore() diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs index 92d46b60c72d3..a9cd8239bcc18 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs @@ -14,6 +14,73 @@ namespace System.Threading { public sealed partial class Thread { + private string? _name; + private StartHelper? _startHelper; + + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background + // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, + // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. + private bool _mayNeedResetForThreadPool; + + public bool IsAlive + { + get => ThreadPool.UseWindowsThreadPool ? IsAliveCore : IsAlivePortableCore; + } + + public bool IsBackground + { + get => ThreadPool.UseWindowsThreadPool ? IsBackgroundCore : IsBackgroundPortableCore; + set + { + if (ThreadPool.UseWindowsThreadPool) + { + IsBackgroundCore = value; + } + else + { + IsBackgroundPortableCore = value; + } + } + } + + public bool IsThreadPoolThread + { + get => ThreadPool.UseWindowsThreadPool ? IsThreadPoolThreadCore : IsThreadPoolThreadPortableCore; + internal set + { + if (ThreadPool.UseWindowsThreadPool) + { + IsThreadPoolThreadCore = value; + } + else + { + IsThreadPoolThreadPortableCore = value; + } + } + } + + public ThreadPriority Priority + { + get => ThreadPool.UseWindowsThreadPool ? PriorityCore : PriorityPortableCore; + set + { + if (ThreadPool.UseWindowsThreadPool) + { + PriorityCore = value; + } + else + { + PriorityPortableCore = value; + } + } + } + + public ThreadState ThreadState => ThreadPool.UseWindowsThreadPool ? ThreadStateCore : ThreadStatePortableCore; + + internal static int OptimalMaxSpinWaitsPerSpinIteration + { + get => ThreadPool.UseWindowsThreadPool ? OptimalMaxSpinWaitsPerSpinIterationCore : OptimalMaxSpinWaitsPerSpinIterationPortableCore; + } private Thread() { @@ -26,18 +93,24 @@ private Thread() } } + private unsafe void StartCore() => ThreadPool.UseWindowsThreadPool ? StartWindowsThreadPoolCore() : StartCLRCore(); + + private static Thread InitializeCurrentThread() => ThreadPool.UseWindowsThreadPool ? InitializeCurrentThreadCore() : InitializeCurrentThreadPortableCore(); + + private void Initialize() => ThreadPool.UseWindowsThreadPool ? InitializeCore() : InitializePortableCore(); + + public bool Join(int millisecondsTimeout) => ThreadPool.UseWindowsThreadPool ? JoinCore(millisecondsTimeout) : JoinPortableCore(millisecondsTimeout); + internal void SetWaitSleepJoinState() => SetWaitSleepJoinStateCore(); internal void ClearWaitSleepJoinState() => ClearWaitSleepJoinStateCrore(); - public bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); + private static void SpinWaitInternal(int iterations) => ThreadPool.UseWindowsThreadPool ? SpinWaitInternalCore(iterations) : SpinWaitInternalPortableCore(iterations); - internal static void SpinWaitInternal(int iterations) => SpinWaitInternalCore(iterations); - - public static void SpinWait(int iterations) => SpinWaitCore(); + public static void SpinWait(int iterations) => ThreadPool.UseWindowsThreadPool ? SpinWaitCore(iterations) : SpinWaitPortableCore(iterations); [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - public static bool Yield() => YieldCore(); + public static bool Yield() => ThreadPool.UseWindowsThreadPool ? YieldCore() : YieldPortableCore(); internal static void IncrementRunningForeground() => IncrementRunningForegroundCore(); @@ -48,10 +121,6 @@ private Thread() /// Returns handle for interop with EE. The handle is guaranteed to be non-null. internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); - public static void SpinWait(int iterations) => SpinWaitCore(iterations); - - public static bool Yield() => YieldCore(); - /// Clean up the thread when it goes away. ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. @@ -78,18 +147,6 @@ public void DisableComObjectEagerCleanup() /// public void Interrupt() => InterruptCore(); - /// - /// Waits for the thread to die or for timeout milliseconds to elapse. - /// - /// - /// Returns true if the thread died, or false if the wait timed out. If - /// -1 is given as the parameter, no timeout will occur. - /// - /// if timeout < -1 (Timeout.Infinite) - /// if the thread is interrupted while waiting - /// if the thread has not been started yet - public bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 3a1c969b8371b..da96b8c44377d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -13,15 +13,72 @@ namespace System.Threading public sealed partial class Thread { + private string? _name; + private StartHelper? _startHelper; + + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background + // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, + // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. + private bool _mayNeedResetForThreadPool; + + /// Returns true if the thread has been started and is not dead. + public bool IsAlive + { + get + { + return IsAlivePortableCore; + } + } + + public bool IsBackground + { + get => IsBackgroundPortableCore; + set + { + IsBackgroundPortableCore = value; + } + } + + public bool IsThreadPoolThread + { + get => IsThreadPoolThreadPortableCore; + internal set { + IsThreadPoolThreadPortableCore = value; + } + } + + public ThreadPriority Priority + { + get => PriorityPortableCore; + set + { + PriorityPortableCore = value; + } + } + + public ThreadState ThreadState => ThreadStatePortableCore; + + internal static int OptimalMaxSpinWaitsPerSpinIteration + { + get => OptimalMaxSpinWaitsPerSpinIterationPortableCore; + } private Thread() { } + private unsafe void StartCore() => StartCLRCore(); + + private static void SpinWaitInternal(int iterations) => SpinWaitInternalPortableCore(iterations); + + private static Thread InitializeCurrentThread() => InitializeCurrentThreadPortableCore(); + + private void Initialize() => InitializePortableCore(); + /// Returns handle for interop with EE. The handle is guaranteed to be non-null. internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); - public static void SpinWait(int iterations) => SpinWaitCore(iterations); + public static void SpinWait(int iterations) => SpinWaitPortableCore(iterations); - public static bool Yield() => YieldCore(); + public static bool Yield() => YieldPortableCore(); /// Clean up the thread when it goes away. ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. @@ -59,7 +116,7 @@ public void DisableComObjectEagerCleanup() /// if timeout < -1 (Timeout.Infinite) /// if the thread is interrupted while waiting /// if the thread has not been started yet - public extern bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); + public bool Join(int millisecondsTimeout) => JoinPortableCore(millisecondsTimeout); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 94e24d6adb944..825b41684e438 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -13,6 +13,66 @@ namespace System.Threading { public sealed partial class Thread { + internal ExecutionContext? _executionContext; + internal SynchronizationContext? _synchronizationContext; + + private string? _name; + private StartHelper? _startHelper; + + private ThreadPriority _priority; + private ManagedThreadId _managedThreadId; + + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background + // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, + // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. + private bool _mayNeedResetForThreadPool; + + public int ManagedThreadId + { + [Intrinsic] + get => _managedThreadId.Id; + } + + public bool IsAlive + { + get => IsAliveCore; + } + + public bool IsBackground + { + get => IsBackgroundCore; + set + { + IsBackgroundCore = value; + } + } + + public bool IsThreadPoolThread + { + get => IsThreadPoolThreadCore; + internal set + { + IsThreadPoolThreadCore = value; + } + } + + public ThreadPriority Priority + { + get => PriorityCore; + set + { + PriorityCore = value; + } + } + + public ThreadState ThreadState => ThreadStateCore; + + internal const int OptimalMaxSpinWaitsPerSpinIteration = OptimalMaxSpinWaitsPerSpinIterationCore; + + internal static ulong CurrentOSThreadId + { + get => CurrentOSThreadIdCore; + } private Thread() { @@ -22,6 +82,12 @@ private Thread() RegisterThreadExitCallback(); } + private void StartCore() => StartWindowsThreadPoolCore(); + + private static Thread InitializeCurrentThread() => InitializeCurrentThreadCore(); + + private void Initialize() => InitializeCore(); + internal void SetWaitSleepJoinState() => SetWaitSleepJoinStateCore(); internal void ClearWaitSleepJoinState() => ClearWaitSleepJoinStateCrore(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs index bcfcf50958633..6d3f838bbdae9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs @@ -19,23 +19,12 @@ public sealed partial class Thread // Bits of _threadState that are returned by the ThreadState property private const ThreadState PublicThreadStateMask = (ThreadState)0x1FF; - internal ExecutionContext? _executionContext; - internal SynchronizationContext? _synchronizationContext; - private volatile int _threadState = (int)ThreadState.Unstarted; - private ThreadPriority _priority; - private ManagedThreadId _managedThreadId; - private string? _name; - private StartHelper? _startHelper; + private Exception? _startException; // Protects starting the thread and setting its priority - private Lock _lock = new Lock(); - - // This is used for a quick check on thread pool threads after running a work item to determine if the name, background - // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, - // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. - private bool _mayNeedResetForThreadPool; + private object _lock = new object(); // so far the only place we initialize it is `WaitForForegroundThreads` // and only in the case when there are running foreground threads @@ -44,7 +33,7 @@ public sealed partial class Thread private static int s_foregroundRunningCount; - private void Initialize() + private void InitializeCore() { _priority = ThreadPriority.Normal; _managedThreadId = new ManagedThreadId(); @@ -58,7 +47,7 @@ private static unsafe void RegisterThreadExitCallback() RuntimeImports.RhSetThreadExitCallback(&OnThreadExit); } - internal static ulong CurrentOSThreadId + internal static ulong CurrentOSThreadIdCore { get { @@ -68,7 +57,7 @@ internal static ulong CurrentOSThreadId // Slow path executed once per thread [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThread() + private static Thread InitializeCurrentThreadCore() { Debug.Assert(t_currentThread == null); @@ -103,7 +92,7 @@ private bool HasStarted() return !GetThreadStateBit(ThreadState.Unstarted); } - public bool IsAlive + private bool IsAliveCore { get { @@ -116,7 +105,7 @@ private bool IsDead() return ((ThreadState)_threadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; } - public bool IsBackground + private bool IsBackgroundCore { get { @@ -156,7 +145,7 @@ public bool IsBackground } } - public bool IsThreadPoolThread + private bool IsThreadPoolThreadCore { get { @@ -166,7 +155,7 @@ public bool IsThreadPoolThread } return GetThreadStateBit(ThreadPoolThread); } - internal set + set { if (IsDead()) { @@ -183,16 +172,10 @@ internal set } } - public int ManagedThreadId - { - [Intrinsic] - get => _managedThreadId.Id; - } - // TODO: Inform the debugger and the profiler // private void ThreadNameChanged(string? value) {} - public ThreadPriority Priority + private ThreadPriority PriorityCore { get { @@ -222,7 +205,8 @@ public ThreadPriority Priority } // Prevent race condition with starting this thread - using (LockHolder.Hold(_lock)) + // using (LockHolder.Hold(_lock)) PR-Comment: just keeping this one to make the change easy to notice + lock(_lock) { if (HasStarted() && !SetPriorityLive(value)) { @@ -238,7 +222,7 @@ public ThreadPriority Priority } } - public ThreadState ThreadState => ((ThreadState)_threadState & PublicThreadStateMask); + private ThreadState ThreadStateCore => ((ThreadState)_threadState & PublicThreadStateMask); private bool GetThreadStateBit(ThreadState bit) { @@ -312,7 +296,7 @@ private bool JoinCore(int millisecondsTimeout) /// appropriate for the processor. /// TODO: See issue https://github.com/dotnet/corert/issues/4430 /// - internal const int OptimalMaxSpinWaitsPerSpinIteration = 8; + private const int OptimalMaxSpinWaitsPerSpinIterationCore = 8; // Max iterations to be done in RhSpinWait. // RhSpinWait does not switch GC modes and we want to avoid native spinning in coop mode for too long. @@ -341,16 +325,18 @@ private static void SpinWaitCore(int iterations) } else { - SpinWaitInternal(iterations); + SpinWaitInternalCore(iterations); } } [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. private static bool YieldCore() => RuntimeImports.RhYield(); - private void StartCore() + // private void StartCore() - PR-Comment: Don't know if this rename it's appropriate + private void StartWindowsThreadPoolCore() { - using (LockHolder.Hold(_lock)) + // using (LockHolder.Hold(_lock)) - PR-Comment: just keeping this one to make the change easy to notice + lock(_lock) { if (!GetThreadStateBit(ThreadState.Unstarted)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index d80024f7b8076..fe6430272215d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -369,7 +369,17 @@ public static void Sleep(int millisecondsTimeout) #if !NATIVEAOT /// Returns the operating system identifier for the current thread. - internal static ulong CurrentOSThreadId => GetCurrentOSThreadId(); + internal static ulong CurrentOSThreadId + { + get + { +#if CORECLR && WINDOWS + return ThreadPool.WindowsThreadPool ? CurrentOSThreadIdCore : GetCurrentOSThreadId(); +#else + return GetCurrentOSThreadId(); +#endif + } + } #endif public ExecutionContext? ExecutionContext => ExecutionContext.Capture(); From b5f28596282806015ffe9161a76383232605aeb4 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Mar 2023 18:55:21 -0700 Subject: [PATCH 012/168] Fix next set of errors --- .../System.Private.CoreLib.csproj | 3 +- .../System/Threading/Thread.CoreCLR.Core.cs | 29 +++-------------- .../Threading/Thread.CoreCLR.Windows.cs | 32 +++++++++++++++++-- .../src/System/Threading/Thread.CoreCLR.cs | 20 ++++++++++++ .../src/System/Threading/ManagedThreadId.cs | 2 +- .../Threading/Thread.NativeAot.Windows.cs | 8 ++--- .../src/System/Threading/Thread.NativeAot.cs | 9 ++++++ 7 files changed, 70 insertions(+), 33 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index fcf1165362049..a7795c339bc44 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -292,8 +292,9 @@ - + + Interop\Windows\Kernel32\Interop.ThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs index d645c157effce..3cb9a7be45d28 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs @@ -32,26 +32,6 @@ public sealed partial class Thread internal ExecutionContext? _executionContext; // this call context follows the logical thread internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext - /*========================================================================= - ** The base implementation of Thread is all native. The following fields - ** should never be used in the C# code. They are here to define the proper - ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS - ** YOU MODIFY ThreadBaseObject in vm\object.h - =========================================================================*/ -#pragma warning disable CA1823, 169 // These fields are not used from managed. - // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit - // fields on 64-bit platforms, where they will be sorted together. - - private IntPtr _DONT_USE_InternalThread; // Pointer - private int _priority; // INT32 - - // The following field is required for interop with the VS Debugger - // Prior to making any changes to this field, please reach out to the VS Debugger - // team to make sure that your changes are not going to prevent the debugger - // from working. - private int _managedThreadId; // INT32 -#pragma warning restore CA1823, 169 - public extern int ManagedThreadId { [Intrinsic] @@ -143,7 +123,7 @@ private unsafe void StartCLRCore() { fixed (char* pThreadName = _name) { - StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName); + StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority_portableCore, pThreadName); } } } @@ -180,6 +160,7 @@ private void StartCallback() /// only take a few machine instructions. Calling this API is preferable to coding /// a explicit busy loop because the hardware can be informed that it is busy waiting. /// + [MethodImpl(MethodImplOptions.InternalCall)] private static extern void SpinWaitInternalPortableCore(int iterations); private static void SpinWaitPortableCore(int iterations) => SpinWaitInternal(iterations); @@ -234,7 +215,7 @@ private ApartmentState GetApartmentStatePortableCore() => /// single-threaded or multi-threaded apartment. /// #if FEATURE_COMINTEROP_APARTMENT_SUPPORT - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + private bool SetApartmentStateUncheckedPortableCore(ApartmentState state, bool throwOnError) { ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state); @@ -267,7 +248,7 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) [MethodImpl(MethodImplOptions.InternalCall)] internal extern int SetApartmentStateNative(int state); #else // FEATURE_COMINTEROP_APARTMENT_SUPPORT - private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + private static bool SetApartmentStateUncheckedPortableCore(ApartmentState state, bool throwOnError) { if (state != ApartmentState.Unknown) { @@ -284,7 +265,7 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InterruptCore(); + private extern void InterruptPortableCore(); [MethodImpl(MethodImplOptions.InternalCall)] private extern bool JoinPortableCore(int millisecondsTimeout); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs index a9cd8239bcc18..d0840118f8d9e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs @@ -17,6 +17,29 @@ public sealed partial class Thread private string? _name; private StartHelper? _startHelper; + /*========================================================================= + ** The base implementation of Thread is all native. The following fields + ** should never be used in the C# code. They are here to define the proper + ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS + ** YOU MODIFY ThreadBaseObject in vm\object.h + =========================================================================*/ +#pragma warning disable CA1823, 169 // These fields are not used from managed. + // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit + // fields on 64-bit platforms, where they will be sorted together. + + private IntPtr _DONT_USE_InternalThread; // Pointer + private int _priority_portableCore; // INT32 + + // The following field is required for interop with the VS Debugger + // Prior to making any changes to this field, please reach out to the VS Debugger + // team to make sure that your changes are not going to prevent the debugger + // from working. + private int _managedThreadId_portableCore; // INT32 +#pragma warning restore CA1823, 169 + + private ThreadPriority _priority_core; + private ManagedThreadId _managedThreadId_core; + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. @@ -86,13 +109,16 @@ private Thread() { if (ThreadPool.UseWindowsThreadPool) { - _managedThreadId = System.Threading.ManagedThreadId.GetCurrentThreadId(); + _managedThreadId_core = System.Threading.ManagedThreadId.GetCurrentThreadId(); PlatformSpecificInitialize(); RegisterThreadExitCallback(); } } + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) => + ThreadPool.UseWindowsThreadPool ? SetApartmentStateUncheckedCore(state, throwOnError) : SetApartmentStateUncheckedPortableCore(state, throwOnError); + private unsafe void StartCore() => ThreadPool.UseWindowsThreadPool ? StartWindowsThreadPoolCore() : StartCLRCore(); private static Thread InitializeCurrentThread() => ThreadPool.UseWindowsThreadPool ? InitializeCurrentThreadCore() : InitializeCurrentThreadPortableCore(); @@ -129,7 +155,7 @@ partial void ThreadNameChanged(string? value) InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); } - public ApartmentState GetApartmentState() => GetApartmentStatePortableCore(); + public ApartmentState GetApartmentState() => ThreadPool.UseWindowsThreadPool ? GetApartmentStateCore() : GetApartmentStatePortableCore(); #if FEATURE_COMINTEROP [MethodImpl(MethodImplOptions.InternalCall)] @@ -145,7 +171,7 @@ public void DisableComObjectEagerCleanup() /// thread is not currently blocked in that manner, it will be interrupted /// when it next begins to block. /// - public void Interrupt() => InterruptCore(); + public void Interrupt() => ThreadPool.UseWindowsThreadPool ? InterruptCore() : InterruptPortableCore(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index da96b8c44377d..b8c1eedf10b45 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -16,6 +16,26 @@ public sealed partial class Thread private string? _name; private StartHelper? _startHelper; + /*========================================================================= + ** The base implementation of Thread is all native. The following fields + ** should never be used in the C# code. They are here to define the proper + ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS + ** YOU MODIFY ThreadBaseObject in vm\object.h + =========================================================================*/ +#pragma warning disable CA1823, 169 // These fields are not used from managed. + // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit + // fields on 64-bit platforms, where they will be sorted together. + + private IntPtr _DONT_USE_InternalThread; // Pointer + private int _priority; // INT32 + + // The following field is required for interop with the VS Debugger + // Prior to making any changes to this field, please reach out to the VS Debugger + // team to make sure that your changes are not going to prevent the debugger + // from working. + private int _managedThreadId; // INT32 +#pragma warning restore CA1823, 169 + // This is used for a quick check on thread pool threads after running a work item to determine if the name, background // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs index 38672d2348e5a..ac8268f056c15 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs @@ -238,7 +238,7 @@ public static void RecycleId(int id) var priorIdDispenser = Volatile.Read(ref s_idDispenser); for (;;) { - var updatedIdDispenser = s_idDispenser.RecycleId(id); + var updatedIdDispenser = s_idDispenser!.RecycleId(id); var interlockedResult = Interlocked.CompareExchange(ref s_idDispenser, updatedIdDispenser, priorIdDispenser); if (object.ReferenceEquals(priorIdDispenser, interlockedResult)) break; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index 467e13cfd6033..6acaeff9d31b0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -225,7 +225,7 @@ private static uint ThreadEntryPoint(IntPtr parameter) return 0; } - public ApartmentState GetApartmentState() + private ApartmentState GetApartmentStateCore() { if (this != CurrentThread) { @@ -245,7 +245,7 @@ public ApartmentState GetApartmentState() } } - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + private bool SetApartmentStateUncheckedCore(ApartmentState state, bool throwOnError) { ApartmentState retState; @@ -378,7 +378,7 @@ private static void UninitializeCom() } // TODO: https://github.com/dotnet/runtime/issues/22161 - public void DisableComObjectEagerCleanup() { } + // public void DisableComObjectEagerCleanup() { } private static Thread InitializeExistingThreadPoolThread() { @@ -401,7 +401,7 @@ internal static Thread EnsureThreadPoolThreadInitialized() return InitializeExistingThreadPoolThread(); } - public void Interrupt() { throw new PlatformNotSupportedException(); } + private void InterruptCore() { throw new PlatformNotSupportedException(); } // // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 825b41684e438..f2dc0c8d43ab2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -106,5 +106,14 @@ private Thread() internal static void DecrementRunningForeground() => DecrementRunningForegroundCore(); internal static void WaitForForegroundThreads() => WaitForForegroundThreadsCore(); + +#if WINDOWS + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) => + SetApartmentStateUncheckedCore(state, throwOnError); + + public ApartmentState GetApartmentState() => GetApartmentStateCore(); + + public void Interrupt() => InterruptCore(); +#endif } } From c6a9d1c2e359a09bd7bd39d2f1656a47518cf503 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Mar 2023 17:30:56 -0700 Subject: [PATCH 013/168] Fix more build errors --- .../System.Private.CoreLib.csproj | 9 +- .../Threading/ThreadPool.CoreCLR.Core.cs | 156 ++++++++++++++++ .../Threading/ThreadPool.CoreCLR.Windows.cs | 166 +++++++++++++++-- .../System/Threading/ThreadPool.CoreCLR.cs | 142 +------------- .../Threading/WaitHandle.CoreCLR.Core.cs | 43 +++++ .../Threading/WaitHandle.CoreCLR.Windows.cs | 32 ++++ .../System/Threading/WaitHandle.CoreCLR.cs | 32 +--- .../src/System/Threading/ManagedThreadId.cs | 8 +- .../System/Threading/ThreadPool.Windows.cs | 24 +-- .../System/Threading/WaitHandle.Windows.cs | 164 +---------------- .../Threading/WaitHandle.WindowsThreadPool.cs | 173 ++++++++++++++++++ 11 files changed, 584 insertions(+), 365 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index a7795c339bc44..15036bb82edef 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -234,8 +234,10 @@ - - + + + + @@ -293,8 +295,9 @@ - + + Interop\Windows\Kernel32\Interop.ThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs new file mode 100644 index 0000000000000..a9efb39561bcf --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================================= +** +** +** +** Purpose: Class for creating and managing a threadpool +** +** +=============================================================================*/ + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem + { + void IThreadPoolWorkItem.Execute() => CompleteWait(); + + // Entry point from unmanaged code + private void CompleteWait() + { + PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); + } + } + + internal sealed class UnmanagedThreadPoolWorkItem : IThreadPoolWorkItem + { + private readonly IntPtr _callback; + private readonly IntPtr _state; + + public UnmanagedThreadPoolWorkItem(IntPtr callback, IntPtr state) + { + _callback = callback; + _state = state; + } + + unsafe void IThreadPoolWorkItem.Execute() => ((delegate* unmanaged)_callback)(_state); + } + + public static partial class ThreadPool + { + private static bool EnsureConfigInitializedCore() + { + return s_initialized; + } + + private static readonly bool s_initialized = InitializeConfig(); + + // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that + // the runtime may use the thread for processing other work + internal static bool YieldFromDispatchLoop => false; + + private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking(); + + private static unsafe bool InitializeConfig() + { + int configVariableIndex = 1; + while (true) + { + int nextConfigVariableIndex = + GetNextConfigUInt32Value( + configVariableIndex, + out uint configValue, + out bool isBoolean, + out char* appContextConfigNameUnsafe); + if (nextConfigVariableIndex < 0) + { + break; + } + + Debug.Assert(nextConfigVariableIndex > configVariableIndex); + configVariableIndex = nextConfigVariableIndex; + + Debug.Assert(appContextConfigNameUnsafe != null); + + var appContextConfigName = new string(appContextConfigNameUnsafe); + if (isBoolean) + { + AppContext.SetSwitch(appContextConfigName, configValue != 0); + } + else + { + AppContext.SetData(appContextConfigName, configValue); + } + } + + return true; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe int GetNextConfigUInt32Value( + int configVariableIndex, + out uint configValue, + out bool isBoolean, + out char* appContextConfigName); + + private static bool GetEnableWorkerTracking() => + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); + + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount + { + get + { + return PortableThreadPool.ThreadPoolInstance.ThreadCount; + } + } + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount + { + get + { + return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; + } + } + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object? state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(waitObject); + ArgumentNullException.ThrowIfNull(callBack); + + RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( + waitObject, + new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), + (int)millisecondsTimeOutInterval, + !executeOnlyOnce); + + PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); + + return registeredWaitHandle; + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs index db9a61efba468..cceb5f355d77a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs @@ -17,44 +17,176 @@ public static partial class ThreadPool [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) { - if (overlapped == null) + if (ThreadPool.UseWindowsThreadPool) { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); + return WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); } + else + { + if (overlapped == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); + } - // OS doesn't signal handle, so do it here - overlapped->InternalLow = IntPtr.Zero; + // OS doesn't signal handle, so do it here + overlapped->InternalLow = IntPtr.Zero; - PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); - return true; + PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); + return true; + } } [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] public static bool BindHandle(IntPtr osHandle) { - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); - return true; + if (ThreadPool.UseWindowsThreadPool) + { + return WindowsThreadPool.BindHandle(osHandle); + } + else + { + PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); + return true; + } } [SupportedOSPlatform("windows")] public static bool BindHandle(SafeHandle osHandle) { - ArgumentNullException.ThrowIfNull(osHandle); + if (ThreadPool.UseWindowsThreadPool) + { + return WindowsThreadPool.BindHandle(osHandle); + } + else + { + ArgumentNullException.ThrowIfNull(osHandle); + + bool mustReleaseSafeHandle = false; + try + { + osHandle.DangerousAddRef(ref mustReleaseSafeHandle); + + PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); + return true; + } + finally + { + if (mustReleaseSafeHandle) + osHandle.DangerousRelease(); + } + } + } - bool mustReleaseSafeHandle = false; - try + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); + + internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); + + internal static object GetOrCreateThreadLocalCompletionCountObject() => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject() : + PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); + + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads) : + PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) + { + if (ThreadPool.UseWindowsThreadPool) + { + WindowsThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); + } + else { - osHandle.DangerousAddRef(ref mustReleaseSafeHandle); + PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); + } + } - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); - return true; + public static bool SetMinThreads(int workerThreads, int completionPortThreads) => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads) : + PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) + { + if (ThreadPool.UseWindowsThreadPool) + { + WindowsThreadPool.GetMinThreads(workerThreads, completionPortThreads); + } + else + { + PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); + } + } + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) + { + if (ThreadPool.UseWindowsThreadPool) + { + WindowsThreadPool.GetAvailableThreads(workerThreads, completionPortThreads); + } + else + { + PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); + } + + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void NotifyWorkItemProgress() + { + if (ThreadPool.UseWindowsThreadPool) + { + WindowsThreadPool.NotifyWorkItemProgress(); + } + else + { + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); } - finally + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs) => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs) : + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); + + internal static bool NotifyThreadBlocked() => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.NotifyThreadBlocked() : + PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); + + internal static void NotifyThreadUnblocked() + { + if (ThreadPool.UseWindowsThreadPool) + { + WindowsThreadPool.NotifyThreadUnblocked(); + } + else { - if (mustReleaseSafeHandle) - osHandle.DangerousRelease(); + PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); } } + internal static unsafe void RequestWorkerThread() + { + if (ThreadPool.UseWindowsThreadPool) + { + WindowsThreadPool.RequestWorkerThread(); + } + else + { + PortableThreadPool.ThreadPoolInstance.RequestWorker(); + } + } + + internal static void ReportThreadStatus(bool isWorking) + { + Debug.Assert(!ThreadPool.UseWindowsThreadPool); + PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 6cf6fcf870270..42be2217932a3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -18,90 +18,9 @@ namespace System.Threading { - internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem - { - void IThreadPoolWorkItem.Execute() => CompleteWait(); - - // Entry point from unmanaged code - private void CompleteWait() - { - PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); - } - } - - internal sealed class UnmanagedThreadPoolWorkItem : IThreadPoolWorkItem - { - private readonly IntPtr _callback; - private readonly IntPtr _state; - - public UnmanagedThreadPoolWorkItem(IntPtr callback, IntPtr state) - { - _callback = callback; - _state = state; - } - - unsafe void IThreadPoolWorkItem.Execute() => ((delegate* unmanaged)_callback)(_state); - } - public static partial class ThreadPool { - internal static bool EnsureConfigInitialized() - { - return s_initialized; - } - - private static readonly bool s_initialized = InitializeConfig(); - - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work - internal static bool YieldFromDispatchLoop => false; - - private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking(); - - private static unsafe bool InitializeConfig() - { - int configVariableIndex = 1; - while (true) - { - int nextConfigVariableIndex = - GetNextConfigUInt32Value( - configVariableIndex, - out uint configValue, - out bool isBoolean, - out char* appContextConfigNameUnsafe); - if (nextConfigVariableIndex < 0) - { - break; - } - - Debug.Assert(nextConfigVariableIndex > configVariableIndex); - configVariableIndex = nextConfigVariableIndex; - - Debug.Assert(appContextConfigNameUnsafe != null); - - var appContextConfigName = new string(appContextConfigNameUnsafe); - if (isBoolean) - { - AppContext.SetSwitch(appContextConfigName, configValue != 0); - } - else - { - AppContext.SetData(appContextConfigName, configValue); - } - } - - return true; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe int GetNextConfigUInt32Value( - int configVariableIndex, - out uint configValue, - out bool isBoolean, - out char* appContextConfigName); - - private static bool GetEnableWorkerTracking() => - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { @@ -128,66 +47,9 @@ public static void GetAvailableThreads(out int workerThreads, out int completion PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); } - /// - /// Gets the number of thread pool threads that currently exist. - /// - /// - /// For a thread pool implementation that may have different types of threads, the count includes all types. - /// - public static int ThreadCount - { - get - { - return PortableThreadPool.ThreadPoolInstance.ThreadCount; - } - } - - /// - /// Gets the number of work items that have been processed so far. - /// - /// - /// For a thread pool implementation that may have different types of work items, the count includes all types. - /// - public static long CompletedWorkItemCount - { - get - { - return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; - } - } - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - object? state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(waitObject); - ArgumentNullException.ThrowIfNull(callBack); - - RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( - waitObject, - new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), - (int)millisecondsTimeOutInterval, - !executeOnlyOnce); - - PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); - - return registeredWaitHandle; - } - internal static void RequestWorkerThread() { - if (UseWindowsThreadPool) - { - WindowsThreadPool.RequestWorkerThread(); - } - else - { - PortableThreadPool.ThreadPoolInstance.RequestWorker(); - } + PortableThreadPool.ThreadPoolInstance.RequestWorker(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs new file mode 100644 index 0000000000000..768b91b30562f --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public abstract partial class WaitHandle + { + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int WaitOnePortableCore(IntPtr waitHandle, int millisecondsTimeout); + + private static unsafe int WaitMultipleIgnoringSyncContextPortableCore(Span waitHandles, bool waitAll, int millisecondsTimeout) + { + fixed (IntPtr* pWaitHandles = &MemoryMarshal.GetReference(waitHandles)) + { + return WaitMultipleIgnoringSyncContext(pWaitHandles, waitHandles.Length, waitAll, millisecondsTimeout); + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe int WaitMultipleIgnoringSyncContext(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout); + + private static int SignalAndWaitPortableCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) + { + int ret = SignalAndWaitNative(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); + + if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS) + { + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); + } + + return ret; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe int WaitMultipleIgnoringSyncContextPortableCore(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs new file mode 100644 index 0000000000000..027af17d797cc --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.IO; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public abstract partial class WaitHandle + { + internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) => + ThreadPool.UseWindowsThreadPool ? + WaitMultipleIgnoringSyncContextCore(handles, waitAll, millisecondsTimeout) : + WaitOnePortableCore(waitHandle, millisecondsTimeout); + + internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) => + ThreadPool.UseWindowsThreadPool ? + WaitOneCoreCore(handle, millisecondsTimeout) : + WaitOnePortableCore(waitHandle, millisecondsTimeout); + + internal static Exception ExceptionFromCreationError(int errorCode, string path) => + ExceptionFromCreationErrorCore(errorCode, path); + + private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) => + ThreadPool.UseWindowsThreadPool ? + SignalAndWaitCoreCore(handleToSignal, handleToWaitOn, millisecondsTimeout) : + SignalAndWaitPortableCore(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs index f25dd2c124f73..54c1cd9fa4605 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs @@ -8,33 +8,15 @@ namespace System.Threading { public abstract partial class WaitHandle { - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout); + private static int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout) => WaitOnePortableCore(waitHandle, millisecondsTimeout); - internal static unsafe int WaitMultipleIgnoringSyncContext(Span waitHandles, bool waitAll, int millisecondsTimeout) - { - fixed (IntPtr* pWaitHandles = &MemoryMarshal.GetReference(waitHandles)) - { - return WaitMultipleIgnoringSyncContext(pWaitHandles, waitHandles.Length, waitAll, millisecondsTimeout); - } - } + internal static unsafe int WaitMultipleIgnoringSyncContext(Span waitHandles, bool waitAll, int millisecondsTimeout) => + WaitMultipleIgnoringSyncContextPortableCore(waitHandles, waitAll, millisecondsTimeout); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe int WaitMultipleIgnoringSyncContext(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout); + private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) => + SignalAndWaitPortableCore(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); - private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) - { - int ret = SignalAndWaitNative(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); - - if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS) - { - throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); - } - - return ret; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout); + private static int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) => + SignalAndWaitNativePortableCore(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs index ac8268f056c15..377ae3d2476c6 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs @@ -155,7 +155,7 @@ public ImmutableIdDispenser AllocateId(out int id) int childSize = ChildSize; if (id < childSize) { - left = left.RecycleId(id); + left = left!.RecycleId(id); } else { @@ -167,7 +167,7 @@ public ImmutableIdDispenser AllocateId(out int id) } else { - right = right.RecycleId(id - BitsPerNode); + right = right!.RecycleId(id - BitsPerNode); } } return new ImmutableIdDispenser(left, right, _used - 1, _size, bitmap); @@ -185,7 +185,7 @@ public ImmutableIdDispenser AllocateId(out int id) // when a Thread object cannot be created yet. We also store it in the Thread.CurrentThread object, // because that object may have longer lifetime than the OS thread. [ThreadStatic] - private static ManagedThreadId t_currentThreadId; + private static ManagedThreadId? t_currentThreadId; [ThreadStatic] private static int t_currentManagedThreadId; @@ -265,7 +265,7 @@ public static ManagedThreadId GetCurrentThreadId() if (t_currentManagedThreadId == IdNone) MakeForCurrentThread(); - return t_currentThreadId; + return t_currentThreadId!; } private static int MakeForCurrentThread() diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index aa9659d6870c2..36b9f75219890 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -25,29 +25,13 @@ public static partial class ThreadPool public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads); - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMaxThreads(workerThreads, completionPortThreads); + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); public static bool SetMinThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads); - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMinThreads(workerThreads, completionPortThreads); - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(workerThreads, completionPortThreads); - - /// - /// Gets the number of thread pool threads that currently exist. - /// - /// - /// For a thread pool implementation that may have different types of threads, the count includes all types. - /// - public static int ThreadCount => WindowsThreadPool.ThreadCount; - - /// - /// Gets the number of work items that have been processed so far. - /// - /// - /// For a thread pool implementation that may have different types of work items, the count includes all types. - /// - public static long CompletedWorkItemCount => WindowsThreadPool.CompletedWorkItemCount; + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 18753773a309f..9026c01b3dea5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -10,164 +10,16 @@ namespace System.Threading { public abstract partial class WaitHandle { - internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) - { - fixed (IntPtr* pHandles = &MemoryMarshal.GetReference(handles)) - { - return WaitForMultipleObjectsIgnoringSyncContext(pHandles, handles.Length, waitAll, millisecondsTimeout); - } - } + internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) => + WaitMultipleIgnoringSyncContextCore(handles, waitAll, millisecondsTimeout); - private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout) - { - Debug.Assert(millisecondsTimeout >= -1); + internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) => + WaitOneCoreCore(handle, millisecondsTimeout); - // Normalize waitAll - if (numHandles == 1) - waitAll = false; + internal static Exception ExceptionFromCreationError(int errorCode, string path) => + ExceptionFromCreationErrorCore(errorCode, path); -#if NATIVEAOT // TODO: reentrant wait support https://github.com/dotnet/runtime/issues/49518 - bool reentrantWait = Thread.ReentrantWaitsEnabled; - - if (reentrantWait) - { - // - // In the CLR, we use CoWaitForMultipleHandles to pump messages while waiting in an STA. In that case, we cannot use WAIT_ALL. - // That's because the wait would only be satisfied if a message arrives while the handles are signalled. - // - if (waitAll) - throw new NotSupportedException(SR.NotSupported_WaitAllSTAThread); - - // CoWaitForMultipleHandles does not support more than 63 handles. It returns RPC_S_CALLPENDING for more than 63 handles - // that is impossible to differentiate from timeout. - if (numHandles > 63) - throw new NotSupportedException(SR.NotSupported_MaxWaitHandles_STA); - } -#endif - - Thread currentThread = Thread.CurrentThread; - currentThread.SetWaitSleepJoinState(); - -#if NATIVEAOT - int result; - if (reentrantWait) - { - Debug.Assert(!waitAll); - result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); - } - else - { - result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); - } -#else - int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); -#endif - currentThread.ClearWaitSleepJoinState(); - - if (result == Interop.Kernel32.WAIT_FAILED) - { - int errorCode = Interop.Kernel32.GetLastError(); - if (waitAll && errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - { - // Check for duplicate handles. This is a brute force O(n^2) search, which is intended since the typical - // array length is short enough that this would actually be faster than using a hash set. Also, the worst - // case is not so bad considering that the array length is limited by - // . - for (int i = 1; i < numHandles; ++i) - { - IntPtr handle = pHandles[i]; - for (int j = 0; j < i; ++j) - { - if (pHandles[j] == handle) - { - throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); - } - } - } - } - - ThrowWaitFailedException(errorCode); - } - - return result; - } - - internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) - { - return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout); - } - - private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) - { - Debug.Assert(millisecondsTimeout >= -1); - - int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.FALSE); - - if (ret == Interop.Kernel32.WAIT_FAILED) - { - ThrowWaitFailedException(Interop.Kernel32.GetLastError()); - } - - return ret; - } - - private static void ThrowWaitFailedException(int errorCode) - { - switch (errorCode) - { - case Interop.Errors.ERROR_INVALID_HANDLE: - ThrowInvalidHandleException(); - return; - - case Interop.Errors.ERROR_INVALID_PARAMETER: - throw new ArgumentException(); - - case Interop.Errors.ERROR_ACCESS_DENIED: - throw new UnauthorizedAccessException(); - - case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: - throw new OutOfMemoryException(); - - case Interop.Errors.ERROR_TOO_MANY_POSTS: - // Only applicable to . Note however, that - // if the semahpore already has the maximum signal count, the Windows SignalObjectAndWait function does not - // return an error, but this code is kept for historical reasons and to convey the intent, since ideally, - // that should be an error. - throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); - - case Interop.Errors.ERROR_NOT_OWNER: - // Only applicable to when signaling a mutex - // that is locked by a different thread. Note that if the mutex is already unlocked, the Windows - // SignalObjectAndWait function does not return an error. - throw new ApplicationException(SR.Arg_SynchronizationLockException); - - case Interop.Errors.ERROR_MUTANT_LIMIT_EXCEEDED: - throw new OverflowException(SR.Overflow_MutexReacquireCount); - - default: - throw new Exception { HResult = errorCode }; - } - } - - internal static Exception ExceptionFromCreationError(int errorCode, string path) - { - switch (errorCode) - { - case Interop.Errors.ERROR_PATH_NOT_FOUND: - return new IOException(SR.Format(SR.IO_PathNotFound_Path, path)); - - case Interop.Errors.ERROR_ACCESS_DENIED: - return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); - - case Interop.Errors.ERROR_ALREADY_EXISTS: - return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path)); - - case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - return new PathTooLongException(); - - default: - return new IOException(SR.Arg_IOException, errorCode); - } - } + private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) => + SignalAndWaitCoreCore(handleToSignal, handleToWaitOn, millisecondsTimeout); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs new file mode 100644 index 0000000000000..3cfb739f8b7bf --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs @@ -0,0 +1,173 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.IO; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public abstract partial class WaitHandle + { + private static unsafe int WaitMultipleIgnoringSyncContextCore(Span handles, bool waitAll, int millisecondsTimeout) + { + fixed (IntPtr* pHandles = &MemoryMarshal.GetReference(handles)) + { + return WaitForMultipleObjectsIgnoringSyncContext(pHandles, handles.Length, waitAll, millisecondsTimeout); + } + } + + private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); + + // Normalize waitAll + if (numHandles == 1) + waitAll = false; + +#if NATIVEAOT // TODO: reentrant wait support https://github.com/dotnet/runtime/issues/49518 + bool reentrantWait = Thread.ReentrantWaitsEnabled; + + if (reentrantWait) + { + // + // In the CLR, we use CoWaitForMultipleHandles to pump messages while waiting in an STA. In that case, we cannot use WAIT_ALL. + // That's because the wait would only be satisfied if a message arrives while the handles are signalled. + // + if (waitAll) + throw new NotSupportedException(SR.NotSupported_WaitAllSTAThread); + + // CoWaitForMultipleHandles does not support more than 63 handles. It returns RPC_S_CALLPENDING for more than 63 handles + // that is impossible to differentiate from timeout. + if (numHandles > 63) + throw new NotSupportedException(SR.NotSupported_MaxWaitHandles_STA); + } +#endif + + Thread currentThread = Thread.CurrentThread; + currentThread.SetWaitSleepJoinState(); + +#if NATIVEAOT + int result; + if (reentrantWait) + { + Debug.Assert(!waitAll); + result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); + } + else + { + result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + } +#else + int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); +#endif + currentThread.ClearWaitSleepJoinState(); + + if (result == Interop.Kernel32.WAIT_FAILED) + { + int errorCode = Interop.Kernel32.GetLastError(); + if (waitAll && errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) + { + // Check for duplicate handles. This is a brute force O(n^2) search, which is intended since the typical + // array length is short enough that this would actually be faster than using a hash set. Also, the worst + // case is not so bad considering that the array length is limited by + // . + for (int i = 1; i < numHandles; ++i) + { + IntPtr handle = pHandles[i]; + for (int j = 0; j < i; ++j) + { + if (pHandles[j] == handle) + { + throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); + } + } + } + } + + ThrowWaitFailedException(errorCode); + } + + return result; + } + + private static unsafe int WaitOneCoreCore(IntPtr handle, int millisecondsTimeout) + { + return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout); + } + + private static int SignalAndWaitCoreCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); + + int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + + if (ret == Interop.Kernel32.WAIT_FAILED) + { + ThrowWaitFailedException(Interop.Kernel32.GetLastError()); + } + + return ret; + } + + private static void ThrowWaitFailedException(int errorCode) + { + switch (errorCode) + { + case Interop.Errors.ERROR_INVALID_HANDLE: + ThrowInvalidHandleException(); + return; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + throw new ArgumentException(); + + case Interop.Errors.ERROR_ACCESS_DENIED: + throw new UnauthorizedAccessException(); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + case Interop.Errors.ERROR_TOO_MANY_POSTS: + // Only applicable to . Note however, that + // if the semahpore already has the maximum signal count, the Windows SignalObjectAndWait function does not + // return an error, but this code is kept for historical reasons and to convey the intent, since ideally, + // that should be an error. + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); + + case Interop.Errors.ERROR_NOT_OWNER: + // Only applicable to when signaling a mutex + // that is locked by a different thread. Note that if the mutex is already unlocked, the Windows + // SignalObjectAndWait function does not return an error. + throw new ApplicationException(SR.Arg_SynchronizationLockException); + + case Interop.Errors.ERROR_MUTANT_LIMIT_EXCEEDED: + throw new OverflowException(SR.Overflow_MutexReacquireCount); + + default: + throw new Exception { HResult = errorCode }; + } + } + + private static Exception ExceptionFromCreationErrorCore(int errorCode, string path) + { + switch (errorCode) + { + case Interop.Errors.ERROR_PATH_NOT_FOUND: + return new IOException(SR.Format(SR.IO_PathNotFound_Path, path)); + + case Interop.Errors.ERROR_ACCESS_DENIED: + return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); + + case Interop.Errors.ERROR_ALREADY_EXISTS: + return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path)); + + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: + return new PathTooLongException(); + + default: + return new IOException(SR.Arg_IOException, errorCode); + } + } + } +} From b7ddda0d217e5fbb98f6de8e54157317de4263c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Apr 2023 20:53:12 -0700 Subject: [PATCH 014/168] Restore Thread files --- .../System.Private.CoreLib.csproj | 47 +- .../System/Threading/Thread.CoreCLR.Core.cs | 285 ----------- .../Threading/Thread.CoreCLR.Windows.cs | 179 ------- .../src/System/Threading/Thread.CoreCLR.cs | 272 ++++++++-- .../Threading/ThreadPool.CoreCLR.Windows.cs | 5 +- .../Threading/WaitHandle.CoreCLR.Windows.cs | 6 +- .../Threading/Thread.NativeAot.Windows.cs | 8 +- .../src/System/Threading/Thread.NativeAot.cs | 460 +++++++++++++++-- .../Win32ThreadPoolRegisteredWaitHandle.cs | 2 +- .../System.Private.CoreLib.Shared.projitems | 2 +- .../RegisteredWaitHandle.WindowsThreadPool.cs | 27 +- .../Threading/Thread.WindowsThreadPool.cs | 483 ------------------ .../src/System/Threading/WindowsThreadPool.cs | 5 +- 13 files changed, 738 insertions(+), 1043 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 15036bb82edef..fa4966036fe6f 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -112,6 +112,7 @@ $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src')) $(MSBuildThisFileDirectory)src $(MSBuildThisFileDirectory)..\nativeaot\System.Private.CoreLib\src + $(NativeAotSourcesRoot)\..\..\Common\src @@ -232,8 +233,7 @@ - - + @@ -294,14 +294,47 @@ - - - - - + Interop\Windows\Kernel32\Interop.ThreadPool.cs + + + + + + + + + + + + + + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs deleted file mode 100644 index 3cb9a7be45d28..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Core.cs +++ /dev/null @@ -1,285 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Globalization; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Runtime.Versioning; - -// PR-Comment: This implementation comes from Thread.CoreCLR.cs (src\coreclr\System.Private.CoreLib\src\System\Threading\Thread.CoreCLR.cs) - -namespace System.Threading -{ - internal readonly struct ThreadHandle - { - private readonly IntPtr _ptr; - - internal ThreadHandle(IntPtr pThread) - { - _ptr = pThread; - } - } - - public sealed partial class Thread - { - /*========================================================================= - ** Data accessed from managed code that needs to be defined in - ** ThreadBaseObject to maintain alignment between the two classes. - ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h - =========================================================================*/ - internal ExecutionContext? _executionContext; // this call context follows the logical thread - internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext - - public extern int ManagedThreadId - { - [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - - private extern bool IsAlivePortableCore - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - - /// - /// Return whether or not this thread is a background thread. Background - /// threads do not affect when the Execution Engine shuts down. - /// - private bool IsBackgroundPortableCore - { - get => IsBackgroundNative(); - set - { - SetBackgroundNative(value); - if (!value) - { - _mayNeedResetForThreadPool = true; - } - } - } - - /// Returns true if the thread is a threadpool thread. - private extern bool IsThreadPoolThreadPortableCore - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - [MethodImpl(MethodImplOptions.InternalCall)] - set; - } - - /// Returns the priority of the thread. - private ThreadPriority PriorityPortableCore - { - get => (ThreadPriority)GetPriorityNative(); - set - { - SetPriorityNative((int)value); - if (value != ThreadPriority.Normal) - { - _mayNeedResetForThreadPool = true; - } - } - } - - /// - /// Return the thread state as a consistent set of bits. This is more - /// general then IsAlive or IsBackground. - /// - private ThreadState ThreadStatePortableCore => (ThreadState)GetThreadStateNative(); - - - /// - /// Max value to be passed into for optimal delaying. This value is normalized to be - /// appropriate for the processor. - /// - private static int OptimalMaxSpinWaitsPerSpinIterationPortableCore - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } - - /// Returns handle for interop with EE. The handle is guaranteed to be non-null. - private ThreadHandle GetNativeHandleCore() - { - IntPtr thread = _DONT_USE_InternalThread; - - // This should never happen under normal circumstances. - if (thread == IntPtr.Zero) - { - throw new ArgumentException(null, SR.Argument_InvalidHandle); - } - - return new ThreadHandle(thread); - } - - // private unsafe void StartCore() - PR-Comment: Don't know if this rename it's appropriate - private unsafe void StartCLRCore() - { - lock (this) - { - fixed (char* pThreadName = _name) - { - StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority_portableCore, pThreadName); - } - } - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")] - private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName); - - // Called from the runtime - private void StartCallback() - { - StartHelper? startHelper = _startHelper; - Debug.Assert(startHelper != null); - _startHelper = null; - - startHelper.Run(); - } - - // Invoked by VM. Helper method to get a logical thread ID for StringBuilder (for - // correctness) and for FileStream's async code path (for perf, to avoid creating - // a Thread instance). - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr InternalGetCurrentThread(); - - /// - /// Suspends the current thread for timeout milliseconds. If timeout == 0, - /// forces the thread to give up the remainder of its timeslice. If timeout - /// == Timeout.Infinite, no timeout will occur. - /// - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void SleepInternal(int millisecondsTimeout); - - /// - /// Wait for a length of time proportional to 'iterations'. Each iteration is should - /// only take a few machine instructions. Calling this API is preferable to coding - /// a explicit busy loop because the hardware can be informed that it is busy waiting. - /// - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void SpinWaitInternalPortableCore(int iterations); - - private static void SpinWaitPortableCore(int iterations) => SpinWaitInternal(iterations); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_YieldThread")] - private static partial Interop.BOOL YieldInternal(); - - private static bool YieldPortableCore() => YieldInternal() != Interop.BOOL.FALSE; - - [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThreadPortableCore() => t_currentThread = GetCurrentThreadNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern Thread GetCurrentThreadNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InitializePortableCore(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InternalFinalize(); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_InformThreadNameChange", StringMarshalling = StringMarshalling.Utf16)] - private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool IsBackgroundNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void SetBackgroundNative(bool isBackground); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern int GetPriorityNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void SetPriorityNative(int priority); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentOSThreadId")] - private static partial ulong GetCurrentOSThreadId(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern int GetThreadStateNative(); - - private ApartmentState GetApartmentStatePortableCore() => -#if FEATURE_COMINTEROP_APARTMENT_SUPPORT - (ApartmentState)GetApartmentStateNative(); -#else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT - ApartmentState.Unknown; -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - - /// - /// An unstarted thread can be marked to indicate that it will host a - /// single-threaded or multi-threaded apartment. - /// -#if FEATURE_COMINTEROP_APARTMENT_SUPPORT - private bool SetApartmentStateUncheckedPortableCore(ApartmentState state, bool throwOnError) - { - ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state); - - // Special case where we pass in Unknown and get back MTA. - // Once we CoUninitialize the thread, the OS will still - // report the thread as implicitly in the MTA if any - // other thread in the process is CoInitialized. - if ((state == System.Threading.ApartmentState.Unknown) && (retState == System.Threading.ApartmentState.MTA)) - { - return true; - } - - if (retState != state) - { - if (throwOnError) - { - string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); - throw new InvalidOperationException(msg); - } - - return false; - } - - return true; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern int GetApartmentStateNative(); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern int SetApartmentStateNative(int state); -#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT - private static bool SetApartmentStateUncheckedPortableCore(ApartmentState state, bool throwOnError) - { - if (state != ApartmentState.Unknown) - { - if (throwOnError) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); - } - - return false; - } - - return true; - } -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void InterruptPortableCore(); - - [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool JoinPortableCore(int millisecondsTimeout); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ResetThreadPoolThreadCore() - { - Debug.Assert(this == CurrentThread); - Debug.Assert(IsThreadPoolThread); - - if (_mayNeedResetForThreadPool) - { - ResetThreadPoolThreadSlow(); - } - } - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs deleted file mode 100644 index d0840118f8d9e..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.Windows.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Globalization; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Runtime.Versioning; - -namespace System.Threading -{ - public sealed partial class Thread - { - private string? _name; - private StartHelper? _startHelper; - - /*========================================================================= - ** The base implementation of Thread is all native. The following fields - ** should never be used in the C# code. They are here to define the proper - ** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS - ** YOU MODIFY ThreadBaseObject in vm\object.h - =========================================================================*/ -#pragma warning disable CA1823, 169 // These fields are not used from managed. - // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit - // fields on 64-bit platforms, where they will be sorted together. - - private IntPtr _DONT_USE_InternalThread; // Pointer - private int _priority_portableCore; // INT32 - - // The following field is required for interop with the VS Debugger - // Prior to making any changes to this field, please reach out to the VS Debugger - // team to make sure that your changes are not going to prevent the debugger - // from working. - private int _managedThreadId_portableCore; // INT32 -#pragma warning restore CA1823, 169 - - private ThreadPriority _priority_core; - private ManagedThreadId _managedThreadId_core; - - // This is used for a quick check on thread pool threads after running a work item to determine if the name, background - // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, - // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. - private bool _mayNeedResetForThreadPool; - - public bool IsAlive - { - get => ThreadPool.UseWindowsThreadPool ? IsAliveCore : IsAlivePortableCore; - } - - public bool IsBackground - { - get => ThreadPool.UseWindowsThreadPool ? IsBackgroundCore : IsBackgroundPortableCore; - set - { - if (ThreadPool.UseWindowsThreadPool) - { - IsBackgroundCore = value; - } - else - { - IsBackgroundPortableCore = value; - } - } - } - - public bool IsThreadPoolThread - { - get => ThreadPool.UseWindowsThreadPool ? IsThreadPoolThreadCore : IsThreadPoolThreadPortableCore; - internal set - { - if (ThreadPool.UseWindowsThreadPool) - { - IsThreadPoolThreadCore = value; - } - else - { - IsThreadPoolThreadPortableCore = value; - } - } - } - - public ThreadPriority Priority - { - get => ThreadPool.UseWindowsThreadPool ? PriorityCore : PriorityPortableCore; - set - { - if (ThreadPool.UseWindowsThreadPool) - { - PriorityCore = value; - } - else - { - PriorityPortableCore = value; - } - } - } - - public ThreadState ThreadState => ThreadPool.UseWindowsThreadPool ? ThreadStateCore : ThreadStatePortableCore; - - internal static int OptimalMaxSpinWaitsPerSpinIteration - { - get => ThreadPool.UseWindowsThreadPool ? OptimalMaxSpinWaitsPerSpinIterationCore : OptimalMaxSpinWaitsPerSpinIterationPortableCore; - } - - private Thread() - { - if (ThreadPool.UseWindowsThreadPool) - { - _managedThreadId_core = System.Threading.ManagedThreadId.GetCurrentThreadId(); - - PlatformSpecificInitialize(); - RegisterThreadExitCallback(); - } - } - - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) => - ThreadPool.UseWindowsThreadPool ? SetApartmentStateUncheckedCore(state, throwOnError) : SetApartmentStateUncheckedPortableCore(state, throwOnError); - - private unsafe void StartCore() => ThreadPool.UseWindowsThreadPool ? StartWindowsThreadPoolCore() : StartCLRCore(); - - private static Thread InitializeCurrentThread() => ThreadPool.UseWindowsThreadPool ? InitializeCurrentThreadCore() : InitializeCurrentThreadPortableCore(); - - private void Initialize() => ThreadPool.UseWindowsThreadPool ? InitializeCore() : InitializePortableCore(); - - public bool Join(int millisecondsTimeout) => ThreadPool.UseWindowsThreadPool ? JoinCore(millisecondsTimeout) : JoinPortableCore(millisecondsTimeout); - - internal void SetWaitSleepJoinState() => SetWaitSleepJoinStateCore(); - - internal void ClearWaitSleepJoinState() => ClearWaitSleepJoinStateCrore(); - - private static void SpinWaitInternal(int iterations) => ThreadPool.UseWindowsThreadPool ? SpinWaitInternalCore(iterations) : SpinWaitInternalPortableCore(iterations); - - public static void SpinWait(int iterations) => ThreadPool.UseWindowsThreadPool ? SpinWaitCore(iterations) : SpinWaitPortableCore(iterations); - - [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - public static bool Yield() => ThreadPool.UseWindowsThreadPool ? YieldCore() : YieldPortableCore(); - - internal static void IncrementRunningForeground() => IncrementRunningForegroundCore(); - - internal static void DecrementRunningForeground() => DecrementRunningForegroundCore(); - - internal static void WaitForForegroundThreads() => WaitForForegroundThreadsCore(); - - /// Returns handle for interop with EE. The handle is guaranteed to be non-null. - internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); - - /// Clean up the thread when it goes away. - ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. - - partial void ThreadNameChanged(string? value) - { - InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); - } - - public ApartmentState GetApartmentState() => ThreadPool.UseWindowsThreadPool ? GetApartmentStateCore() : GetApartmentStatePortableCore(); - -#if FEATURE_COMINTEROP - [MethodImpl(MethodImplOptions.InternalCall)] - public extern void DisableComObjectEagerCleanup(); -#else // !FEATURE_COMINTEROP - public void DisableComObjectEagerCleanup() - { - } -#endif // FEATURE_COMINTEROP - - /// - /// Interrupts a thread that is inside a Wait(), Sleep() or Join(). If that - /// thread is not currently blocked in that manner, it will be interrupted - /// when it next begins to block. - /// - public void Interrupt() => ThreadPool.UseWindowsThreadPool ? InterruptCore() : InterruptPortableCore(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index b8c1eedf10b45..4f3083be8a269 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -10,9 +10,26 @@ namespace System.Threading { + internal readonly struct ThreadHandle + { + private readonly IntPtr _ptr; + + internal ThreadHandle(IntPtr pThread) + { + _ptr = pThread; + } + } public sealed partial class Thread { + /*========================================================================= + ** Data accessed from managed code that needs to be defined in + ** ThreadBaseObject to maintain alignment between the two classes. + ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h + =========================================================================*/ + internal ExecutionContext? _executionContext; // this call context follows the logical thread + internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext + private string? _name; private StartHelper? _startHelper; @@ -41,74 +58,236 @@ public sealed partial class Thread // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. private bool _mayNeedResetForThreadPool; - /// Returns true if the thread has been started and is not dead. - public bool IsAlive + private Thread() { } + + public extern int ManagedThreadId + { + [Intrinsic] + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// Returns handle for interop with EE. The handle is guaranteed to be non-null. + internal ThreadHandle GetNativeHandle() + { + IntPtr thread = _DONT_USE_InternalThread; + + // This should never happen under normal circumstances. + if (thread == IntPtr.Zero) + { + throw new ArgumentException(null, SR.Argument_InvalidHandle); + } + + return new ThreadHandle(thread); + } + + private unsafe void StartCore() { - get + lock (this) { - return IsAlivePortableCore; + fixed (char* pThreadName = _name) + { + StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName); + } } } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")] + private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName); + + // Called from the runtime + private void StartCallback() + { + StartHelper? startHelper = _startHelper; + Debug.Assert(startHelper != null); + _startHelper = null; + + startHelper.Run(); + } + + // Invoked by VM. Helper method to get a logical thread ID for StringBuilder (for + // correctness) and for FileStream's async code path (for perf, to avoid creating + // a Thread instance). + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr InternalGetCurrentThread(); + + /// + /// Suspends the current thread for timeout milliseconds. If timeout == 0, + /// forces the thread to give up the remainder of its timeslice. If timeout + /// == Timeout.Infinite, no timeout will occur. + /// + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void SleepInternal(int millisecondsTimeout); + + /// + /// Wait for a length of time proportional to 'iterations'. Each iteration is should + /// only take a few machine instructions. Calling this API is preferable to coding + /// a explicit busy loop because the hardware can be informed that it is busy waiting. + /// + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void SpinWaitInternal(int iterations); + + public static void SpinWait(int iterations) => SpinWaitInternal(iterations); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_YieldThread")] + private static partial Interop.BOOL YieldInternal(); + + public static bool Yield() => YieldInternal() != Interop.BOOL.FALSE; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern Thread GetCurrentThreadNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void Initialize(); + + /// Clean up the thread when it goes away. + ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void InternalFinalize(); + + partial void ThreadNameChanged(string? value) + { + InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_InformThreadNameChange", StringMarshalling = StringMarshalling.Utf16)] + private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len); + + /// Returns true if the thread has been started and is not dead. + public extern bool IsAlive + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } + + /// + /// Return whether or not this thread is a background thread. Background + /// threads do not affect when the Execution Engine shuts down. + /// public bool IsBackground { - get => IsBackgroundPortableCore; + get => IsBackgroundNative(); set { - IsBackgroundPortableCore = value; + SetBackgroundNative(value); + if (!value) + { + _mayNeedResetForThreadPool = true; + } } } - public bool IsThreadPoolThread + [MethodImpl(MethodImplOptions.InternalCall)] + private extern bool IsBackgroundNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void SetBackgroundNative(bool isBackground); + + /// Returns true if the thread is a threadpool thread. + public extern bool IsThreadPoolThread { - get => IsThreadPoolThreadPortableCore; - internal set { - IsThreadPoolThreadPortableCore = value; - } + [MethodImpl(MethodImplOptions.InternalCall)] + get; + [MethodImpl(MethodImplOptions.InternalCall)] + internal set; } + /// Returns the priority of the thread. public ThreadPriority Priority { - get => PriorityPortableCore; + get => (ThreadPriority)GetPriorityNative(); set { - PriorityPortableCore = value; + SetPriorityNative((int)value); + if (value != ThreadPriority.Normal) + { + _mayNeedResetForThreadPool = true; + } } } - public ThreadState ThreadState => ThreadStatePortableCore; + [MethodImpl(MethodImplOptions.InternalCall)] + private extern int GetPriorityNative(); - internal static int OptimalMaxSpinWaitsPerSpinIteration - { - get => OptimalMaxSpinWaitsPerSpinIterationPortableCore; - } + [MethodImpl(MethodImplOptions.InternalCall)] + private extern void SetPriorityNative(int priority); - private Thread() { } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentOSThreadId")] + private static partial ulong GetCurrentOSThreadId(); - private unsafe void StartCore() => StartCLRCore(); + /// + /// Return the thread state as a consistent set of bits. This is more + /// general then IsAlive or IsBackground. + /// + public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); - private static void SpinWaitInternal(int iterations) => SpinWaitInternalPortableCore(iterations); + [MethodImpl(MethodImplOptions.InternalCall)] + private extern int GetThreadStateNative(); - private static Thread InitializeCurrentThread() => InitializeCurrentThreadPortableCore(); + public ApartmentState GetApartmentState() => +#if FEATURE_COMINTEROP_APARTMENT_SUPPORT + (ApartmentState)GetApartmentStateNative(); +#else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT + ApartmentState.Unknown; +#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - private void Initialize() => InitializePortableCore(); + /// + /// An unstarted thread can be marked to indicate that it will host a + /// single-threaded or multi-threaded apartment. + /// +#if FEATURE_COMINTEROP_APARTMENT_SUPPORT + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) + { + ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state); - /// Returns handle for interop with EE. The handle is guaranteed to be non-null. - internal ThreadHandle GetNativeHandle() => GetNativeHandleCore(); + // Special case where we pass in Unknown and get back MTA. + // Once we CoUninitialize the thread, the OS will still + // report the thread as implicitly in the MTA if any + // other thread in the process is CoInitialized. + if ((state == System.Threading.ApartmentState.Unknown) && (retState == System.Threading.ApartmentState.MTA)) + { + return true; + } - public static void SpinWait(int iterations) => SpinWaitPortableCore(iterations); + if (retState != state) + { + if (throwOnError) + { + string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState); + throw new InvalidOperationException(msg); + } - public static bool Yield() => YieldPortableCore(); + return false; + } - /// Clean up the thread when it goes away. - ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. + return true; + } - partial void ThreadNameChanged(string? value) + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern int GetApartmentStateNative(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern int SetApartmentStateNative(int state); +#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT + private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) { - InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0); - } + if (state != ApartmentState.Unknown) + { + if (throwOnError) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } - public ApartmentState GetApartmentState() => GetApartmentStatePortableCore(); + return false; + } + + return true; + } +#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT #if FEATURE_COMINTEROP [MethodImpl(MethodImplOptions.InternalCall)] @@ -124,7 +303,8 @@ public void DisableComObjectEagerCleanup() /// thread is not currently blocked in that manner, it will be interrupted /// when it next begins to block. /// - public extern void Interrupt() => InterruptCore(); + [MethodImpl(MethodImplOptions.InternalCall)] + public extern void Interrupt(); /// /// Waits for the thread to die or for timeout milliseconds to elapse. @@ -136,9 +316,29 @@ public void DisableComObjectEagerCleanup() /// if timeout < -1 (Timeout.Infinite) /// if the thread is interrupted while waiting /// if the thread has not been started yet - public bool Join(int millisecondsTimeout) => JoinPortableCore(millisecondsTimeout); + [MethodImpl(MethodImplOptions.InternalCall)] + public extern bool Join(int millisecondsTimeout); + + /// + /// Max value to be passed into for optimal delaying. This value is normalized to be + /// appropriate for the processor. + /// + internal static int OptimalMaxSpinWaitsPerSpinIteration + { + [MethodImpl(MethodImplOptions.InternalCall)] + get; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ResetThreadPoolThread() => ResetThreadPoolThreadCore(); + internal void ResetThreadPoolThread() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(IsThreadPoolThread); + + if (_mayNeedResetForThreadPool) + { + ResetThreadPoolThreadSlow(); + } + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs index cceb5f355d77a..5fba962862bf9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -116,7 +117,7 @@ public static void GetMinThreads(out int workerThreads, out int completionPortTh { if (ThreadPool.UseWindowsThreadPool) { - WindowsThreadPool.GetMinThreads(workerThreads, completionPortThreads); + WindowsThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); } else { @@ -127,7 +128,7 @@ public static void GetAvailableThreads(out int workerThreads, out int completion { if (ThreadPool.UseWindowsThreadPool) { - WindowsThreadPool.GetAvailableThreads(workerThreads, completionPortThreads); + WindowsThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); } else { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs index 027af17d797cc..2f92827ed760d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs @@ -14,12 +14,12 @@ public abstract partial class WaitHandle internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) => ThreadPool.UseWindowsThreadPool ? WaitMultipleIgnoringSyncContextCore(handles, waitAll, millisecondsTimeout) : - WaitOnePortableCore(waitHandle, millisecondsTimeout); + WaitMultipleIgnoringSyncContextPortableCore(handles, waitAll, millisecondsTimeout); internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) => ThreadPool.UseWindowsThreadPool ? WaitOneCoreCore(handle, millisecondsTimeout) : - WaitOnePortableCore(waitHandle, millisecondsTimeout); + WaitOnePortableCore(handle, millisecondsTimeout); internal static Exception ExceptionFromCreationError(int errorCode, string path) => ExceptionFromCreationErrorCore(errorCode, path); @@ -27,6 +27,6 @@ internal static Exception ExceptionFromCreationError(int errorCode, string path) private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) => ThreadPool.UseWindowsThreadPool ? SignalAndWaitCoreCore(handleToSignal, handleToWaitOn, millisecondsTimeout) : - SignalAndWaitPortableCore(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); + SignalAndWaitPortableCore(handleToSignal, handleToWaitOn, millisecondsTimeout); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index 6acaeff9d31b0..467e13cfd6033 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -225,7 +225,7 @@ private static uint ThreadEntryPoint(IntPtr parameter) return 0; } - private ApartmentState GetApartmentStateCore() + public ApartmentState GetApartmentState() { if (this != CurrentThread) { @@ -245,7 +245,7 @@ private ApartmentState GetApartmentStateCore() } } - private bool SetApartmentStateUncheckedCore(ApartmentState state, bool throwOnError) + private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) { ApartmentState retState; @@ -378,7 +378,7 @@ private static void UninitializeCom() } // TODO: https://github.com/dotnet/runtime/issues/22161 - // public void DisableComObjectEagerCleanup() { } + public void DisableComObjectEagerCleanup() { } private static Thread InitializeExistingThreadPoolThread() { @@ -401,7 +401,7 @@ internal static Thread EnsureThreadPoolThreadInitialized() return InitializeExistingThreadPoolThread(); } - private void InterruptCore() { throw new PlatformNotSupportedException(); } + public void Interrupt() { throw new PlatformNotSupportedException(); } // // Suppresses reentrant waits on the current thread, until a matching call to RestoreReentrantWaits. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index f2dc0c8d43ab2..cda114276746c 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -13,107 +13,493 @@ namespace System.Threading { public sealed partial class Thread { + // Extra bits used in _threadState + private const ThreadState ThreadPoolThread = (ThreadState)0x1000; + + // Bits of _threadState that are returned by the ThreadState property + private const ThreadState PublicThreadStateMask = (ThreadState)0x1FF; + internal ExecutionContext? _executionContext; internal SynchronizationContext? _synchronizationContext; + private volatile int _threadState = (int)ThreadState.Unstarted; + private ThreadPriority _priority; + private ManagedThreadId _managedThreadId; private string? _name; private StartHelper? _startHelper; + private Exception? _startException; - private ThreadPriority _priority; - private ManagedThreadId _managedThreadId; + // Protects starting the thread and setting its priority + private Lock _lock = new Lock(); // This is used for a quick check on thread pool threads after running a work item to determine if the name, background // state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those, // but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized. private bool _mayNeedResetForThreadPool; - public int ManagedThreadId + // so far the only place we initialize it is `WaitForForegroundThreads` + // and only in the case when there are running foreground threads + // by the moment of `StartupCodeHelpers.Shutdown()` invocation + private static ManualResetEvent s_allDone; + + private static int s_foregroundRunningCount; + + private Thread() { - [Intrinsic] - get => _managedThreadId.Id; + _managedThreadId = System.Threading.ManagedThreadId.GetCurrentThreadId(); + + PlatformSpecificInitialize(); + RegisterThreadExitCallback(); + } + + private void Initialize() + { + _priority = ThreadPriority.Normal; + _managedThreadId = new ManagedThreadId(); + + PlatformSpecificInitialize(); + RegisterThreadExitCallback(); + } + + private static unsafe void RegisterThreadExitCallback() + { + RuntimeImports.RhSetThreadExitCallback(&OnThreadExit); + } + + internal static ulong CurrentOSThreadId + { + get + { + return RuntimeImports.RhCurrentOSThreadId(); + } + } + + // Slow path executed once per thread + [MethodImpl(MethodImplOptions.NoInlining)] + private static Thread InitializeCurrentThread() + { + Debug.Assert(t_currentThread == null); + + var currentThread = new Thread(); + Debug.Assert(currentThread._threadState == (int)ThreadState.Unstarted); + + ThreadState state = 0; + + // The main thread is foreground, other ones are background + if (currentThread._managedThreadId.Id != System.Threading.ManagedThreadId.IdMainThread) + { + state |= ThreadState.Background; + } + else + { + Interlocked.Increment(ref s_foregroundRunningCount); + } + + currentThread._threadState = (int)(state | ThreadState.Running); + currentThread.PlatformSpecificInitializeExistingThread(); + currentThread._priority = currentThread.GetPriorityLive(); + t_currentThread = currentThread; + + return currentThread; + } + + /// + /// Returns true if the underlying OS thread has been created and started execution of managed code. + /// + private bool HasStarted() + { + return !GetThreadStateBit(ThreadState.Unstarted); } public bool IsAlive { - get => IsAliveCore; + get + { + return ((ThreadState)_threadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0; + } + } + + private bool IsDead() + { + return ((ThreadState)_threadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; } public bool IsBackground { - get => IsBackgroundCore; + get + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + return GetThreadStateBit(ThreadState.Background); + } set { - IsBackgroundCore = value; + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + // we changing foreground count only for started threads + // on thread start we count its state in `StartThread` + if (value) + { + int threadState = SetThreadStateBit(ThreadState.Background); + // was foreground and has started + if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == 0) + { + DecrementRunningForeground(); + } + } + else + { + int threadState = ClearThreadStateBit(ThreadState.Background); + // was background and has started + if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == (int)ThreadState.Background) + { + IncrementRunningForeground(); + _mayNeedResetForThreadPool = true; + } + } } } public bool IsThreadPoolThread { - get => IsThreadPoolThreadCore; + get + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + return GetThreadStateBit(ThreadPoolThread); + } internal set { - IsThreadPoolThreadCore = value; + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + if (value) + { + SetThreadStateBit(ThreadPoolThread); + } + else + { + ClearThreadStateBit(ThreadPoolThread); + } } } + public int ManagedThreadId + { + [Intrinsic] + get => _managedThreadId.Id; + } + + // TODO: Inform the debugger and the profiler + // private void ThreadNameChanged(string? value) {} + public ThreadPriority Priority { - get => PriorityCore; + get + { + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_Priority); + } + if (!HasStarted()) + { + // The thread has not been started yet; return the value assigned to the Priority property. + // Race condition with setting the priority or starting the thread is OK, we may return an old value. + return _priority; + } + // The priority might have been changed by external means. Obtain the actual value from the OS + // rather than using the value saved in _priority. + return GetPriorityLive(); + } set { - PriorityCore = value; + if ((value < ThreadPriority.Lowest) || (ThreadPriority.Highest < value)) + { + throw new ArgumentOutOfRangeException(SR.Argument_InvalidFlag); + } + if (IsDead()) + { + throw new ThreadStateException(SR.ThreadState_Dead_Priority); + } + + // Prevent race condition with starting this thread + using (LockHolder.Hold(_lock)) + { + if (HasStarted() && !SetPriorityLive(value)) + { + throw new ThreadStateException(SR.ThreadState_SetPriorityFailed); + } + _priority = value; + } + + if (value != ThreadPriority.Normal) + { + _mayNeedResetForThreadPool = true; + } } } - public ThreadState ThreadState => ThreadStateCore; + public ThreadState ThreadState => ((ThreadState)_threadState & PublicThreadStateMask); - internal const int OptimalMaxSpinWaitsPerSpinIteration = OptimalMaxSpinWaitsPerSpinIterationCore; + private bool GetThreadStateBit(ThreadState bit) + { + Debug.Assert((bit & ThreadState.Stopped) == 0, "ThreadState.Stopped bit may be stale; use GetThreadState instead."); + return (_threadState & (int)bit) != 0; + } - internal static ulong CurrentOSThreadId + private int SetThreadStateBit(ThreadState bit) { - get => CurrentOSThreadIdCore; + int oldState, newState; + do + { + oldState = _threadState; + newState = oldState | (int)bit; + } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); + return oldState; } - private Thread() + private int ClearThreadStateBit(ThreadState bit) { - _managedThreadId = System.Threading.ManagedThreadId.GetCurrentThreadId(); + int oldState, newState; + do + { + oldState = _threadState; + newState = oldState & ~(int)bit; + } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); + return oldState; + } - PlatformSpecificInitialize(); - RegisterThreadExitCallback(); + internal void SetWaitSleepJoinState() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(!GetThreadStateBit(ThreadState.WaitSleepJoin)); + + SetThreadStateBit(ThreadState.WaitSleepJoin); } - private void StartCore() => StartWindowsThreadPoolCore(); + internal void ClearWaitSleepJoinState() + { + Debug.Assert(this == CurrentThread); + Debug.Assert(GetThreadStateBit(ThreadState.WaitSleepJoin)); - private static Thread InitializeCurrentThread() => InitializeCurrentThreadCore(); + ClearThreadStateBit(ThreadState.WaitSleepJoin); + } - private void Initialize() => InitializeCore(); + private static int VerifyTimeoutMilliseconds(int millisecondsTimeout) + { + if (millisecondsTimeout < -1) + { + throw new ArgumentOutOfRangeException( + nameof(millisecondsTimeout), + millisecondsTimeout, + SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + } + return millisecondsTimeout; + } - internal void SetWaitSleepJoinState() => SetWaitSleepJoinStateCore(); + public bool Join(int millisecondsTimeout) + { + VerifyTimeoutMilliseconds(millisecondsTimeout); + if (GetThreadStateBit(ThreadState.Unstarted)) + { + throw new ThreadStateException(SR.ThreadState_NotStarted); + } + return JoinInternal(millisecondsTimeout); + } - internal void ClearWaitSleepJoinState() => ClearWaitSleepJoinStateCrore(); + /// + /// Max value to be passed into for optimal delaying. Currently, the value comes from + /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be + /// appropriate for the processor. + /// TODO: See issue https://github.com/dotnet/corert/issues/4430 + /// + internal const int OptimalMaxSpinWaitsPerSpinIteration = 8; - public bool Join(int millisecondsTimeout) => JoinCore(millisecondsTimeout); + // Max iterations to be done in RhSpinWait. + // RhSpinWait does not switch GC modes and we want to avoid native spinning in coop mode for too long. + private const int SpinWaitCoopThreshold = 1024; - internal static void SpinWaitInternal(int iterations) => SpinWaitInternalCore(iterations); + internal static void SpinWaitInternal(int iterations) + { + Debug.Assert(iterations <= SpinWaitCoopThreshold); + if (iterations > 0) + { + RuntimeImports.RhSpinWait(iterations); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. + private static void LongSpinWait(int iterations) + { + RuntimeImports.RhLongSpinWait(iterations); + } - public static void SpinWait(int iterations) => SpinWaitCore(); + public static void SpinWait(int iterations) + { + if (iterations > SpinWaitCoopThreshold) + { + LongSpinWait(iterations); + } + else + { + SpinWaitInternal(iterations); + } + } [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - public static bool Yield() => YieldCore(); + public static bool Yield() => RuntimeImports.RhYield(); - internal static void IncrementRunningForeground() => IncrementRunningForegroundCore(); + private void StartCore() + { + using (LockHolder.Hold(_lock)) + { + if (!GetThreadStateBit(ThreadState.Unstarted)) + { + throw new ThreadStateException(SR.ThreadState_AlreadyStarted); + } - internal static void DecrementRunningForeground() => DecrementRunningForegroundCore(); + bool waitingForThreadStart = false; + GCHandle threadHandle = GCHandle.Alloc(this); - internal static void WaitForForegroundThreads() => WaitForForegroundThreadsCore(); + try + { + if (!CreateThread(threadHandle)) + { + throw new OutOfMemoryException(); + } -#if WINDOWS - private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) => - SetApartmentStateUncheckedCore(state, throwOnError); + // Skip cleanup if any asynchronous exception happens while waiting for the thread start + waitingForThreadStart = true; - public ApartmentState GetApartmentState() => GetApartmentStateCore(); + // Wait until the new thread either dies or reports itself as started + while (GetThreadStateBit(ThreadState.Unstarted) && !JoinInternal(0)) + { + Yield(); + } - public void Interrupt() => InterruptCore(); + waitingForThreadStart = false; + } + finally + { + Debug.Assert(!waitingForThreadStart, "Leaked threadHandle"); + if (!waitingForThreadStart) + { + threadHandle.Free(); + } + } + + if (GetThreadStateBit(ThreadState.Unstarted)) + { + Exception? startException = _startException; + _startException = null; + + throw new ThreadStartException(startException ?? new OutOfMemoryException()); + } + + Debug.Assert(_startException == null); + } + } + + private static void StartThread(IntPtr parameter) + { + GCHandle threadHandle = (GCHandle)parameter; + Thread thread = (Thread)threadHandle.Target!; + + try + { + t_currentThread = thread; + System.Threading.ManagedThreadId.SetForCurrentThread(thread._managedThreadId); + thread.InitializeComOnNewThread(); + } + catch (Exception e) + { + thread._startException = e; + +#if TARGET_UNIX + // This should go away once OnThreadExit stops using t_currentThread to signal + // shutdown of the thread on Unix. + thread._stopped!.Set(); #endif + // Terminate the current thread. The creator thread will throw a ThreadStartException. + return; + } + + // Report success to the creator thread, which will free threadHandle + int state = thread.ClearThreadStateBit(ThreadState.Unstarted); + if ((state & (int)ThreadState.Background) == 0) + { + IncrementRunningForeground(); + } + + try + { + StartHelper? startHelper = thread._startHelper; + Debug.Assert(startHelper != null); + thread._startHelper = null; + + startHelper.Run(); + } + finally + { + thread.SetThreadStateBit(ThreadState.Stopped); + } + } + + private static void StopThread(Thread thread) + { + if ((thread._threadState & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) + { + thread.SetThreadStateBit(ThreadState.Stopped); + } + + int state = thread.ClearThreadStateBit(ThreadState.Background); + if ((state & (int)ThreadState.Background) == 0) + { + DecrementRunningForeground(); + } + } + + internal static void IncrementRunningForeground() + { + Interlocked.Increment(ref s_foregroundRunningCount); + } + + internal static void DecrementRunningForeground() + { + if (Interlocked.Decrement(ref s_foregroundRunningCount) == 0) + { + // Interlocked.Decrement issues full memory barrier + // so most recent write to s_allDone should be visible here + s_allDone?.Set(); + } + } + + internal static void WaitForForegroundThreads() + { + Thread.CurrentThread.IsBackground = true; + // last read/write inside `IsBackground` issues full barrier no matter of logic flow + // so we can just read `s_foregroundRunningCount` + if (s_foregroundRunningCount == 0) + { + // current thread is the last foreground thread, so let the runtime finish + return; + } + Volatile.Write(ref s_allDone, new ManualResetEvent(false)); + // other foreground threads could have their job finished meanwhile + // Volatile.Write above issues release barrier + // but we need acquire barrier to observe most recent write to s_foregroundRunningCount + if (Volatile.Read(ref s_foregroundRunningCount) == 0) + { + return; + } + s_allDone.WaitOne(); + } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs index a460216520458..c31ef2baad360 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -18,7 +18,7 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - private readonly object _lock; + private readonly Lock _lock; private SafeWaitHandle _waitHandle; private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; private readonly uint _millisecondsTimeout; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 6dc6f5a835aad..addf4d902aba6 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2031,8 +2031,8 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 33763c770c4a3..ab39c752593f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -33,6 +33,7 @@ private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, private void PerformCallbackCore(bool timedOut) { + /* PR-Comment: Previous implementation bool lockAcquired; var spinner = new SpinWait(); @@ -68,8 +69,28 @@ private void PerformCallbackCore(bool timedOut) _lock.Release(); } } + */ - _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper, timedOut); + // If another thread is running Unregister, no need to restart the timer or clean up + lock (_lock!) + { + if (!_unregistering) + { + if (_repeating) + { + // Allow this wait to fire again. Restart the timer before executing the callback. + RestartWait(); + } + else + { + // This wait will not be fired again. Free the GC handle to allow the GC to collect this object. + Debug.Assert(_gcHandle.IsAllocated); + _gcHandle.Free(); + } + } + } + + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper!, timedOut); } private unsafe void RestartWaitCore() @@ -84,13 +105,13 @@ private unsafe void RestartWaitCore() } // We can use DangerousGetHandle because of DangerousAddRef in the constructor - Interop.Kernel32.SetThreadpoolWait(_tpWait, _waitHandle.DangerousGetHandle(), (IntPtr)pTimeout); + Interop.Kernel32.SetThreadpoolWait(_tpWait, _waitHandle!.DangerousGetHandle(), (IntPtr)pTimeout); } private bool UnregisterCore(WaitHandle waitObject) { // Hold the lock during the synchronous part of Unregister (as in CoreCLR) - using (LockHolder.Hold(_lock)) + lock(_lock) { if (!_unregistering) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs deleted file mode 100644 index 6d3f838bbdae9..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.WindowsThreadPool.cs +++ /dev/null @@ -1,483 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Globalization; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Threading -{ - public sealed partial class Thread - { - // Extra bits used in _threadState - private const ThreadState ThreadPoolThread = (ThreadState)0x1000; - - // Bits of _threadState that are returned by the ThreadState property - private const ThreadState PublicThreadStateMask = (ThreadState)0x1FF; - - private volatile int _threadState = (int)ThreadState.Unstarted; - - private Exception? _startException; - - // Protects starting the thread and setting its priority - private object _lock = new object(); - - // so far the only place we initialize it is `WaitForForegroundThreads` - // and only in the case when there are running foreground threads - // by the moment of `StartupCodeHelpers.Shutdown()` invocation - private static ManualResetEvent s_allDone; - - private static int s_foregroundRunningCount; - - private void InitializeCore() - { - _priority = ThreadPriority.Normal; - _managedThreadId = new ManagedThreadId(); - - PlatformSpecificInitialize(); - RegisterThreadExitCallback(); - } - - private static unsafe void RegisterThreadExitCallback() - { - RuntimeImports.RhSetThreadExitCallback(&OnThreadExit); - } - - internal static ulong CurrentOSThreadIdCore - { - get - { - return RuntimeImports.RhCurrentOSThreadId(); - } - } - - // Slow path executed once per thread - [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThreadCore() - { - Debug.Assert(t_currentThread == null); - - var currentThread = new Thread(); - Debug.Assert(currentThread._threadState == (int)ThreadState.Unstarted); - - ThreadState state = 0; - - // The main thread is foreground, other ones are background - if (currentThread._managedThreadId.Id != System.Threading.ManagedThreadId.IdMainThread) - { - state |= ThreadState.Background; - } - else - { - Interlocked.Increment(ref s_foregroundRunningCount); - } - - currentThread._threadState = (int)(state | ThreadState.Running); - currentThread.PlatformSpecificInitializeExistingThread(); - currentThread._priority = currentThread.GetPriorityLive(); - t_currentThread = currentThread; - - return currentThread; - } - - /// - /// Returns true if the underlying OS thread has been created and started execution of managed code. - /// - private bool HasStarted() - { - return !GetThreadStateBit(ThreadState.Unstarted); - } - - private bool IsAliveCore - { - get - { - return ((ThreadState)_threadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0; - } - } - - private bool IsDead() - { - return ((ThreadState)_threadState & (ThreadState.Stopped | ThreadState.Aborted)) != 0; - } - - private bool IsBackgroundCore - { - get - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - return GetThreadStateBit(ThreadState.Background); - } - set - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - // we changing foreground count only for started threads - // on thread start we count its state in `StartThread` - if (value) - { - int threadState = SetThreadStateBit(ThreadState.Background); - // was foreground and has started - if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == 0) - { - DecrementRunningForeground(); - } - } - else - { - int threadState = ClearThreadStateBit(ThreadState.Background); - // was background and has started - if ((threadState & ((int)ThreadState.Background | (int)ThreadState.Unstarted | (int)ThreadState.Stopped)) == (int)ThreadState.Background) - { - IncrementRunningForeground(); - _mayNeedResetForThreadPool = true; - } - } - } - } - - private bool IsThreadPoolThreadCore - { - get - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - return GetThreadStateBit(ThreadPoolThread); - } - set - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_State); - } - if (value) - { - SetThreadStateBit(ThreadPoolThread); - } - else - { - ClearThreadStateBit(ThreadPoolThread); - } - } - } - - // TODO: Inform the debugger and the profiler - // private void ThreadNameChanged(string? value) {} - - private ThreadPriority PriorityCore - { - get - { - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_Priority); - } - if (!HasStarted()) - { - // The thread has not been started yet; return the value assigned to the Priority property. - // Race condition with setting the priority or starting the thread is OK, we may return an old value. - return _priority; - } - // The priority might have been changed by external means. Obtain the actual value from the OS - // rather than using the value saved in _priority. - return GetPriorityLive(); - } - set - { - if ((value < ThreadPriority.Lowest) || (ThreadPriority.Highest < value)) - { - throw new ArgumentOutOfRangeException(SR.Argument_InvalidFlag); - } - if (IsDead()) - { - throw new ThreadStateException(SR.ThreadState_Dead_Priority); - } - - // Prevent race condition with starting this thread - // using (LockHolder.Hold(_lock)) PR-Comment: just keeping this one to make the change easy to notice - lock(_lock) - { - if (HasStarted() && !SetPriorityLive(value)) - { - throw new ThreadStateException(SR.ThreadState_SetPriorityFailed); - } - _priority = value; - } - - if (value != ThreadPriority.Normal) - { - _mayNeedResetForThreadPool = true; - } - } - } - - private ThreadState ThreadStateCore => ((ThreadState)_threadState & PublicThreadStateMask); - - private bool GetThreadStateBit(ThreadState bit) - { - Debug.Assert((bit & ThreadState.Stopped) == 0, "ThreadState.Stopped bit may be stale; use GetThreadState instead."); - return (_threadState & (int)bit) != 0; - } - - private int SetThreadStateBit(ThreadState bit) - { - int oldState, newState; - do - { - oldState = _threadState; - newState = oldState | (int)bit; - } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); - return oldState; - } - - private int ClearThreadStateBit(ThreadState bit) - { - int oldState, newState; - do - { - oldState = _threadState; - newState = oldState & ~(int)bit; - } while (Interlocked.CompareExchange(ref _threadState, newState, oldState) != oldState); - return oldState; - } - - private void SetWaitSleepJoinStateCore() - { - Debug.Assert(this == CurrentThread); - Debug.Assert(!GetThreadStateBit(ThreadState.WaitSleepJoin)); - - SetThreadStateBit(ThreadState.WaitSleepJoin); - } - - private void ClearWaitSleepJoinStateCore() - { - Debug.Assert(this == CurrentThread); - Debug.Assert(GetThreadStateBit(ThreadState.WaitSleepJoin)); - - ClearThreadStateBit(ThreadState.WaitSleepJoin); - } - - private static int VerifyTimeoutMilliseconds(int millisecondsTimeout) - { - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), - millisecondsTimeout, - SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - } - return millisecondsTimeout; - } - - private bool JoinCore(int millisecondsTimeout) - { - VerifyTimeoutMilliseconds(millisecondsTimeout); - if (GetThreadStateBit(ThreadState.Unstarted)) - { - throw new ThreadStateException(SR.ThreadState_NotStarted); - } - return JoinInternal(millisecondsTimeout); - } - - /// - /// Max value to be passed into for optimal delaying. Currently, the value comes from - /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be - /// appropriate for the processor. - /// TODO: See issue https://github.com/dotnet/corert/issues/4430 - /// - private const int OptimalMaxSpinWaitsPerSpinIterationCore = 8; - - // Max iterations to be done in RhSpinWait. - // RhSpinWait does not switch GC modes and we want to avoid native spinning in coop mode for too long. - private const int SpinWaitCoopThreshold = 1024; - - private static void SpinWaitInternalCore(int iterations) - { - Debug.Assert(iterations <= SpinWaitCoopThreshold); - if (iterations > 0) - { - RuntimeImports.RhSpinWait(iterations); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - private static void LongSpinWait(int iterations) - { - RuntimeImports.RhLongSpinWait(iterations); - } - - private static void SpinWaitCore(int iterations) - { - if (iterations > SpinWaitCoopThreshold) - { - LongSpinWait(iterations); - } - else - { - SpinWaitInternalCore(iterations); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. - private static bool YieldCore() => RuntimeImports.RhYield(); - - // private void StartCore() - PR-Comment: Don't know if this rename it's appropriate - private void StartWindowsThreadPoolCore() - { - // using (LockHolder.Hold(_lock)) - PR-Comment: just keeping this one to make the change easy to notice - lock(_lock) - { - if (!GetThreadStateBit(ThreadState.Unstarted)) - { - throw new ThreadStateException(SR.ThreadState_AlreadyStarted); - } - - bool waitingForThreadStart = false; - GCHandle threadHandle = GCHandle.Alloc(this); - - try - { - if (!CreateThread(threadHandle)) - { - throw new OutOfMemoryException(); - } - - // Skip cleanup if any asynchronous exception happens while waiting for the thread start - waitingForThreadStart = true; - - // Wait until the new thread either dies or reports itself as started - while (GetThreadStateBit(ThreadState.Unstarted) && !JoinInternal(0)) - { - Yield(); - } - - waitingForThreadStart = false; - } - finally - { - Debug.Assert(!waitingForThreadStart, "Leaked threadHandle"); - if (!waitingForThreadStart) - { - threadHandle.Free(); - } - } - - if (GetThreadStateBit(ThreadState.Unstarted)) - { - Exception? startException = _startException; - _startException = null; - - throw new ThreadStartException(startException ?? new OutOfMemoryException()); - } - - Debug.Assert(_startException == null); - } - } - - private static void StartThread(IntPtr parameter) - { - GCHandle threadHandle = (GCHandle)parameter; - Thread thread = (Thread)threadHandle.Target!; - - try - { - t_currentThread = thread; - System.Threading.ManagedThreadId.SetForCurrentThread(thread._managedThreadId); - thread.InitializeComOnNewThread(); - } - catch (Exception e) - { - thread._startException = e; - -#if TARGET_UNIX - // This should go away once OnThreadExit stops using t_currentThread to signal - // shutdown of the thread on Unix. - thread._stopped!.Set(); -#endif - // Terminate the current thread. The creator thread will throw a ThreadStartException. - return; - } - - // Report success to the creator thread, which will free threadHandle - int state = thread.ClearThreadStateBit(ThreadState.Unstarted); - if ((state & (int)ThreadState.Background) == 0) - { - IncrementRunningForeground(); - } - - try - { - StartHelper? startHelper = thread._startHelper; - Debug.Assert(startHelper != null); - thread._startHelper = null; - - startHelper.Run(); - } - finally - { - thread.SetThreadStateBit(ThreadState.Stopped); - } - } - - private static void StopThread(Thread thread) - { - if ((thread._threadState & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) - { - thread.SetThreadStateBit(ThreadState.Stopped); - } - - int state = thread.ClearThreadStateBit(ThreadState.Background); - if ((state & (int)ThreadState.Background) == 0) - { - DecrementRunningForeground(); - } - } - - private static void IncrementRunningForegroundCore() - { - Interlocked.Increment(ref s_foregroundRunningCount); - } - - private static void DecrementRunningForegroundCore() - { - if (Interlocked.Decrement(ref s_foregroundRunningCount) == 0) - { - // Interlocked.Decrement issues full memory barrier - // so most recent write to s_allDone should be visible here - s_allDone?.Set(); - } - } - - private static void WaitForForegroundThreadsCore() - { - Thread.CurrentThread.IsBackground = true; - // last read/write inside `IsBackground` issues full barrier no matter of logic flow - // so we can just read `s_foregroundRunningCount` - if (s_foregroundRunningCount == 0) - { - // current thread is the last foreground thread, so let the runtime finish - return; - } - Volatile.Write(ref s_allDone, new ManualResetEvent(false)); - // other foreground threads could have their job finished meanwhile - // Volatile.Write above issues release barrier - // but we need acquire barrier to observe most recent write to s_foregroundRunningCount - if (Volatile.Read(ref s_foregroundRunningCount) == 0) - { - return; - } - s_allDone.WaitOne(); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 9ce37d35008df..3721360f57603 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -149,13 +149,14 @@ internal static void NotifyThreadUnblocked() { } [UnmanagedCallersOnly] private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { - var wrapper = ThreadPoolCallbackWrapper.Enter(); + // PR-Comment: Assuming this is no longer necessary, might be wrong about this + // var wrapper = ThreadPoolCallbackWrapper.Enter(); Debug.Assert(s_work == work); Interlocked.Increment(ref s_workingThreadCounter.Count); ThreadPoolWorkQueue.Dispatch(); Interlocked.Decrement(ref s_workingThreadCounter.Count); // We reset the thread after executing each callback - wrapper.Exit(resetThread: false); + // wrapper.Exit(resetThread: false); } internal static unsafe void RequestWorkerThread() From 13894d0429d4006fafffa8fbf15fc8f49027a937 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Apr 2023 21:08:28 -0700 Subject: [PATCH 015/168] Adding some comments --- .../System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs | 1 + .../Threading/RegisteredWaitHandle.WindowsThreadPool.cs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs index c31ef2baad360..8964b19a24fe0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -13,6 +13,7 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // + // PR-Comment: This implementation was previously in ThreadPool.Windows.cs #if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] #endif diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index ab39c752593f8..f1dd5ef33d5f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -20,7 +20,8 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject { private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { - var wrapper = ThreadPoolCallbackWrapper.Enter(); + // PR-Comment: Assuming this is no longer necessary, might be wrong about this + // var wrapper = ThreadPoolCallbackWrapper.Enter(); GCHandle handle = (GCHandle)context; RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); @@ -28,7 +29,7 @@ private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); registeredWaitHandle.PerformCallback(timedOut); ThreadPool.IncrementCompletedWorkItemCount(); - wrapper.Exit(); + // wrapper.Exit(); } private void PerformCallbackCore(bool timedOut) From 7367244036a138b5ce4dd2ddd8282730f57deaba Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 5 Apr 2023 16:20:32 -0700 Subject: [PATCH 016/168] Restore WaitHandle --- .../Threading/WaitHandle.CoreCLR.Core.cs | 43 ----- .../Threading/WaitHandle.CoreCLR.Windows.cs | 32 ---- .../System/Threading/WaitHandle.CoreCLR.cs | 32 +++- .../System/Threading/WaitHandle.Windows.cs | 164 ++++++++++++++++- .../Threading/WaitHandle.WindowsThreadPool.cs | 173 ------------------ 5 files changed, 181 insertions(+), 263 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs deleted file mode 100644 index 768b91b30562f..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Core.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - public abstract partial class WaitHandle - { - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int WaitOnePortableCore(IntPtr waitHandle, int millisecondsTimeout); - - private static unsafe int WaitMultipleIgnoringSyncContextPortableCore(Span waitHandles, bool waitAll, int millisecondsTimeout) - { - fixed (IntPtr* pWaitHandles = &MemoryMarshal.GetReference(waitHandles)) - { - return WaitMultipleIgnoringSyncContext(pWaitHandles, waitHandles.Length, waitAll, millisecondsTimeout); - } - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe int WaitMultipleIgnoringSyncContext(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout); - - private static int SignalAndWaitPortableCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) - { - int ret = SignalAndWaitNative(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); - - if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS) - { - throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); - } - - return ret; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe int WaitMultipleIgnoringSyncContextPortableCore(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs deleted file mode 100644 index 2f92827ed760d..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.IO; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - public abstract partial class WaitHandle - { - internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) => - ThreadPool.UseWindowsThreadPool ? - WaitMultipleIgnoringSyncContextCore(handles, waitAll, millisecondsTimeout) : - WaitMultipleIgnoringSyncContextPortableCore(handles, waitAll, millisecondsTimeout); - - internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) => - ThreadPool.UseWindowsThreadPool ? - WaitOneCoreCore(handle, millisecondsTimeout) : - WaitOnePortableCore(handle, millisecondsTimeout); - - internal static Exception ExceptionFromCreationError(int errorCode, string path) => - ExceptionFromCreationErrorCore(errorCode, path); - - private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) => - ThreadPool.UseWindowsThreadPool ? - SignalAndWaitCoreCore(handleToSignal, handleToWaitOn, millisecondsTimeout) : - SignalAndWaitPortableCore(handleToSignal, handleToWaitOn, millisecondsTimeout); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs index 54c1cd9fa4605..f25dd2c124f73 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs @@ -8,15 +8,33 @@ namespace System.Threading { public abstract partial class WaitHandle { - private static int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout) => WaitOnePortableCore(waitHandle, millisecondsTimeout); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout); - internal static unsafe int WaitMultipleIgnoringSyncContext(Span waitHandles, bool waitAll, int millisecondsTimeout) => - WaitMultipleIgnoringSyncContextPortableCore(waitHandles, waitAll, millisecondsTimeout); + internal static unsafe int WaitMultipleIgnoringSyncContext(Span waitHandles, bool waitAll, int millisecondsTimeout) + { + fixed (IntPtr* pWaitHandles = &MemoryMarshal.GetReference(waitHandles)) + { + return WaitMultipleIgnoringSyncContext(pWaitHandles, waitHandles.Length, waitAll, millisecondsTimeout); + } + } - private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) => - SignalAndWaitPortableCore(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe int WaitMultipleIgnoringSyncContext(IntPtr* waitHandles, int numHandles, bool waitAll, int millisecondsTimeout); - private static int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) => - SignalAndWaitNativePortableCore(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); + private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) + { + int ret = SignalAndWaitNative(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); + + if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS) + { + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); + } + + return ret; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int SignalAndWaitNative(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 9026c01b3dea5..18753773a309f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -10,16 +10,164 @@ namespace System.Threading { public abstract partial class WaitHandle { - internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) => - WaitMultipleIgnoringSyncContextCore(handles, waitAll, millisecondsTimeout); + internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) + { + fixed (IntPtr* pHandles = &MemoryMarshal.GetReference(handles)) + { + return WaitForMultipleObjectsIgnoringSyncContext(pHandles, handles.Length, waitAll, millisecondsTimeout); + } + } - internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) => - WaitOneCoreCore(handle, millisecondsTimeout); + private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); - internal static Exception ExceptionFromCreationError(int errorCode, string path) => - ExceptionFromCreationErrorCore(errorCode, path); + // Normalize waitAll + if (numHandles == 1) + waitAll = false; - private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) => - SignalAndWaitCoreCore(handleToSignal, handleToWaitOn, millisecondsTimeout); +#if NATIVEAOT // TODO: reentrant wait support https://github.com/dotnet/runtime/issues/49518 + bool reentrantWait = Thread.ReentrantWaitsEnabled; + + if (reentrantWait) + { + // + // In the CLR, we use CoWaitForMultipleHandles to pump messages while waiting in an STA. In that case, we cannot use WAIT_ALL. + // That's because the wait would only be satisfied if a message arrives while the handles are signalled. + // + if (waitAll) + throw new NotSupportedException(SR.NotSupported_WaitAllSTAThread); + + // CoWaitForMultipleHandles does not support more than 63 handles. It returns RPC_S_CALLPENDING for more than 63 handles + // that is impossible to differentiate from timeout. + if (numHandles > 63) + throw new NotSupportedException(SR.NotSupported_MaxWaitHandles_STA); + } +#endif + + Thread currentThread = Thread.CurrentThread; + currentThread.SetWaitSleepJoinState(); + +#if NATIVEAOT + int result; + if (reentrantWait) + { + Debug.Assert(!waitAll); + result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); + } + else + { + result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + } +#else + int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); +#endif + currentThread.ClearWaitSleepJoinState(); + + if (result == Interop.Kernel32.WAIT_FAILED) + { + int errorCode = Interop.Kernel32.GetLastError(); + if (waitAll && errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) + { + // Check for duplicate handles. This is a brute force O(n^2) search, which is intended since the typical + // array length is short enough that this would actually be faster than using a hash set. Also, the worst + // case is not so bad considering that the array length is limited by + // . + for (int i = 1; i < numHandles; ++i) + { + IntPtr handle = pHandles[i]; + for (int j = 0; j < i; ++j) + { + if (pHandles[j] == handle) + { + throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); + } + } + } + } + + ThrowWaitFailedException(errorCode); + } + + return result; + } + + internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) + { + return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout); + } + + private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); + + int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + + if (ret == Interop.Kernel32.WAIT_FAILED) + { + ThrowWaitFailedException(Interop.Kernel32.GetLastError()); + } + + return ret; + } + + private static void ThrowWaitFailedException(int errorCode) + { + switch (errorCode) + { + case Interop.Errors.ERROR_INVALID_HANDLE: + ThrowInvalidHandleException(); + return; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + throw new ArgumentException(); + + case Interop.Errors.ERROR_ACCESS_DENIED: + throw new UnauthorizedAccessException(); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + case Interop.Errors.ERROR_TOO_MANY_POSTS: + // Only applicable to . Note however, that + // if the semahpore already has the maximum signal count, the Windows SignalObjectAndWait function does not + // return an error, but this code is kept for historical reasons and to convey the intent, since ideally, + // that should be an error. + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); + + case Interop.Errors.ERROR_NOT_OWNER: + // Only applicable to when signaling a mutex + // that is locked by a different thread. Note that if the mutex is already unlocked, the Windows + // SignalObjectAndWait function does not return an error. + throw new ApplicationException(SR.Arg_SynchronizationLockException); + + case Interop.Errors.ERROR_MUTANT_LIMIT_EXCEEDED: + throw new OverflowException(SR.Overflow_MutexReacquireCount); + + default: + throw new Exception { HResult = errorCode }; + } + } + + internal static Exception ExceptionFromCreationError(int errorCode, string path) + { + switch (errorCode) + { + case Interop.Errors.ERROR_PATH_NOT_FOUND: + return new IOException(SR.Format(SR.IO_PathNotFound_Path, path)); + + case Interop.Errors.ERROR_ACCESS_DENIED: + return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); + + case Interop.Errors.ERROR_ALREADY_EXISTS: + return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path)); + + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: + return new PathTooLongException(); + + default: + return new IOException(SR.Arg_IOException, errorCode); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs deleted file mode 100644 index 3cfb739f8b7bf..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.WindowsThreadPool.cs +++ /dev/null @@ -1,173 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.IO; -using System.Runtime; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - public abstract partial class WaitHandle - { - private static unsafe int WaitMultipleIgnoringSyncContextCore(Span handles, bool waitAll, int millisecondsTimeout) - { - fixed (IntPtr* pHandles = &MemoryMarshal.GetReference(handles)) - { - return WaitForMultipleObjectsIgnoringSyncContext(pHandles, handles.Length, waitAll, millisecondsTimeout); - } - } - - private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout) - { - Debug.Assert(millisecondsTimeout >= -1); - - // Normalize waitAll - if (numHandles == 1) - waitAll = false; - -#if NATIVEAOT // TODO: reentrant wait support https://github.com/dotnet/runtime/issues/49518 - bool reentrantWait = Thread.ReentrantWaitsEnabled; - - if (reentrantWait) - { - // - // In the CLR, we use CoWaitForMultipleHandles to pump messages while waiting in an STA. In that case, we cannot use WAIT_ALL. - // That's because the wait would only be satisfied if a message arrives while the handles are signalled. - // - if (waitAll) - throw new NotSupportedException(SR.NotSupported_WaitAllSTAThread); - - // CoWaitForMultipleHandles does not support more than 63 handles. It returns RPC_S_CALLPENDING for more than 63 handles - // that is impossible to differentiate from timeout. - if (numHandles > 63) - throw new NotSupportedException(SR.NotSupported_MaxWaitHandles_STA); - } -#endif - - Thread currentThread = Thread.CurrentThread; - currentThread.SetWaitSleepJoinState(); - -#if NATIVEAOT - int result; - if (reentrantWait) - { - Debug.Assert(!waitAll); - result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); - } - else - { - result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); - } -#else - int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); -#endif - currentThread.ClearWaitSleepJoinState(); - - if (result == Interop.Kernel32.WAIT_FAILED) - { - int errorCode = Interop.Kernel32.GetLastError(); - if (waitAll && errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - { - // Check for duplicate handles. This is a brute force O(n^2) search, which is intended since the typical - // array length is short enough that this would actually be faster than using a hash set. Also, the worst - // case is not so bad considering that the array length is limited by - // . - for (int i = 1; i < numHandles; ++i) - { - IntPtr handle = pHandles[i]; - for (int j = 0; j < i; ++j) - { - if (pHandles[j] == handle) - { - throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); - } - } - } - } - - ThrowWaitFailedException(errorCode); - } - - return result; - } - - private static unsafe int WaitOneCoreCore(IntPtr handle, int millisecondsTimeout) - { - return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout); - } - - private static int SignalAndWaitCoreCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) - { - Debug.Assert(millisecondsTimeout >= -1); - - int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.FALSE); - - if (ret == Interop.Kernel32.WAIT_FAILED) - { - ThrowWaitFailedException(Interop.Kernel32.GetLastError()); - } - - return ret; - } - - private static void ThrowWaitFailedException(int errorCode) - { - switch (errorCode) - { - case Interop.Errors.ERROR_INVALID_HANDLE: - ThrowInvalidHandleException(); - return; - - case Interop.Errors.ERROR_INVALID_PARAMETER: - throw new ArgumentException(); - - case Interop.Errors.ERROR_ACCESS_DENIED: - throw new UnauthorizedAccessException(); - - case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: - throw new OutOfMemoryException(); - - case Interop.Errors.ERROR_TOO_MANY_POSTS: - // Only applicable to . Note however, that - // if the semahpore already has the maximum signal count, the Windows SignalObjectAndWait function does not - // return an error, but this code is kept for historical reasons and to convey the intent, since ideally, - // that should be an error. - throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); - - case Interop.Errors.ERROR_NOT_OWNER: - // Only applicable to when signaling a mutex - // that is locked by a different thread. Note that if the mutex is already unlocked, the Windows - // SignalObjectAndWait function does not return an error. - throw new ApplicationException(SR.Arg_SynchronizationLockException); - - case Interop.Errors.ERROR_MUTANT_LIMIT_EXCEEDED: - throw new OverflowException(SR.Overflow_MutexReacquireCount); - - default: - throw new Exception { HResult = errorCode }; - } - } - - private static Exception ExceptionFromCreationErrorCore(int errorCode, string path) - { - switch (errorCode) - { - case Interop.Errors.ERROR_PATH_NOT_FOUND: - return new IOException(SR.Format(SR.IO_PathNotFound_Path, path)); - - case Interop.Errors.ERROR_ACCESS_DENIED: - return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); - - case Interop.Errors.ERROR_ALREADY_EXISTS: - return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path)); - - case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - return new PathTooLongException(); - - default: - return new IOException(SR.Arg_IOException, errorCode); - } - } - } -} From aa0c74e5b65b3aa4b2bd3440be947c738e1fd70d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 5 Apr 2023 19:53:27 -0700 Subject: [PATCH 017/168] Fix dependencies --- .../System.Private.CoreLib.csproj | 41 +------------------ .../Threading/ThreadPool.CoreCLR.Windows.cs | 2 +- .../System.Private.CoreLib.Shared.projitems | 3 +- .../RegisteredWaitHandle.WindowsThreadPool.cs | 5 ++- .../ThreadPoolBoundHandle.Portable.Core.cs | 2 +- .../src/System/Threading/WindowsThreadPool.cs | 3 +- 6 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index fa4966036fe6f..b00fe8373a3ad 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -236,8 +236,7 @@ - - + @@ -294,47 +293,9 @@ - Interop\Windows\Kernel32\Interop.ThreadPool.cs - - - - - - - - - - - - - - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs index 5fba962862bf9..6e1638fc8947c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs @@ -183,7 +183,7 @@ internal static unsafe void RequestWorkerThread() PortableThreadPool.ThreadPoolInstance.RequestWorker(); } } - + internal static void ReportThreadStatus(bool isWorking) { Debug.Assert(!ThreadPool.UseWindowsThreadPool); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index addf4d902aba6..57f8f88a70e67 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2032,7 +2032,6 @@ - @@ -2516,7 +2515,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index f1dd5ef33d5f6..05571af8933e9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -18,10 +18,12 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { +#pragma warning disable IDE0060 private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { // PR-Comment: Assuming this is no longer necessary, might be wrong about this // var wrapper = ThreadPoolCallbackWrapper.Enter(); + GCHandle handle = (GCHandle)context; RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); @@ -31,6 +33,7 @@ private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, ThreadPool.IncrementCompletedWorkItemCount(); // wrapper.Exit(); } +#pragma warning restore IDE0060 private void PerformCallbackCore(bool timedOut) { @@ -112,7 +115,7 @@ private unsafe void RestartWaitCore() private bool UnregisterCore(WaitHandle waitObject) { // Hold the lock during the synchronous part of Unregister (as in CoreCLR) - lock(_lock) + lock(_lock!) { if (!_unregistering) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index 85eec7b0fa701..2d32042a8a2f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -41,7 +41,7 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) overlapped._boundHandle = this; return overlapped._nativeOverlapped; } - + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) { ArgumentNullException.ThrowIfNull(preAllocated); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 3721360f57603..ca168b77c0089 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -13,6 +13,7 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // + // PR-Comment: Making it internal just to make it build public static class WindowsThreadPool { internal const bool IsWorkerTrackingEnabledInConfig = false; @@ -36,7 +37,7 @@ public static class WindowsThreadPool private static IntPtr s_work; - private class ThreadCountHolder + private sealed class ThreadCountHolder { internal ThreadCountHolder() => Interlocked.Increment(ref s_threadCount); ~ThreadCountHolder() => Interlocked.Decrement(ref s_threadCount); From af6cfd6e8b7b416fd6e3287fb5f143edf5afce2f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Apr 2023 13:49:30 -0700 Subject: [PATCH 018/168] Add some pragmas --- .../src/System/Threading/WindowsThreadPool.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index ca168b77c0089..7e51a90f5951f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -14,7 +14,7 @@ namespace System.Threading // Windows-specific implementation of ThreadPool // // PR-Comment: Making it internal just to make it build - public static class WindowsThreadPool + internal static class WindowsThreadPool { internal const bool IsWorkerTrackingEnabledInConfig = false; @@ -83,11 +83,13 @@ private static object CreateThreadLocalCompletionCountObject() return threadLocalCompletionCountObject; } +#pragma warning disable IDE0060 public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { // Not supported at present return false; } +#pragma warning restore IDE0060 public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) { @@ -97,11 +99,13 @@ public static void GetMaxThreads(out int workerThreads, out int completionPortTh completionPortThreads = MaxThreadCount; } +#pragma warning disable IDE0060 public static bool SetMinThreads(int workerThreads, int completionPortThreads) { // Not supported at present return false; } +#pragma warning restore IDE0060 public static void GetMinThreads(out int workerThreads, out int completionPortThreads) { @@ -196,7 +200,6 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr); - [CLSCompliant(false)] [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) { @@ -218,10 +221,12 @@ public static bool BindHandle(IntPtr osHandle) throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } +#pragma warning disable IDE0060 [SupportedOSPlatform("windows")] public static bool BindHandle(SafeHandle osHandle) { throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } +#pragma warning restore IDE0060 } } From d2de26401342139efb9680f7bba6397e1259453e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Apr 2023 20:35:44 -0700 Subject: [PATCH 019/168] Before adding SafeThreadPoolIOHandle --- .../System.Private.CoreLib.csproj | 6 +- .../PreAllocatedOverlapped.CoreCLR.Windows.cs | 72 +++++++++++++ .../ThreadPoolBoundHandle.CoreCLR.Windows.cs | 101 ++++++++++++++++++ .../src/System.Private.CoreLib.csproj | 1 + .../Threading/Win32ThreadPoolBoundHandle.cs | 18 +--- .../Win32ThreadPoolPreAllocatedOverlapped.cs | 24 ++--- .../System.Private.CoreLib.Shared.projitems | 11 +- .../PreAllocatedOverlapped.Portable.Core.cs | 55 ++++++++++ .../Threading/PreAllocatedOverlapped.cs | 37 ++----- .../RegisteredWaitHandle.Portable.Core.cs | 3 - .../RegisteredWaitHandle.WindowsThreadPool.cs | 3 - .../ThreadPoolBoundHandle.Portable.Core.cs | 27 +++-- .../ThreadPoolBoundHandle.Portable.cs | 19 ++-- .../ThreadPoolBoundHandle.Windows.cs | 2 +- ...ThreadPoolBoundHandle.WindowsThreadPool.cs | 35 ++++-- ...reAllocatedOverlapped.WindowsThreadPool.cs | 25 +++-- 16 files changed, 330 insertions(+), 109 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index b00fe8373a3ad..66190359fb3f3 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -291,8 +291,12 @@ Common\Interop\Windows\OleAut32\Interop.VariantClear.cs - + + + + + Interop\Windows\Kernel32\Interop.ThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs new file mode 100644 index 0000000000000..d9bba3d5a70de --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Threading +{ + /// + /// Represents pre-allocated state for native overlapped I/O operations. + /// + /// + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped_core; + private DeferredDisposableLifetime _lifetime; + + [CLSCompliant(false)] + public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : + this(callback, state, pinData, flowExecutionContext: true) + { + } + + [CLSCompliant(false)] + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => + ThreadPool.UseWindowsThreadPool ? UnsafeCreateCore(callback, state, pinData) : UnsafeCreatePortableCore(callback, state, pinData); + + private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + if (ThreadPool.UseWindowsThreadPool) + { + InitiliazeCore(callback, state, pinData, flowExecutionContext); + } + else + { + InitializePortableCore(callback, state, pinData, flowExecutionContext); + } + } + + internal bool AddRef() => ThreadPool.UseWindowsThreadPool ? AddRefCore() : AddRefPortableCore(); + + internal void Release() => ThreadPool.UseWindowsThreadPool ? ReleaseCore() : ReleasePortableCore(); + + public void Dispose() + { + if (ThreadPool.UseWindowsThreadPool) + { + DisposeCore(); + } + else + { + DisposePortableCore(); + } + } + + ~PreAllocatedOverlapped() + { + Dispose(); + } + + unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + { + if (ThreadPool.UseWindowsThreadPool) + { + IDeferredDisposableOnFinalReleaseCore(); + } + else + { + IDeferredDisposableOnFinalReleasePortableCore(disposed); + } + } + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs new file mode 100644 index 0000000000000..5a22666e0c584 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; + +namespace System.Threading +{ + /// + /// Represents an I/O handle that is bound to the system thread pool and enables low-level + /// components to receive notifications for asynchronous I/O operations. + /// + public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + { + private readonly SafeHandle _handle; + private readonly SafeThreadPoolIOHandle _threadPoolHandle; + private DeferredDisposableLifetime _lifetime; + private bool _isDisposed; + + private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) + { + _threadPoolHandle = threadPoolHandle; + _handle = handle; + } + + private ThreadPoolBoundHandle(SafeHandle handle) + { + _handle = handle; + } + + /// + /// Gets the bound operating system handle. + /// + /// + /// A object that holds the bound operating system handle. + /// + public SafeHandle Handle => _handle; + + public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => + ThreadPool.UseWindowsThreadPool ? BindHandleCore(handle) : BindHandlePortableCore(handle); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + ThreadPool.UseWindowsThreadPool ? + AllocateNativeOverlappedCore(callback, state, pinData) : + AllocateNativeOverlappedPortableCore(callback, state, pinData); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + ThreadPool.UseWindowsThreadPool ? + UnsafeAllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false) : + UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => + ThreadPool.UseWindowsThreadPool ? + AllocateNativeOverlappedCore(preAllocated) : + AllocateNativeOverlappedPortableCore(preAllocated); + + [CLSCompliant(false)] + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => + ThreadPool.UseWindowsThreadPool ? + FreeNativeOverlappedCore(overlapped) : + FreeNativeOverlappedPortableCore(overlapped); + + [CLSCompliant(false)] + public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => + ThreadPool.UseWindowsThreadPool ? + GetNativeOverlappedStateCore(overlapped) : + GetNativeOverlappedStatePortableCore(overlapped); + + public void Dispose() + { + if (ThreadPool.UseWindowsThreadPool) + { + DisposeCore(); + } + else + { + DisposePortableCore(); + } + } + + ~ThreadPoolBoundHandle() + { + if (ThreadPool.UseWindowsThreadPool) + { + FinalizeCore(); + } + } + + void IDeferredDisposable.OnFinalRelease(bool disposed) + { + IDeferredDisposableOnFinalReleaseCore(disposed); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 31e77129e95d8..08365a5d9bb58 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -305,6 +305,7 @@ + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs index 00f851c03c6cc..19276ad03f15b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs @@ -13,7 +13,7 @@ namespace System.Threading // // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool // - public partial sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable { private readonly SafeHandle _handle; private readonly SafeThreadPoolIOHandle _threadPoolHandle; @@ -50,26 +50,16 @@ public SafeHandle Handle [CLSCompliant(false)] public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); - public void Dispose() - { - _lifetime.Dispose(this); - GC.SuppressFinalize(this); - } + public void Dispose() => DisposeCore(); ~ThreadPoolBoundHandle() { - // - // During shutdown, don't automatically clean up, because this instance may still be - // reachable/usable by other code. - // - if (!Environment.HasShutdownStarted) - Dispose(); + FinalizeCore(); } void IDeferredDisposable.OnFinalRelease(bool disposed) { - if (disposed) - _threadPoolHandle.Dispose(); + IDeferredDisposableOnFinalReleaseCore(disposed); } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs index 9c441a578fd03..524a882798977 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -5,9 +5,9 @@ namespace System.Threading { - public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped; + internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped_core; private DeferredDisposableLifetime _lifetime; [CLSCompliant(false)] @@ -17,7 +17,13 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje } [CLSCompliant(false)] - public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => UnsafeCreateCore(callback, state, pinData); + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => + UnsafeCreateCore(callback, state, pinData); + + private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + InitiliazeCore(callback, state, pinData, flowExecutionContext); + } internal bool AddRef() => AddRefCore(); @@ -25,8 +31,7 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje public void Dispose() { - _lifetime.Dispose(this); - GC.SuppressFinalize(this); + DisposeCore(); } ~PreAllocatedOverlapped() @@ -36,15 +41,8 @@ public void Dispose() unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) { - if (_overlapped != null) - { - if (disposed) - Win32ThreadPoolNativeOverlapped.Free(_overlapped); - else - *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlapped) = default(NativeOverlapped); - } + IDeferredDisposableOnFinalReleaseCore(); } - internal unsafe bool IsUserObject(byte[]? buffer) => IsUserObjectCore(buffer); } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 57f8f88a70e67..27aeaf7b8ec51 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2030,8 +2030,13 @@ + + + + + @@ -2506,16 +2511,14 @@ - + - - + - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs new file mode 100644 index 0000000000000..1d0bb96befecf --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading +{ + /// + /// Represents pre-allocated state for native overlapped I/O operations. + /// + /// + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + private static PreAllocatedOverlapped UnsafeCreatePortableCore(IOCompletionCallback callback, object? state, object? pinData) => + new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); + + void InitializePortableCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(callback); + + _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); + } + + private bool AddRefPortableCore() + { + return _lifetime.AddRef(); + } + + private void ReleasePortableCore() + { + _lifetime.Release(this); + } + + private void DisposePortableCore() + { + _lifetime.Dispose(this); + GC.SuppressFinalize(this); + } + + private unsafe void IDeferredDisposableOnFinalReleasePortableCore(bool disposed) + { + if (_overlapped_portable_core != null) // protect against ctor throwing exception and leaving field uninitialized + { + if (disposed) + { + Overlapped.Free(_overlapped_portable_core._nativeOverlapped); + } + else + { + _overlapped_portable_core._boundHandle = null; + _overlapped_portable_core._completed = false; + *_overlapped_portable_core._nativeOverlapped = default; + } + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index 01a7aa93f012a..5de5c064105a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -7,9 +7,9 @@ namespace System.Threading /// Represents pre-allocated state for native overlapped I/O operations. /// /// - public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly ThreadPoolBoundHandleOverlapped _overlapped; + internal readonly ThreadPoolBoundHandleOverlapped _overlapped_portable_core; private DeferredDisposableLifetime _lifetime; /// @@ -89,32 +89,23 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje /// [CLSCompliant(false)] public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); + UnsafeCreatePortableCore(callback, state, pinData); private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); + InitializePortableCore(callback, state, pinData, flowExecutionContext); } - internal bool AddRef() - { - return _lifetime.AddRef(); - } + internal bool AddRef() => AddRefPortableCore(); - internal void Release() - { - _lifetime.Release(this); - } + internal void Release() => ReleasePortableCore(); /// /// Frees the resources associated with this instance. /// public void Dispose() { - _lifetime.Dispose(this); - GC.SuppressFinalize(this); + DisposePortableCore(); } ~PreAllocatedOverlapped() @@ -124,19 +115,7 @@ public void Dispose() unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) { - if (_overlapped != null) // protect against ctor throwing exception and leaving field uninitialized - { - if (disposed) - { - Overlapped.Free(_overlapped._nativeOverlapped); - } - else - { - _overlapped._boundHandle = null; - _overlapped._completed = false; - *_overlapped._nativeOverlapped = default; - } - } + IDeferredDisposableOnFinalReleasePortableCore(disposed); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs index 813a254d41961..205bde5d364a9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs @@ -10,9 +10,6 @@ namespace System.Threading /// /// An object representing the registration of a via . /// -#if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] -#endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { private static AutoResetEvent RentEvent() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 05571af8933e9..7842215a15870 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -13,9 +13,6 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // -#if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] -#endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { #pragma warning disable IDE0060 diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index 2d32042a8a2f1..638652f16cc4b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -23,16 +23,16 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) if (handle.IsClosed || handle.IsInvalid) throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - return BindHandleCore(handle); + return BindHandleWindowsCore(handle); } - private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + private unsafe NativeOverlapped* AllocateNativeOverlappedPortableCore(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlappedPortableCore(callback, state, pinData, flowExecutionContext: true); - private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); + private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedPortableCore(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlappedPortableCore(callback, state, pinData, flowExecutionContext: false); - private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + private unsafe NativeOverlapped* AllocateNativeOverlappedPortableCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { ArgumentNullException.ThrowIfNull(callback); ObjectDisposedException.ThrowIf(_isDisposed, this); @@ -42,7 +42,7 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) return overlapped._nativeOverlapped; } - private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) + private unsafe NativeOverlapped* AllocateNativeOverlappedPortableCore(PreAllocatedOverlapped preAllocated) { ArgumentNullException.ThrowIfNull(preAllocated); ObjectDisposedException.ThrowIf(_isDisposed, this); @@ -66,7 +66,7 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) } } - private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) + private unsafe void FreeNativeOverlappedPortableCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -83,7 +83,7 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) Overlapped.Free(overlapped); } - private static unsafe object? GetNativeOverlappedStateCore(NativeOverlapped* overlapped) + private static unsafe object? GetNativeOverlappedStatePortableCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -106,5 +106,14 @@ private static unsafe ThreadPoolBoundHandleOverlapped GetOverlappedWrapper(Nativ return wrapper; } + + private void DisposePortableCore() + { + // .NET Native's version of ThreadPoolBoundHandle that wraps the Win32 ThreadPool holds onto + // native resources so it needs to be disposable. To match the contract, we are also disposable. + // We also implement a disposable state to mimic behavior between this implementation and + // .NET Native's version (code written against us, will also work against .NET Native's version). + _isDisposed = true; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs index 1bad4b3511c8b..ae33397f95aef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs @@ -116,7 +116,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlappedCore(callback, state, pinData); + AllocateNativeOverlappedPortableCore(callback, state, pinData); /// /// Returns an unmanaged pointer to a structure, specifying @@ -164,7 +164,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeAllocateNativeOverlappedCore(callback, state, pinData); + UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); /// /// Returns an unmanaged pointer to a structure, using the callback, @@ -195,7 +195,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// /// [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => AllocateNativeOverlappedCore(preAllocated); + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => AllocateNativeOverlappedPortableCore(preAllocated); /// /// Frees the unmanaged memory associated with a structure @@ -221,7 +221,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// This method was called after the was disposed. /// [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedCore(overlapped); + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedPortableCore(overlapped); /// /// Returns the user-provided object specified when the instance was @@ -240,15 +240,8 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// is . /// [CLSCompliant(false)] - public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); + public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStatePortableCore(overlapped); - public void Dispose() - { - // .NET Native's version of ThreadPoolBoundHandle that wraps the Win32 ThreadPool holds onto - // native resources so it needs to be disposable. To match the contract, we are also disposable. - // We also implement a disposable state to mimic behavior between this implementation and - // .NET Native's version (code written against us, will also work against .NET Native's version). - _isDisposed = true; - } + public void Dispose() => DisposePortableCore(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index f80107fcf1a66..19fc2ed4ef01b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -8,7 +8,7 @@ namespace System.Threading { public sealed partial class ThreadPoolBoundHandle { - private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + private static ThreadPoolBoundHandle BindHandleWindowsCore(SafeHandle handle) { Debug.Assert(handle != null); Debug.Assert(!handle.IsClosed); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index b23a983c4ba99..f336b592e35a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -13,7 +13,7 @@ namespace System.Threading // // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool // - public partial sealed class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable { private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) { @@ -38,15 +38,13 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) return new ThreadPoolBoundHandle(handle, threadPoolHandle); } - [CLSCompliant(false)] private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true); + AllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: true); - [CLSCompliant(false)] private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false); + AllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false); - private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object state, object pinData, bool flowExecutionContext) + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object state, object pinData, bool flowExecutionContext) { ArgumentNullException.ThrowIfNull(callback); @@ -67,7 +65,6 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) } } - [CLSCompliant(false)] private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) { ArgumentNullException.ThrowIfNull(preAllocated); @@ -99,7 +96,6 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) } } - [CLSCompliant(false)] private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -122,7 +118,6 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); } - [CLSCompliant(false)] private static unsafe object GetNativeOverlappedStateCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -172,5 +167,27 @@ private void Release() { _lifetime.Release(this); } + + void IDeferredDisposableOnFinalReleaseCore(bool disposed) + { + if (disposed) + _threadPoolHandle.Dispose(); + } + + private void DisposeCore() + { + _lifetime.Dispose(this); + GC.SuppressFinalize(this); + } + + private void FinalizeCore() + { + // + // During shutdown, don't automatically clean up, because this instance may still be + // reachable/usable by other code. + // + if (!Environment.HasShutdownStarted) + Dispose(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs index 62cb920630dd7..f7cb3f2537dbc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs @@ -5,17 +5,16 @@ namespace System.Threading { - public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - [CLSCompliant(false)] private static PreAllocatedOverlapped UnsafeCreateCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); - private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + private unsafe void InitializeCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { ArgumentNullException.ThrowIfNull(callback); - _overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); } private bool AddRefCore() @@ -28,17 +27,23 @@ private void ReleaseCore() _lifetime.Release(this); } - unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + internal unsafe bool IsUserObject(byte[]? buffer) => _overlapped_core->IsUserObject(buffer); + + private void DisposeCore() { - if (_overlapped != null) + _lifetime.Dispose(this); + GC.SuppressFinalize(this); + } + + private unsafe void IDeferredDisposableOnFinalReleaseCore(bool disposed) + { + if (_overlapped_core != null) { if (disposed) - Win32ThreadPoolNativeOverlapped.Free(_overlapped); + Win32ThreadPoolNativeOverlapped.Free(_overlapped_core); else - *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlapped) = default(NativeOverlapped); + *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlapped_core) = default(NativeOverlapped); } } - - private unsafe bool IsUserObjectCore(byte[]? buffer) => _overlapped->IsUserObject(buffer); } } From 9da7ec3ace4b4cbc6162c2bf39b6c038cb34b199 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 13:30:15 -0700 Subject: [PATCH 020/168] Add SafeThreadPoolIOHandle and Interop.ThreadPoolIO.cs --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 66190359fb3f3..e793bf375b94b 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -295,8 +295,11 @@ - + + + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs + Interop\Windows\Kernel32\Interop.ThreadPool.cs From 6c420facabbba60d81787b7b27a96e4286bd2cf4 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 14:59:50 -0700 Subject: [PATCH 021/168] Most errors fixed --- .../System.Private.CoreLib.csproj | 2 + .../PreAllocatedOverlapped.CoreCLR.Windows.cs | 25 +- .../ThreadPoolBoundHandle.CoreCLR.Windows.cs | 21 +- .../Threading/Win32ThreadPoolBoundHandle.cs | 2 +- .../Win32ThreadPoolNativeOverlapped.cs | 207 +++++++++++++++- .../Win32ThreadPoolPreAllocatedOverlapped.cs | 4 +- .../System.Private.CoreLib.Shared.projitems | 1 + .../PreAllocatedOverlapped.Portable.Core.cs | 10 +- .../Threading/PreAllocatedOverlapped.cs | 6 +- ...ThreadPoolBoundHandle.WindowsThreadPool.cs | 14 +- ...dPoolNativeOverlapped.WindowsThreadPool.cs | 220 +----------------- ...reAllocatedOverlapped.WindowsThreadPool.cs | 9 +- 12 files changed, 255 insertions(+), 266 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index e793bf375b94b..900926b5e56dc 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -297,6 +297,8 @@ + + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs index d9bba3d5a70de..3929e3c12cdc0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs @@ -11,8 +11,6 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped_core; - private DeferredDisposableLifetime _lifetime; [CLSCompliant(false)] public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : @@ -28,18 +26,31 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta { if (ThreadPool.UseWindowsThreadPool) { - InitiliazeCore(callback, state, pinData, flowExecutionContext); + ArgumentNullException.ThrowIfNull(callback); + + _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); } else { - InitializePortableCore(callback, state, pinData, flowExecutionContext); + ArgumentNullException.ThrowIfNull(callback); + + _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); } } internal bool AddRef() => ThreadPool.UseWindowsThreadPool ? AddRefCore() : AddRefPortableCore(); - internal void Release() => ThreadPool.UseWindowsThreadPool ? ReleaseCore() : ReleasePortableCore(); - + internal void Release() + { + if (ThreadPool.UseWindowsThreadPool) + { + ReleaseCore(); + } + else + { + ReleasePortableCore(); + } + } public void Dispose() { if (ThreadPool.UseWindowsThreadPool) @@ -61,7 +72,7 @@ unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) { if (ThreadPool.UseWindowsThreadPool) { - IDeferredDisposableOnFinalReleaseCore(); + IDeferredDisposableOnFinalReleaseCore(disposed); } else { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs index 5a22666e0c584..c102acf2f91ee 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs @@ -17,7 +17,7 @@ namespace System.Threading public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable { private readonly SafeHandle _handle; - private readonly SafeThreadPoolIOHandle _threadPoolHandle; + private readonly SafeThreadPoolIOHandle? _threadPoolHandle; private DeferredDisposableLifetime _lifetime; private bool _isDisposed; @@ -52,7 +52,7 @@ public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => ThreadPool.UseWindowsThreadPool ? - UnsafeAllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false) : + UnsafeAllocateNativeOverlappedCore(callback, state, pinData) : UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); [CLSCompliant(false)] @@ -62,13 +62,20 @@ public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => AllocateNativeOverlappedPortableCore(preAllocated); [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => - ThreadPool.UseWindowsThreadPool ? - FreeNativeOverlappedCore(overlapped) : - FreeNativeOverlappedPortableCore(overlapped); + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) + { + if (ThreadPool.UseWindowsThreadPool) + { + FreeNativeOverlappedCore(overlapped); + } + else + { + FreeNativeOverlappedPortableCore(overlapped); + } + } [CLSCompliant(false)] - public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => + public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => ThreadPool.UseWindowsThreadPool ? GetNativeOverlappedStateCore(overlapped) : GetNativeOverlappedStatePortableCore(overlapped); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs index 19276ad03f15b..239081651dd37 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs @@ -38,7 +38,7 @@ public SafeHandle Handle [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeAllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false); + UnsafeAllocateNativeOverlappedCore(callback, state, pinData); [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs index e306bb6a20106..1083133acedbe 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs @@ -28,17 +28,210 @@ internal OverlappedData Data get { return s_dataArray[_dataIndex]; } } - internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) => - AllocateCore(callback, state, pinData, preAllocated, flowExecutionControl); + internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) + { + Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew(); + try + { + overlapped->SetData(callback, state, pinData, preAllocated, flowExecutionControl); + } + catch + { + Free(overlapped); + throw; + } + return overlapped; + } + + private static unsafe Win32ThreadPoolNativeOverlapped* AllocateNew() + { + IntPtr freePtr; + Win32ThreadPoolNativeOverlapped* overlapped; + OverlappedData data; + + // Find a free Overlapped + while ((freePtr = Volatile.Read(ref s_freeList)) != IntPtr.Zero) + { + overlapped = (Win32ThreadPoolNativeOverlapped*)freePtr; + + if (Interlocked.CompareExchange(ref s_freeList, overlapped->_nextFree, freePtr) != freePtr) + continue; + + overlapped->_nextFree = IntPtr.Zero; + return overlapped; + } + + // None are free; allocate a new one. + overlapped = (Win32ThreadPoolNativeOverlapped*)NativeMemory.Alloc((nuint)sizeof(Win32ThreadPoolNativeOverlapped)); + *overlapped = default(Win32ThreadPoolNativeOverlapped); + + // Allocate a OverlappedData object, and an index at which to store it in _dataArray. + data = new OverlappedData(); + int dataIndex = Interlocked.Increment(ref s_dataCount) - 1; + + // Make sure we didn't wrap around. + if (dataIndex < 0) + Environment.FailFast("Too many outstanding Win32ThreadPoolNativeOverlapped instances"); + + while (true) + { + OverlappedData[]? dataArray = Volatile.Read(ref s_dataArray); + int currentLength = dataArray == null ? 0 : dataArray.Length; + + // If the current array is too small, create a new, larger one. + if (currentLength <= dataIndex) + { + int newLength = currentLength; + if (newLength == 0) + newLength = 128; + while (newLength <= dataIndex) + newLength = (newLength * 3) / 2; + + OverlappedData[]? newDataArray = dataArray; + Array.Resize(ref newDataArray, newLength); - internal static unsafe void Free(Win32ThreadPoolNativeOverlapped* overlapped) => FreeCore(overlapped); + if (Interlocked.CompareExchange(ref s_dataArray, newDataArray, dataArray) != dataArray) + continue; // Someone else got the free one, try again - internal static unsafe NativeOverlapped* ToNativeOverlapped(Win32ThreadPoolNativeOverlapped* overlapped) => ToNativeOverlappedCore(overlapped); + dataArray = newDataArray; + } - internal static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlapped(NativeOverlapped* overlapped) => FromNativeOverlappedCore(overlapped); + // If we haven't stored this object in the array yet, do so now. Then we need to make another pass through + // the loop, in case another thread resized the array before we made this update. + if (s_dataArray[dataIndex] == null) + { + // Full fence so this write can't move past subsequent reads. + Interlocked.Exchange(ref dataArray![dataIndex], data); + continue; + } + + // We're already in the array, so we're done. + Debug.Assert(dataArray![dataIndex] == data); + overlapped->_dataIndex = dataIndex; + return overlapped; + } + } + + private void SetData(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionContext) + { + Debug.Assert(callback != null); + + OverlappedData data = Data; + + data._callback = callback; + data._state = state; + data._executionContext = flowExecutionContext ? ExecutionContext.Capture() : null; + data._preAllocated = preAllocated; + + // + // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to + // an instance of a blittable type to be pinned. + // + if (pinData != null) + { + object[]? objArray = pinData as object[]; + if (objArray != null && objArray.GetType() == typeof(object[])) + { + if (data._pinnedData == null || data._pinnedData.Length < objArray.Length) + Array.Resize(ref data._pinnedData, objArray.Length); + + for (int i = 0; i < objArray.Length; i++) + { + if (!data._pinnedData[i].IsAllocated) + data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); + else + data._pinnedData[i].Target = objArray[i]; + } + } + else + { + data._pinnedData ??= new GCHandle[1]; + + if (!data._pinnedData[0].IsAllocated) + data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned); + else + data._pinnedData[0].Target = pinData; + } + } + } + + internal static unsafe void Free(Win32ThreadPoolNativeOverlapped* overlapped) + { + // Reset all data. + overlapped->Data.Reset(); + overlapped->_overlapped = default(NativeOverlapped); - internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) => CompleteWithCallbackCore(errorCode, bytesWritten, overlapped); + // Add to the free list. + while (true) + { + IntPtr freePtr = Volatile.Read(ref s_freeList); + overlapped->_nextFree = freePtr; + + if (Interlocked.CompareExchange(ref s_freeList, (IntPtr)overlapped, freePtr) == freePtr) + break; + } + } + + internal static unsafe NativeOverlapped* ToNativeOverlapped(Win32ThreadPoolNativeOverlapped* overlapped) + { + return (NativeOverlapped*)overlapped; + } + + internal static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlapped(NativeOverlapped* overlapped) + { + return (Win32ThreadPoolNativeOverlapped*)overlapped; + } + + internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) + { + OverlappedData data = overlapped->Data; + + Debug.Assert(!data._completed); + data._completed = true; + + if (data._executionContext == null) + { + Debug.Assert(data._callback != null, "Does CompleteWithCallback called after Reset?"); + data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); + return; + } + + ContextCallback callback = s_executionContextCallback; + if (callback == null) + s_executionContextCallback = callback = OnExecutionContextCallback; + + // Get an args object from the per-thread cache. + ExecutionContextCallbackArgs args = t_executionContextCallbackArgs; + args ??= new ExecutionContextCallbackArgs(); + + t_executionContextCallbackArgs = null; + + args._errorCode = errorCode; + args._bytesWritten = bytesWritten; + args._overlapped = overlapped; + args._data = data; + + ExecutionContext.Run(data._executionContext, callback, args); + } + + private static unsafe void OnExecutionContextCallback(object? state) + { + Debug.Assert(state != null); + ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state; + + uint errorCode = args._errorCode; + uint bytesWritten = args._bytesWritten; + Win32ThreadPoolNativeOverlapped* overlapped = args._overlapped; + OverlappedData data = args._data; + + // Put the args object back in the per-thread cache, now that we're done with it. + args._data = null; + t_executionContextCallbackArgs = args; + + Debug.Assert(data._callback != null, "Does OnExecutionContextCallback called after Reset?"); + data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); + } - internal bool IsUserObject(byte[]? buffer) => IsUserObjectCore(buffer); + internal bool IsUserObject(byte[]? buffer) => ReferenceEquals(Data._pinnedData, buffer); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs index 524a882798977..bf5313c28e885 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -22,7 +22,9 @@ public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { - InitiliazeCore(callback, state, pinData, flowExecutionContext); + ArgumentNullException.ThrowIfNull(callback); + + _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); } internal bool AddRef() => AddRefCore(); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 27aeaf7b8ec51..624aee810ee71 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2512,6 +2512,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs index 1d0bb96befecf..22bdf51530a4d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -9,16 +9,12 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { + internal readonly ThreadPoolBoundHandleOverlapped? _overlapped_portable_core; + private DeferredDisposableLifetime _lifetime; + private static PreAllocatedOverlapped UnsafeCreatePortableCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); - void InitializePortableCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); - } - private bool AddRefPortableCore() { return _lifetime.AddRef(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index 5de5c064105a4..7957332d800a9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -9,8 +9,6 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly ThreadPoolBoundHandleOverlapped _overlapped_portable_core; - private DeferredDisposableLifetime _lifetime; /// /// Initializes a new instance of the class, specifying @@ -93,7 +91,9 @@ public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { - InitializePortableCore(callback, state, pinData, flowExecutionContext); + ArgumentNullException.ThrowIfNull(callback); + + _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); } internal bool AddRef() => AddRefPortableCore(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index f336b592e35a4..f2b9cdda588c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -44,7 +44,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false); - private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object state, object pinData, bool flowExecutionContext) + private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { ArgumentNullException.ThrowIfNull(callback); @@ -54,7 +54,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) Win32ThreadPoolNativeOverlapped* overlapped = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, preAllocated: null, flowExecutionContext); overlapped->Data._boundHandle = this; - Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); + Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!); return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(overlapped); } @@ -76,15 +76,15 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) addedRefToThis = AddRef(); addedRefToPreAllocated = preAllocated.AddRef(); - Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped->Data; + Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped_core->Data; if (data._boundHandle != null) throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); data._boundHandle = this; - Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle); + Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!); - return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped); + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped_core); } catch { @@ -105,7 +105,7 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) if (!data._completed) { - Interop.Kernel32.CancelThreadpoolIo(_threadPoolHandle); + Interop.Kernel32.CancelThreadpoolIo(_threadPoolHandle!); Release(); } @@ -118,7 +118,7 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); } - private static unsafe object GetNativeOverlappedStateCore(NativeOverlapped* overlapped) + private static unsafe object? GetNativeOverlappedStateCore(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs index 68841bda351b8..5f282702bb03e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs @@ -1,219 +1 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - [StructLayout(LayoutKind.Sequential)] - internal partial struct Win32ThreadPoolNativeOverlapped - { - private static unsafe Win32ThreadPoolNativeOverlapped* AllocateCore(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) - { - Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew(); - try - { - overlapped->SetData(callback, state, pinData, preAllocated, flowExecutionControl); - } - catch - { - Free(overlapped); - throw; - } - return overlapped; - } - - private static unsafe Win32ThreadPoolNativeOverlapped* AllocateNew() - { - IntPtr freePtr; - Win32ThreadPoolNativeOverlapped* overlapped; - OverlappedData data; - - // Find a free Overlapped - while ((freePtr = Volatile.Read(ref s_freeList)) != IntPtr.Zero) - { - overlapped = (Win32ThreadPoolNativeOverlapped*)freePtr; - - if (Interlocked.CompareExchange(ref s_freeList, overlapped->_nextFree, freePtr) != freePtr) - continue; - - overlapped->_nextFree = IntPtr.Zero; - return overlapped; - } - - // None are free; allocate a new one. - overlapped = (Win32ThreadPoolNativeOverlapped*)NativeMemory.Alloc((nuint)sizeof(Win32ThreadPoolNativeOverlapped)); - *overlapped = default(Win32ThreadPoolNativeOverlapped); - - // Allocate a OverlappedData object, and an index at which to store it in _dataArray. - data = new OverlappedData(); - int dataIndex = Interlocked.Increment(ref s_dataCount) - 1; - - // Make sure we didn't wrap around. - if (dataIndex < 0) - Environment.FailFast("Too many outstanding Win32ThreadPoolNativeOverlapped instances"); - - while (true) - { - OverlappedData[]? dataArray = Volatile.Read(ref s_dataArray); - int currentLength = dataArray == null ? 0 : dataArray.Length; - - // If the current array is too small, create a new, larger one. - if (currentLength <= dataIndex) - { - int newLength = currentLength; - if (newLength == 0) - newLength = 128; - while (newLength <= dataIndex) - newLength = (newLength * 3) / 2; - - OverlappedData[]? newDataArray = dataArray; - Array.Resize(ref newDataArray, newLength); - - if (Interlocked.CompareExchange(ref s_dataArray, newDataArray, dataArray) != dataArray) - continue; // Someone else got the free one, try again - - dataArray = newDataArray; - } - - // If we haven't stored this object in the array yet, do so now. Then we need to make another pass through - // the loop, in case another thread resized the array before we made this update. - if (s_dataArray[dataIndex] == null) - { - // Full fence so this write can't move past subsequent reads. - Interlocked.Exchange(ref dataArray![dataIndex], data); - continue; - } - - // We're already in the array, so we're done. - Debug.Assert(dataArray![dataIndex] == data); - overlapped->_dataIndex = dataIndex; - return overlapped; - } - } - - private void SetData(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionContext) - { - Debug.Assert(callback != null); - - OverlappedData data = Data; - - data._callback = callback; - data._state = state; - data._executionContext = flowExecutionContext ? ExecutionContext.Capture() : null; - data._preAllocated = preAllocated; - - // - // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to - // an instance of a blittable type to be pinned. - // - if (pinData != null) - { - object[]? objArray = pinData as object[]; - if (objArray != null && objArray.GetType() == typeof(object[])) - { - if (data._pinnedData == null || data._pinnedData.Length < objArray.Length) - Array.Resize(ref data._pinnedData, objArray.Length); - - for (int i = 0; i < objArray.Length; i++) - { - if (!data._pinnedData[i].IsAllocated) - data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); - else - data._pinnedData[i].Target = objArray[i]; - } - } - else - { - data._pinnedData ??= new GCHandle[1]; - - if (!data._pinnedData[0].IsAllocated) - data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned); - else - data._pinnedData[0].Target = pinData; - } - } - } - - private static unsafe void FreeCore(Win32ThreadPoolNativeOverlapped* overlapped) - { - // Reset all data. - overlapped->Data.Reset(); - overlapped->_overlapped = default(NativeOverlapped); - - // Add to the free list. - while (true) - { - IntPtr freePtr = Volatile.Read(ref s_freeList); - overlapped->_nextFree = freePtr; - - if (Interlocked.CompareExchange(ref s_freeList, (IntPtr)overlapped, freePtr) == freePtr) - break; - } - } - - private static unsafe NativeOverlapped* ToNativeOverlappedCore(Win32ThreadPoolNativeOverlapped* overlapped) - { - return (NativeOverlapped*)overlapped; - } - - private static unsafe Win32ThreadPoolNativeOverlapped* FromNativeOverlappedCore(NativeOverlapped* overlapped) - { - return (Win32ThreadPoolNativeOverlapped*)overlapped; - } - - private static unsafe void CompleteWithCallbackCore(uint errorCode, uint bytesWritten, Win32ThreadPoolNativeOverlapped* overlapped) - { - OverlappedData data = overlapped->Data; - - Debug.Assert(!data._completed); - data._completed = true; - - if (data._executionContext == null) - { - Debug.Assert(data._callback != null, "Does CompleteWithCallback called after Reset?"); - data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); - return; - } - - ContextCallback callback = s_executionContextCallback; - if (callback == null) - s_executionContextCallback = callback = OnExecutionContextCallback; - - // Get an args object from the per-thread cache. - ExecutionContextCallbackArgs args = t_executionContextCallbackArgs; - args ??= new ExecutionContextCallbackArgs(); - - t_executionContextCallbackArgs = null; - - args._errorCode = errorCode; - args._bytesWritten = bytesWritten; - args._overlapped = overlapped; - args._data = data; - - ExecutionContext.Run(data._executionContext, callback, args); - } - - private static unsafe void OnExecutionContextCallback(object? state) - { - Debug.Assert(state != null); - ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state; - - uint errorCode = args._errorCode; - uint bytesWritten = args._bytesWritten; - Win32ThreadPoolNativeOverlapped* overlapped = args._overlapped; - OverlappedData data = args._data; - - // Put the args object back in the per-thread cache, now that we're done with it. - args._data = null; - t_executionContextCallbackArgs = args; - - Debug.Assert(data._callback != null, "Does OnExecutionContextCallback called after Reset?"); - data._callback(errorCode, bytesWritten, ToNativeOverlapped(overlapped)); - } - - private bool IsUserObjectCore(byte[]? buffer) => ReferenceEquals(Data._pinnedData, buffer); - } -} + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs index f7cb3f2537dbc..ba25d06a4c297 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs @@ -7,16 +7,11 @@ namespace System.Threading { public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { + internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped_core; + private static PreAllocatedOverlapped UnsafeCreateCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); - private unsafe void InitializeCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); - } - private bool AddRefCore() { return _lifetime.AddRef(); From 157508d44644e7204043b49c941db1390a348758 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 19:50:49 -0700 Subject: [PATCH 022/168] Fix CoreCLR errors --- .../SafeHandles/SafeThreadPoolIOHandle.cs | 2 +- ...Overlapped.ExecutionContextCallbackArgs.cs | 4 ++-- ...readPoolNativeOverlapped.OverlappedData.cs | 6 ++--- .../Win32ThreadPoolNativeOverlapped.cs | 22 +++++++++---------- .../Win32ThreadPoolPreAllocatedOverlapped.cs | 1 - .../System.Private.CoreLib.Shared.projitems | 1 - .../ThreadPoolBoundHandle.Portable.Core.cs | 2 +- ...ThreadPoolBoundHandle.WindowsThreadPool.cs | 13 ++++++----- ...dPoolNativeOverlapped.WindowsThreadPool.cs | 1 - 9 files changed, 25 insertions(+), 27 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs index d6bf95212d4e4..5d8f7916bca04 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs @@ -6,7 +6,7 @@ namespace Microsoft.Win32.SafeHandles { - internal class SafeThreadPoolIOHandle : SafeHandle + internal sealed class SafeThreadPoolIOHandle : SafeHandle { public SafeThreadPoolIOHandle() : base(IntPtr.Zero, true) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs index 173a237759875..a3c8567d4bbb3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs @@ -5,12 +5,12 @@ namespace System.Threading { internal partial struct Win32ThreadPoolNativeOverlapped { - private unsafe class ExecutionContextCallbackArgs + private sealed unsafe class ExecutionContextCallbackArgs { internal uint _errorCode; internal uint _bytesWritten; internal Win32ThreadPoolNativeOverlapped* _overlapped; - internal OverlappedData _data; + internal OverlappedData? _data; } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs index 798f11584fbd7..065e67fede2ed 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs @@ -8,13 +8,13 @@ namespace System.Threading { internal partial struct Win32ThreadPoolNativeOverlapped { - internal class OverlappedData + internal sealed class OverlappedData { - internal GCHandle[] _pinnedData; + internal GCHandle[]? _pinnedData; internal IOCompletionCallback? _callback; internal object? _state; internal ExecutionContext? _executionContext; - internal ThreadPoolBoundHandle _boundHandle; + internal ThreadPoolBoundHandle? _boundHandle; internal PreAllocatedOverlapped? _preAllocated; internal bool _completed; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs index 1083133acedbe..02abf282c6756 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs @@ -12,10 +12,10 @@ internal partial struct Win32ThreadPoolNativeOverlapped { // Per-thread cache of the args object, so we don't have to allocate a new one each time. [ThreadStatic] - private static ExecutionContextCallbackArgs t_executionContextCallbackArgs; + private static ExecutionContextCallbackArgs? t_executionContextCallbackArgs; - private static ContextCallback s_executionContextCallback; - private static OverlappedData[] s_dataArray; + private static ContextCallback? s_executionContextCallback; + private static OverlappedData[]? s_dataArray; private static int s_dataCount; // Current number of valid entries in _dataArray private static IntPtr s_freeList; // Lock-free linked stack of free ThreadPoolNativeOverlapped instances. @@ -25,10 +25,10 @@ internal partial struct Win32ThreadPoolNativeOverlapped internal OverlappedData Data { - get { return s_dataArray[_dataIndex]; } + get { return s_dataArray![_dataIndex]; } } - internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionControl) + internal static unsafe Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped? preAllocated, bool flowExecutionControl) { Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew(); try @@ -98,7 +98,7 @@ internal OverlappedData Data // If we haven't stored this object in the array yet, do so now. Then we need to make another pass through // the loop, in case another thread resized the array before we made this update. - if (s_dataArray[dataIndex] == null) + if (s_dataArray![dataIndex] == null) { // Full fence so this write can't move past subsequent reads. Interlocked.Exchange(ref dataArray![dataIndex], data); @@ -112,7 +112,7 @@ internal OverlappedData Data } } - private void SetData(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped preAllocated, bool flowExecutionContext) + private void SetData(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped? preAllocated, bool flowExecutionContext) { Debug.Assert(callback != null); @@ -196,12 +196,12 @@ internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritt return; } - ContextCallback callback = s_executionContextCallback; + ContextCallback? callback = s_executionContextCallback; if (callback == null) s_executionContextCallback = callback = OnExecutionContextCallback; // Get an args object from the per-thread cache. - ExecutionContextCallbackArgs args = t_executionContextCallbackArgs; + ExecutionContextCallbackArgs? args = t_executionContextCallbackArgs; args ??= new ExecutionContextCallbackArgs(); t_executionContextCallbackArgs = null; @@ -217,12 +217,12 @@ internal static unsafe void CompleteWithCallback(uint errorCode, uint bytesWritt private static unsafe void OnExecutionContextCallback(object? state) { Debug.Assert(state != null); - ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state; + ExecutionContextCallbackArgs args = (ExecutionContextCallbackArgs)state!; uint errorCode = args._errorCode; uint bytesWritten = args._bytesWritten; Win32ThreadPoolNativeOverlapped* overlapped = args._overlapped; - OverlappedData data = args._data; + OverlappedData data = args._data!; // Put the args object back in the per-thread cache, now that we're done with it. args._data = null; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs index bf5313c28e885..1827c1a7b7553 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -7,7 +7,6 @@ namespace System.Threading { public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped_core; private DeferredDisposableLifetime _lifetime; [CLSCompliant(false)] diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 624aee810ee71..6ff12e55361dc 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2034,7 +2034,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index 638652f16cc4b..36ecb85e3c15f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -50,7 +50,7 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) preAllocated.AddRef(); try { - ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped; + ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped_portable_core!; if (overlapped._boundHandle != null) throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index f2b9cdda588c5..36269a97ede62 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -128,7 +128,7 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) return data._state; } - private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle expectedBoundHandle) + private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlappedData(Win32ThreadPoolNativeOverlapped* overlapped, ThreadPoolBoundHandle? expectedBoundHandle) { Win32ThreadPoolNativeOverlapped.OverlappedData data = overlapped->Data; @@ -144,10 +144,11 @@ private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlapp [UnmanagedCallersOnly] private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, nuint numberOfBytesTransferred, IntPtr ioPtr) { - var wrapper = ThreadPoolCallbackWrapper.Enter(); + // PR-Comment: Assuming it's not necessary, might be wrong about this + // var wrapper = ThreadPoolCallbackWrapper.Enter(); Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; - ThreadPoolBoundHandle boundHandle = overlapped->Data._boundHandle; + ThreadPoolBoundHandle? boundHandle = overlapped->Data._boundHandle; if (boundHandle == null) throw new InvalidOperationException(SR.Argument_NativeOverlappedAlreadyFree); @@ -155,7 +156,7 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); ThreadPool.IncrementCompletedWorkItemCount(); - wrapper.Exit(); + // wrapper.Exit(); } private bool AddRef() @@ -168,10 +169,10 @@ private void Release() _lifetime.Release(this); } - void IDeferredDisposableOnFinalReleaseCore(bool disposed) + private void IDeferredDisposableOnFinalReleaseCore(bool disposed) { if (disposed) - _threadPoolHandle.Dispose(); + _threadPoolHandle!.Dispose(); } private void DisposeCore() diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs deleted file mode 100644 index 5f282702bb03e..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolNativeOverlapped.WindowsThreadPool.cs +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 1f54bd2a149829a42e7807ac06f059b7353e77b2 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Apr 2023 20:02:12 -0700 Subject: [PATCH 023/168] Fix some NativeAOT errors --- .../src/System/Diagnostics/Tracing/RuntimeEventSource.cs | 4 ++-- .../src/System/Threading/ThreadPoolWorkQueue.cs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs index 5c01e52e0e467..7f373f9f41723 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs @@ -97,10 +97,10 @@ protected override void OnEventCommand(EventCommandEventArgs command) _gen0GCCounter ??= new IncrementingPollingCounter("gen-0-gc-count", this, () => GC.CollectionCount(0)) { DisplayName = "Gen 0 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; _gen1GCCounter ??= new IncrementingPollingCounter("gen-1-gc-count", this, () => GC.CollectionCount(1)) { DisplayName = "Gen 1 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; _gen2GCCounter ??= new IncrementingPollingCounter("gen-2-gc-count", this, () => GC.CollectionCount(2)) { DisplayName = "Gen 2 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; - _threadPoolThreadCounter ??= new PollingCounter("threadpool-thread-count", this, () => ThreadPool.ThreadCount) { DisplayName = "ThreadPool Thread Count" }; + _threadPoolThreadCounter ??= new PollingCounter("threadpool-thread-count", this, () => WindowsThreadPool.ThreadCount) { DisplayName = "ThreadPool Thread Count" }; _monitorContentionCounter ??= new IncrementingPollingCounter("monitor-lock-contention-count", this, () => Monitor.LockContentionCount) { DisplayName = "Monitor Lock Contention Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _threadPoolQueueCounter ??= new PollingCounter("threadpool-queue-length", this, () => ThreadPool.PendingWorkItemCount) { DisplayName = "ThreadPool Queue Length" }; - _completedItemsCounter ??= new IncrementingPollingCounter("threadpool-completed-items-count", this, () => ThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; + _completedItemsCounter ??= new IncrementingPollingCounter("threadpool-completed-items-count", this, () => WindowsThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _allocRateCounter ??= new IncrementingPollingCounter("alloc-rate", this, () => GC.GetTotalAllocatedBytes()) { DisplayName = "Allocation Rate", DisplayUnits = "B", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _timerCounter ??= new PollingCounter("active-timer-count", this, () => Timer.ActiveCount) { DisplayName = "Number of Active Timers" }; _fragmentationCounter ??= new PollingCounter("gc-fragmentation", this, () => { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index c41f7e37280d0..1a43908ce3d4b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1372,6 +1372,10 @@ public static partial class ThreadPool } }; +#if NATIVEAOT + private const bool IsWorkerTrackingEnabledInConfig = false; +#endif + internal static bool EnableWorkerTracking => IsWorkerTrackingEnabledInConfig && EventSource.IsSupported; #if !FEATURE_WASM_THREADS From 6a972edba38e1b5cd614598ba71c885f99ea6e28 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 17 Apr 2023 16:02:12 -0700 Subject: [PATCH 024/168] Fix building errors --- .../RegisteredWaitHandle.CoreCLR.Windows.cs | 12 ++++++ .../System/Threading/ThreadPool.Windows.cs | 37 +++++++++++++++++- .../Win32ThreadPoolPreAllocatedOverlapped.cs | 2 +- .../Win32ThreadPoolRegisteredWaitHandle.cs | 5 --- .../RegisteredWaitHandle.Portable.Core.cs | 2 +- .../RegisteredWaitHandle.WindowsThreadPool.cs | 39 +------------------ .../src/System/Threading/WindowsThreadPool.cs | 2 +- 7 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs index dca0013bf6efd..311c4b993c6d0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs @@ -141,6 +141,18 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); + internal void PerformCallback(bool timedOut) + { + if (ThreadPool.UseWindowsThreadPool) + { + PerformCallbackCore(timedOut); + } + else + { + PerformCallbackPortableCore(timedOut); + } + } + ~RegisteredWaitHandle() { Debug.Assert(ThreadPool.UseWindowsThreadPool); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 36b9f75219890..8f8f60d4ffb04 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -16,6 +16,14 @@ namespace System.Threading public static partial class ThreadPool { + // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that + // the runtime may use the thread for processing other work. + // + // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be + // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread + // injection. + internal static bool YieldFromDispatchLoop => WindowsThreadPool.YieldFromDispatchLoop; + internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -33,6 +41,22 @@ public static partial class ThreadPool public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount => WindowsThreadPool.ThreadCount; + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount => WindowsThreadPool.CompletedWorkItemCount; + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); @@ -44,7 +68,18 @@ public static partial class ThreadPool internal static unsafe void RequestWorkerThread() => WindowsThreadPool.RequestWorkerThread(); - [CLSCompliant(false)] + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + } + + [CLSCompliant(false)] [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs index 1827c1a7b7553..ecbcab81ef927 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -42,7 +42,7 @@ public void Dispose() unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) { - IDeferredDisposableOnFinalReleaseCore(); + IDeferredDisposableOnFinalReleaseCore(disposed); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs index 8964b19a24fe0..e6c8d7270bb74 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -35,10 +35,6 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, uint millisecondsTimeout, bool repeating) { - if (!ThreadPool.UseWindowsThreadPool) - { - GC.SuppressFinalize(this); - } _lock = new Lock(); // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) @@ -71,7 +67,6 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int ~RegisteredWaitHandle() { - Debug.Assert(ThreadPool.UseWindowsThreadPool); // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC Debug.Assert(!_gcHandle.IsAllocated); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs index 205bde5d364a9..79c42b9bbc1f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs @@ -126,7 +126,7 @@ private void SignalUserWaitHandle() /// Perform the registered callback if the has not been signaled. /// /// Whether or not the wait timed out. - internal void PerformCallback(bool timedOut) + internal void PerformCallbackPortableCore(bool timedOut) { #if DEBUG s_callbackLock.Acquire(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 7842215a15870..17f481b250d06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -26,7 +26,7 @@ private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); - registeredWaitHandle.PerformCallback(timedOut); + registeredWaitHandle.PerformCallbackCore(timedOut); ThreadPool.IncrementCompletedWorkItemCount(); // wrapper.Exit(); } @@ -34,43 +34,6 @@ private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, private void PerformCallbackCore(bool timedOut) { - /* PR-Comment: Previous implementation - bool lockAcquired; - var spinner = new SpinWait(); - - // Prevent the race condition with Unregister and the previous PerformCallback call, which may still be - // holding the _lock. - while (!(lockAcquired = _lock.TryAcquire(0)) && !Volatile.Read(ref _unregistering)) - { - spinner.SpinOnce(); - } - - // If another thread is running Unregister, no need to restart the timer or clean up - if (lockAcquired) - { - try - { - if (!_unregistering) - { - if (_repeating) - { - // Allow this wait to fire again. Restart the timer before executing the callback. - RestartWait(); - } - else - { - // This wait will not be fired again. Free the GC handle to allow the GC to collect this object. - Debug.Assert(_gcHandle.IsAllocated); - _gcHandle.Free(); - } - } - } - finally - { - _lock.Release(); - } - } - */ // If another thread is running Unregister, no need to restart the timer or clean up lock (_lock!) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 7e51a90f5951f..256ec0cf3a70f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -179,7 +179,7 @@ internal static unsafe void RequestWorkerThread() Interop.Kernel32.SubmitThreadpoolWork(s_work); } - private static RegisteredWaitHandle RegisterWaitForSingleObject( + internal static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, object state, From 4727ea379fbfc8b3f9501362e7ad336e63fdbd95 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 24 Apr 2023 14:51:28 -0700 Subject: [PATCH 025/168] Fix RegisteredWaitHandle --- .../RegisteredWaitHandle.CoreCLR.Windows.cs | 18 +++++++++++------- .../Threading/ThreadPool.CoreCLR.Windows.cs | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs index 311c4b993c6d0..704a0e3945d18 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs @@ -31,10 +31,8 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, uint millisecondsTimeout, bool repeating) { - if (!ThreadPool.UseWindowsThreadPool) - { - GC.SuppressFinalize(this); - } + Debug.Assert(ThreadPool.UseWindowsThreadPool); + _lock = new object(); // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) @@ -60,6 +58,9 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, int millisecondsTimeout, bool repeating) { + Debug.Assert(!ThreadPool.UseWindowsThreadPool); + GC.SuppressFinalize(this); + Thread.ThrowIfNoThreadStart(); Handle = waitHandle.SafeWaitHandle; Callback = callbackHelper; @@ -137,9 +138,12 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) => RegisteredWaitCallbackCore(instance, context, wait, waitResult); - internal unsafe void RestartWait() => RestartWaitCore(); - - public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); + internal unsafe void RestartWait() + { + Debug.Assert(ThreadPool.UseWindowsThreadPool); + RestartWaitCore(); + } + public bool Unregister(WaitHandle waitObject) => ThreadPool.UseWindowsThreadPool ? UnregisterCore(waitObject) : UnregisterPortableCore(waitObject); internal void PerformCallback(bool timedOut) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs index 6e1638fc8947c..3c8d9acda1457 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs @@ -186,7 +186,7 @@ internal static unsafe void RequestWorkerThread() internal static void ReportThreadStatus(bool isWorking) { - Debug.Assert(!ThreadPool.UseWindowsThreadPool); + // Debug.Assert(!ThreadPool.UseWindowsThreadPool); PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); } } From cb38e38586d42e5eb1b95a6e69914e794db87115 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Apr 2023 14:22:04 -0700 Subject: [PATCH 026/168] Remove AotCommonPath + Sort Aot dependencies --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 900926b5e56dc..b6ec8de5d127e 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -112,7 +112,6 @@ $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src')) $(MSBuildThisFileDirectory)src $(MSBuildThisFileDirectory)..\nativeaot\System.Private.CoreLib\src - $(NativeAotSourcesRoot)\..\..\Common\src @@ -296,9 +295,9 @@ - + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs From 25319b43b7682b6bbf0d4e60e7bfde2d4d14602f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Apr 2023 14:56:49 -0700 Subject: [PATCH 027/168] OnFinalRelease only if WindowsThreadPool is enabled --- .../Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs index c102acf2f91ee..e30b5050abe98 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs @@ -102,7 +102,10 @@ public void Dispose() void IDeferredDisposable.OnFinalRelease(bool disposed) { - IDeferredDisposableOnFinalReleaseCore(disposed); + if (ThreadPool.UseWindowsThreadPool) + { + IDeferredDisposableOnFinalReleaseCore(disposed); + } } } } From cf84c0362c3522d9a768f583daa6299fc315a89b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Apr 2023 18:25:18 -0700 Subject: [PATCH 028/168] Moving RegisteredWaitCallback to a single place --- .../Threading/RegisteredWaitHandle.CoreCLR.Windows.cs | 4 ---- .../Threading/Win32ThreadPoolRegisteredWaitHandle.cs | 4 ---- .../Threading/RegisteredWaitHandle.WindowsThreadPool.cs | 8 ++++---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs index 704a0e3945d18..217b12a6cca42 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs @@ -134,10 +134,6 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb /// internal PortableThreadPool.WaitThread? WaitThread { get; set; } - [UnmanagedCallersOnly] - internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) => - RegisteredWaitCallbackCore(instance, context, wait, waitResult); - internal unsafe void RestartWait() { Debug.Assert(ThreadPool.UseWindowsThreadPool); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs index e6c8d7270bb74..350b26f6c923f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -57,10 +57,6 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO } } - [UnmanagedCallersOnly] - internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) => - RegisteredWaitCallbackCore(instance, context, wait, waitResult); - internal unsafe void RestartWait() => RestartWaitCore(); public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 17f481b250d06..0591b226c93d7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -16,11 +16,11 @@ namespace System.Threading public sealed partial class RegisteredWaitHandle : MarshalByRefObject { #pragma warning disable IDE0060 - private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) + [UnmanagedCallersOnly] + internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { - // PR-Comment: Assuming this is no longer necessary, might be wrong about this + // Commenting this one might be wrong, not sure yet // var wrapper = ThreadPoolCallbackWrapper.Enter(); - GCHandle handle = (GCHandle)context; RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); @@ -34,7 +34,7 @@ private static void RegisteredWaitCallbackCore(IntPtr instance, IntPtr context, private void PerformCallbackCore(bool timedOut) { - + // New logic might be wrong here, not sure yet // If another thread is running Unregister, no need to restart the timer or clean up lock (_lock!) { From bbf441b04ce49682da9b9235c1d5f1335407cbc8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Apr 2023 18:34:21 -0700 Subject: [PATCH 029/168] Move RestartWait to a single place --- .../System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs | 5 ----- .../System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs | 2 -- .../Threading/RegisteredWaitHandle.WindowsThreadPool.cs | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs index 217b12a6cca42..90c22e0905b3c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs @@ -134,11 +134,6 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb /// internal PortableThreadPool.WaitThread? WaitThread { get; set; } - internal unsafe void RestartWait() - { - Debug.Assert(ThreadPool.UseWindowsThreadPool); - RestartWaitCore(); - } public bool Unregister(WaitHandle waitObject) => ThreadPool.UseWindowsThreadPool ? UnregisterCore(waitObject) : UnregisterPortableCore(waitObject); internal void PerformCallback(bool timedOut) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs index 350b26f6c923f..40a4b856bb63b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -57,8 +57,6 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO } } - internal unsafe void RestartWait() => RestartWaitCore(); - public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); ~RegisteredWaitHandle() diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 0591b226c93d7..5fe9a32f9683e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -57,7 +57,7 @@ private void PerformCallbackCore(bool timedOut) _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(_callbackHelper!, timedOut); } - private unsafe void RestartWaitCore() + internal unsafe void RestartWait() { long timeout; long* pTimeout = null; // Null indicates infinite timeout From 54ae7e6d9298021e2ff1484acf7a024873bcbc62 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Apr 2023 19:06:57 -0700 Subject: [PATCH 030/168] Nit in ThreadPoolBoundHandle --- .../PreAllocatedOverlapped.CoreCLR.Windows.cs | 1 + .../PreAllocatedOverlapped.Portable.Core.cs | 1 - .../src/System/Threading/PreAllocatedOverlapped.cs | 1 + .../ThreadPoolBoundHandle.WindowsThreadPool.cs | 12 ++++++------ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs index 3929e3c12cdc0..f8f48f2acc479 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs @@ -11,6 +11,7 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { + private DeferredDisposableLifetime _lifetime; [CLSCompliant(false)] public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs index 22bdf51530a4d..e169fe734dfa2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -10,7 +10,6 @@ namespace System.Threading public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { internal readonly ThreadPoolBoundHandleOverlapped? _overlapped_portable_core; - private DeferredDisposableLifetime _lifetime; private static PreAllocatedOverlapped UnsafeCreatePortableCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index 7957332d800a9..c50c664eb3378 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -9,6 +9,7 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { + DeferredDisposableLifetime _lifetime; /// /// Initializes a new instance of the class, specifying diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index 36269a97ede62..8409297a94a0e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -169,12 +169,6 @@ private void Release() _lifetime.Release(this); } - private void IDeferredDisposableOnFinalReleaseCore(bool disposed) - { - if (disposed) - _threadPoolHandle!.Dispose(); - } - private void DisposeCore() { _lifetime.Dispose(this); @@ -190,5 +184,11 @@ private void FinalizeCore() if (!Environment.HasShutdownStarted) Dispose(); } + + private void IDeferredDisposableOnFinalReleaseCore(bool disposed) + { + if (disposed) + _threadPoolHandle!.Dispose(); + } } } From 83e1554ca047667800df8ee3d3c86a8211f75f4c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Apr 2023 19:28:43 -0700 Subject: [PATCH 031/168] Add PerformCallback to RegisteredWaitHandle.Portable.cs --- .../src/System/Threading/RegisteredWaitHandle.Portable.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index 4355edafd3877..79820b6b39936 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -92,5 +92,10 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb internal PortableThreadPool.WaitThread? WaitThread { get; set; } public bool Unregister(WaitHandle waitObject) => UnregisterPortableCore(waitObject); + + internal void PerformCallback(bool timedOut) + { + PerformCallbackPortableCore(timedOut); + } } } From 7fc89a6c97f5ee49c804b90e06cfae9344787d69 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Apr 2023 13:42:56 -0700 Subject: [PATCH 032/168] Move switch files to libraries and rename them --- .../System.Private.CoreLib.csproj | 4 - .../PreAllocatedOverlapped.CoreCLR.Windows.cs | 84 ------------ .../System.Private.CoreLib.Shared.projitems | 6 +- .../PreAllocatedOverlapped.Portable.cs | 122 +++++++++++++++++ .../Threading/PreAllocatedOverlapped.cs | 128 ++++++------------ .../System/Threading/RegisteredWaitHandle.cs} | 0 .../src/System/Threading/ThreadPool.cs} | 0 .../Threading/ThreadPoolBoundHandle.cs} | 0 8 files changed, 172 insertions(+), 172 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs rename src/{coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs => libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs} (100%) rename src/{coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs => libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs} (100%) rename src/{coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs => libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs} (100%) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index f7806017edcca..19b8a4763094e 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -290,10 +290,6 @@ Common\Interop\Windows\OleAut32\Interop.VariantClear.cs - - - - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs deleted file mode 100644 index f8f48f2acc479..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.CoreCLR.Windows.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Threading -{ - /// - /// Represents pre-allocated state for native overlapped I/O operations. - /// - /// - public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable - { - private DeferredDisposableLifetime _lifetime; - - [CLSCompliant(false)] - public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : - this(callback, state, pinData, flowExecutionContext: true) - { - } - - [CLSCompliant(false)] - public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - ThreadPool.UseWindowsThreadPool ? UnsafeCreateCore(callback, state, pinData) : UnsafeCreatePortableCore(callback, state, pinData); - - private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - if (ThreadPool.UseWindowsThreadPool) - { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); - } - else - { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); - } - } - - internal bool AddRef() => ThreadPool.UseWindowsThreadPool ? AddRefCore() : AddRefPortableCore(); - - internal void Release() - { - if (ThreadPool.UseWindowsThreadPool) - { - ReleaseCore(); - } - else - { - ReleasePortableCore(); - } - } - public void Dispose() - { - if (ThreadPool.UseWindowsThreadPool) - { - DisposeCore(); - } - else - { - DisposePortableCore(); - } - } - - ~PreAllocatedOverlapped() - { - Dispose(); - } - - unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) - { - if (ThreadPool.UseWindowsThreadPool) - { - IDeferredDisposableOnFinalReleaseCore(disposed); - } - else - { - IDeferredDisposableOnFinalReleasePortableCore(disposed); - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3a6c778460ea7..8b7da114d4e26 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2519,6 +2519,7 @@ + @@ -2539,10 +2540,13 @@ - + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs new file mode 100644 index 0000000000000..c50c664eb3378 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading +{ + /// + /// Represents pre-allocated state for native overlapped I/O operations. + /// + /// + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + DeferredDisposableLifetime _lifetime; + + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// + [CLSCompliant(false)] + public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : + this(callback, state, pinData, flowExecutionContext: true) + { + } + + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation + /// of the callback. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// + [CLSCompliant(false)] + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => + UnsafeCreatePortableCore(callback, state, pinData); + + private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(callback); + + _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); + } + + internal bool AddRef() => AddRefPortableCore(); + + internal void Release() => ReleasePortableCore(); + + /// + /// Frees the resources associated with this instance. + /// + public void Dispose() + { + DisposePortableCore(); + } + + ~PreAllocatedOverlapped() + { + Dispose(); + } + + unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + { + IDeferredDisposableOnFinalReleasePortableCore(disposed); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index c50c664eb3378..f8f48f2acc479 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -1,6 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; + namespace System.Threading { /// @@ -9,104 +11,57 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - DeferredDisposableLifetime _lifetime; + private DeferredDisposableLifetime _lifetime; - /// - /// Initializes a new instance of the class, specifying - /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when each asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes instance produced from this - /// object from other instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operations. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// The new instance can be passed to - /// , to produce - /// a instance that can be passed to the operating system in overlapped - /// I/O operations. A single instance can only be used for - /// a single native I/O operation at a time. However, the state stored in the - /// instance can be reused for subsequent native operations. - /// - /// The buffers specified in are pinned until is called. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// [CLSCompliant(false)] public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : this(callback, state, pinData, flowExecutionContext: true) { } - /// - /// Initializes a new instance of the class, specifying - /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when each asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes instance produced from this - /// object from other instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operations. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// The new instance can be passed to - /// , to produce - /// a instance that can be passed to the operating system in overlapped - /// I/O operations. A single instance can only be used for - /// a single native I/O operation at a time. However, the state stored in the - /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation - /// of the callback. - /// - /// The buffers specified in are pinned until is called. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// [CLSCompliant(false)] public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeCreatePortableCore(callback, state, pinData); + ThreadPool.UseWindowsThreadPool ? UnsafeCreateCore(callback, state, pinData) : UnsafeCreatePortableCore(callback, state, pinData); - private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { - ArgumentNullException.ThrowIfNull(callback); + if (ThreadPool.UseWindowsThreadPool) + { + ArgumentNullException.ThrowIfNull(callback); - _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); - } + _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + } + else + { + ArgumentNullException.ThrowIfNull(callback); - internal bool AddRef() => AddRefPortableCore(); + _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); + } + } - internal void Release() => ReleasePortableCore(); + internal bool AddRef() => ThreadPool.UseWindowsThreadPool ? AddRefCore() : AddRefPortableCore(); - /// - /// Frees the resources associated with this instance. - /// + internal void Release() + { + if (ThreadPool.UseWindowsThreadPool) + { + ReleaseCore(); + } + else + { + ReleasePortableCore(); + } + } public void Dispose() { - DisposePortableCore(); + if (ThreadPool.UseWindowsThreadPool) + { + DisposeCore(); + } + else + { + DisposePortableCore(); + } } ~PreAllocatedOverlapped() @@ -116,7 +71,14 @@ public void Dispose() unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) { - IDeferredDisposableOnFinalReleasePortableCore(disposed); + if (ThreadPool.UseWindowsThreadPool) + { + IDeferredDisposableOnFinalReleaseCore(disposed); + } + else + { + IDeferredDisposableOnFinalReleasePortableCore(disposed); + } } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.CoreCLR.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.CoreCLR.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs From 5e4e6cdf1641b77e80989cfeffa7ef4a541c3f4d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Apr 2023 18:08:28 -0700 Subject: [PATCH 033/168] Move nativeaot files under libraries --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 4 ---- .../src/System.Private.CoreLib.csproj | 4 ---- .../Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs | 0 .../src/System.Private.CoreLib.Shared.projitems | 6 ++++++ ...readPoolNativeOverlapped.ExecutionContextCallbackArgs.cs | 0 .../Win32ThreadPoolNativeOverlapped.OverlappedData.cs | 0 .../src/System/Threading/Win32ThreadPoolNativeOverlapped.cs | 0 7 files changed, 6 insertions(+), 8 deletions(-) rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs (100%) rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs (100%) rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs (100%) rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs (100%) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 19b8a4763094e..e35cbfd7d1e96 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -290,10 +290,6 @@ Common\Interop\Windows\OleAut32\Interop.VariantClear.cs - - - - Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index ee2836528f2f1..cc1ad16bb053a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -254,7 +254,6 @@ - @@ -304,9 +303,6 @@ - - - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs rename to src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeThreadPoolIOHandle.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 8b7da114d4e26..9f8eb80d5aac3 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -74,6 +74,7 @@ + @@ -1210,6 +1211,9 @@ + + + @@ -2519,6 +2523,8 @@ + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.ExecutionContextCallbackArgs.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.OverlappedData.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolNativeOverlapped.cs From 4cc04db30acefeaea68b7e6bd135f826d97aaa9d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Apr 2023 18:09:46 -0700 Subject: [PATCH 034/168] NativeAotSourcesRoot --- src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index e35cbfd7d1e96..cf353c09bbc96 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -111,7 +111,6 @@ $([MSBuild]::NormalizeDirectory('$(LibrariesProjectRoot)', 'Common', 'src')) $(MSBuildThisFileDirectory)src - $(MSBuildThisFileDirectory)..\nativeaot\System.Private.CoreLib\src From 1d654d6ab7eff9a96e0d2c6ced043a2865c3f398 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Apr 2023 20:23:59 -0700 Subject: [PATCH 035/168] Move Portable ThreadPool implementation to ThreadPool.CoreCLR.Core.cs. This will allow Mono to switch between Portable and Windows thread pool --- .../Threading/ThreadPool.CoreCLR.Core.cs | 97 +++++++++++++++++ .../src/System/Threading/ThreadPool.cs | 103 ++++++------------ .../src/System/Threading/ThreadPool.Mono.cs | 2 +- 3 files changed, 131 insertions(+), 71 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs index a9efb39561bcf..88954c3ba9fe0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs @@ -131,6 +131,98 @@ public static long CompletedWorkItemCount } } + private static void InitializeForThreadPoolThreadPortableCore() { } + + [SupportedOSPlatform("windows")] + private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) + { + if (overlapped == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); + } + + // OS doesn't signal handle, so do it here + overlapped->InternalLow = IntPtr.Zero; + + PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); + return true; + } + + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] + [SupportedOSPlatform("windows")] + private static bool BindHandlePortableCore(IntPtr osHandle) + { + PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); + return true; + } + + [SupportedOSPlatform("windows")] + private static bool BindHandlePortableCore(SafeHandle osHandle) + { + ArgumentNullException.ThrowIfNull(osHandle); + + bool mustReleaseSafeHandle = false; + try + { + osHandle.DangerousAddRef(ref mustReleaseSafeHandle); + + PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); + return true; + } + finally + { + if (mustReleaseSafeHandle) + osHandle.DangerousRelease(); + } + } + + private static object GetOrCreateThreadLocalCompletionCountObjectPortableCore() => + PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); + + private static bool SetMaxThreadsPortableCore(int workerThreads, int completionPortThreads) => + PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); + + private static void GetMaxThreadsPortableCore(out int workerThreads, out int completionPortThreads) + { + PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); + } + + private static bool SetMinThreadsPortableCore(int workerThreads, int completionPortThreads) => + PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); + + private static void GetMinThreadsPortableCore(out int workerThreads, out int completionPortThreads) + { + PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); + } + + private static void GetAvailableThreadsPortableCore(out int workerThreads, out int completionPortThreads) + { + PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void NotifyWorkItemProgressPortableCore() + { + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool NotifyWorkItemCompletePortableCore(object threadLocalCompletionCountObject, int currentTimeMs) => + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); + + private static bool NotifyThreadBlockedPortableCore() => + PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); + + private static void NotifyThreadUnblockedPortableCore() + { + PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); + } + + private static unsafe void RequestWorkerThreadPortableCore() + { + PortableThreadPool.ThreadPoolInstance.RequestWorker(); + } + private static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, @@ -152,5 +244,10 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( return registeredWaitHandle; } + + private static void ReportThreadStatusCore(bool isWorking) + { + PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); + } } } 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 3c8d9acda1457..3ae0362ede062 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -16,85 +16,49 @@ public static partial class ThreadPool [CLSCompliant(false)] [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) - { - if (ThreadPool.UseWindowsThreadPool) - { - return WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); - } - else - { - if (overlapped == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); - } - - // OS doesn't signal handle, so do it here - overlapped->InternalLow = IntPtr.Zero; - - PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); - return true; - } - } + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped) : + UnsafeQueueNativeOverlappedPortableCore(overlapped); [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) - { - if (ThreadPool.UseWindowsThreadPool) - { - return WindowsThreadPool.BindHandle(osHandle); - } - else - { - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); - return true; - } - } + public static bool BindHandle(IntPtr osHandle) => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.BindHandle(osHandle) : + BindHandlePortableCore(osHandle); [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) + public static bool BindHandle(SafeHandle osHandle) => + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.BindHandle(osHandle) : + BindHandlePortableCore(osHandle); + + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); + + internal static void InitializeForThreadPoolThread() { if (ThreadPool.UseWindowsThreadPool) { - return WindowsThreadPool.BindHandle(osHandle); + WindowsThreadPool.InitializeForThreadPoolThread(); } else { - ArgumentNullException.ThrowIfNull(osHandle); - - bool mustReleaseSafeHandle = false; - try - { - osHandle.DangerousAddRef(ref mustReleaseSafeHandle); - - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); - return true; - } - finally - { - if (mustReleaseSafeHandle) - osHandle.DangerousRelease(); - } + InitializeForThreadPoolThreadPortableCore(); } } - - internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); - - internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); internal static object GetOrCreateThreadLocalCompletionCountObject() => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject() : - PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); + GetOrCreateThreadLocalCompletionCountObjectPortableCore(); public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads) : - PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); + SetMaxThreadsPortableCore(workerThreads, completionPortThreads); public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) { @@ -104,14 +68,12 @@ public static void GetMaxThreads(out int workerThreads, out int completionPortTh } else { - PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); + GetMaxThreadsPortableCore(out workerThreads, out completionPortThreads); } } public static bool SetMinThreads(int workerThreads, int completionPortThreads) => - ThreadPool.UseWindowsThreadPool ? - WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads) : - PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); + SetMinThreadsPortableCore(workerThreads, completionPortThreads); public static void GetMinThreads(out int workerThreads, out int completionPortThreads) { @@ -121,9 +83,10 @@ public static void GetMinThreads(out int workerThreads, out int completionPortTh } else { - PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); + GetMinThreadsPortableCore(out workerThreads, out completionPortThreads); } } + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) { if (ThreadPool.UseWindowsThreadPool) @@ -132,9 +95,8 @@ public static void GetAvailableThreads(out int workerThreads, out int completion } else { - PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); + GetAvailableThreadsPortableCore(out workerThreads, out completionPortThreads); } - } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -146,7 +108,7 @@ internal static void NotifyWorkItemProgress() } else { - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); + NotifyWorkItemProgressPortableCore(); } } @@ -154,12 +116,12 @@ internal static void NotifyWorkItemProgress() internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs) : - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); + NotifyWorkItemCompletePortableCore(threadLocalCompletionCountObject, currentTimeMs); internal static bool NotifyThreadBlocked() => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.NotifyThreadBlocked() : - PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); + NotifyThreadBlockedPortableCore(); internal static void NotifyThreadUnblocked() { @@ -169,9 +131,10 @@ internal static void NotifyThreadUnblocked() } else { - PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); + NotifyThreadUnblockedPortableCore(); } } + internal static unsafe void RequestWorkerThread() { if (ThreadPool.UseWindowsThreadPool) @@ -180,14 +143,14 @@ internal static unsafe void RequestWorkerThread() } else { - PortableThreadPool.ThreadPoolInstance.RequestWorker(); + RequestWorkerThreadPortableCore(); } } internal static void ReportThreadStatus(bool isWorking) { - // Debug.Assert(!ThreadPool.UseWindowsThreadPool); - PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); + Debug.Assert(!ThreadPool.UseWindowsThreadPool); + ReportThreadStatusCore(isWorking); } } } diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs index 6a6dd5b82e006..b5e1eef9e736f 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs @@ -10,7 +10,7 @@ namespace System.Threading public static partial class ThreadPool { [Conditional("unnecessary")] - internal static void ReportThreadStatus(bool isWorking) + internal static void ReportThreadStatusCore(bool isWorking) { } } From 5c8d29cb577213c11e028ee053ff26d1af2bf695 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Apr 2023 20:51:07 -0700 Subject: [PATCH 036/168] Allow Mono to switch between portable and windows thread pool --- .../src/CompatibilitySuppressions.xml | 1 + .../src/CompatibilitySuppressions.xml | 1 + .../Threading/ThreadPool.Portable.Windows.cs | 6 +-- .../System/Threading/ThreadPool.Portable.cs | 29 ++++++++------ .../src/CompatibilitySuppressions.xml | 1 + .../src/CompatibilitySuppressions.xml | 1 + .../CompatibilitySuppressions.xml | 39 +++++++++++++++++++ .../System.Private.CoreLib.csproj | 6 +++ 8 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Collections/src/CompatibilitySuppressions.xml b/src/libraries/System.Collections/src/CompatibilitySuppressions.xml index c313881d43172..f281317339d43 100644 --- a/src/libraries/System.Collections/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Collections/src/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + CP0001 diff --git a/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml index d7f1235b6f415..9846cd5c1057d 100644 --- a/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + CP0002 diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs index 024110ea93b92..96b87d96d4f79 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs @@ -12,7 +12,7 @@ public static partial class ThreadPool { [CLSCompliant(false)] [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) + public static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) { if (overlapped == null) { @@ -28,14 +28,14 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) + public static bool BindHandlePortableCore(IntPtr osHandle) { PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); return true; } [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) + public static bool BindHandlePortableCore(SafeHandle osHandle) { ArgumentNullException.ThrowIfNull(osHandle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index 39e1d6453263e..f3c489181bbd1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -28,8 +28,13 @@ public static partial class ThreadPool AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); #endif + + private static bool EnsureConfigInitializedCore() { + throw new NotImplementedException(); + } + // Threadpool specific initialization of a new thread. Used by OS-provided threadpools. No-op for portable threadpool. - internal static void InitializeForThreadPoolThread() { } + private static void InitializeForThreadPoolThreadPortableCore() { } #pragma warning disable IDE0060 internal static bool CanSetMinIOCompletionThreads(int ioCompletionThreads) => false; @@ -41,15 +46,15 @@ internal static void SetMinIOCompletionThreads(int ioCompletionThreads) { } [Conditional("unnecessary")] internal static void SetMaxIOCompletionThreads(int ioCompletionThreads) { } - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => + private static bool SetMaxThreadsPortableCore(int workerThreads, int completionPortThreads) => PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => + private static void GetMaxThreadsPortableCore(out int workerThreads, out int completionPortThreads) => PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); - public static bool SetMinThreads(int workerThreads, int completionPortThreads) => + private static bool SetMinThreadsPortableCore(int workerThreads, int completionPortThreads) => PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => + private static void GetMinThreadsPortableCore(out int workerThreads, out int completionPortThreads) => PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => + private static void GetAvailableThreadsPortableCore(out int workerThreads, out int completionPortThreads) => PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); /// @@ -71,18 +76,18 @@ public static void GetAvailableThreads(out int workerThreads, out int completion /// /// This method is called to request a new thread pool worker to handle pending work. /// - internal static void RequestWorkerThread() => PortableThreadPool.ThreadPoolInstance.RequestWorker(); + private static void RequestWorkerThreadPortableCore() => PortableThreadPool.ThreadPoolInstance.RequestWorker(); - internal static void NotifyWorkItemProgress() => PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); + private static void NotifyWorkItemProgressPortableCore() => PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object? threadLocalCompletionCountObject, int currentTimeMs) => + private static bool NotifyWorkItemCompletePortableCore(object? threadLocalCompletionCountObject, int currentTimeMs) => PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); - internal static bool NotifyThreadBlocked() => PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); - internal static void NotifyThreadUnblocked() => PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); + private static bool NotifyThreadBlockedPortableCore() => PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); + private static void NotifyThreadUnblockedPortableCore() => PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); - internal static object GetOrCreateThreadLocalCompletionCountObject() => + private static object GetOrCreateThreadLocalCompletionCountObjectPortableCore() => PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); private static RegisteredWaitHandle RegisterWaitForSingleObject( diff --git a/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml index ebabc977812cb..b4c6a06271867 100644 --- a/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + CP0001 diff --git a/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml index c9bc62741a6b1..51e030bd88c0c 100644 --- a/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + CP0002 diff --git a/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml index 6836172aa2e68..b4cd2364e28b3 100644 --- a/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml +++ b/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml @@ -1,7 +1,46 @@  + + + CP0001 + T:Internal.Console + + + CP0001 + T:System.Runtime.CompilerServices.ICastable + + + CP0002 + F:System.Resources.ResourceManager.BaseNameField + + + CP0002 + F:System.Resources.ResourceSet.Reader + + + CP0002 + M:System.Threading.ThreadPool.BindHandlePortableCore(System.IntPtr) + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0002 + M:System.Threading.ThreadPool.BindHandlePortableCore(System.Runtime.InteropServices.SafeHandle) + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + + + CP0002 + M:System.Threading.ThreadPool.UnsafeQueueNativeOverlappedPortableCore(System.Threading.NativeOverlapped*) + ref/net8.0/System.Private.CoreLib.dll + lib/net8.0/System.Private.CoreLib.dll + CP0014 M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute] + + CP0014 + M:System.Runtime.InteropServices.Marshal.CreateWrapperOfType(System.Object,System.Type)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute] + \ No newline at end of file diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 1cc7d38b44fb1..5ce849019b7ae 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -271,6 +271,12 @@ CommonSystem\Collections\Generic\ArrayBuilder.cs + + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs + + + Interop\Windows\Kernel32\Interop.ThreadPool.cs + From e4612e5834572b86449dd4cdab1ff85b6ef4e31f Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 28 Apr 2023 14:00:52 -0700 Subject: [PATCH 037/168] Delete UnmanagedThreadPoolWorkItem + place calls to PortableThreadPool in ThreadPool.cs only --- .../Threading/ThreadPool.CoreCLR.Core.cs | 61 ------------------- .../System/Threading/ThreadPool.Portable.cs | 28 --------- .../src/System/Threading/ThreadPool.cs | 27 ++++---- 3 files changed, 16 insertions(+), 100 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs index 88954c3ba9fe0..2aa60acf0573b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs @@ -29,20 +29,6 @@ private void CompleteWait() } } - internal sealed class UnmanagedThreadPoolWorkItem : IThreadPoolWorkItem - { - private readonly IntPtr _callback; - private readonly IntPtr _state; - - public UnmanagedThreadPoolWorkItem(IntPtr callback, IntPtr state) - { - _callback = callback; - _state = state; - } - - unsafe void IThreadPoolWorkItem.Execute() => ((delegate* unmanaged)_callback)(_state); - } - public static partial class ThreadPool { private static bool EnsureConfigInitializedCore() @@ -176,53 +162,6 @@ private static bool BindHandlePortableCore(SafeHandle osHandle) } } - private static object GetOrCreateThreadLocalCompletionCountObjectPortableCore() => - PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); - - private static bool SetMaxThreadsPortableCore(int workerThreads, int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); - - private static void GetMaxThreadsPortableCore(out int workerThreads, out int completionPortThreads) - { - PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); - } - - private static bool SetMinThreadsPortableCore(int workerThreads, int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); - - private static void GetMinThreadsPortableCore(out int workerThreads, out int completionPortThreads) - { - PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); - } - - private static void GetAvailableThreadsPortableCore(out int workerThreads, out int completionPortThreads) - { - PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void NotifyWorkItemProgressPortableCore() - { - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool NotifyWorkItemCompletePortableCore(object threadLocalCompletionCountObject, int currentTimeMs) => - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); - - private static bool NotifyThreadBlockedPortableCore() => - PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); - - private static void NotifyThreadUnblockedPortableCore() - { - PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); - } - - private static unsafe void RequestWorkerThreadPortableCore() - { - PortableThreadPool.ThreadPoolInstance.RequestWorker(); - } - private static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index f3c489181bbd1..306fae799a1e1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -46,17 +46,6 @@ internal static void SetMinIOCompletionThreads(int ioCompletionThreads) { } [Conditional("unnecessary")] internal static void SetMaxIOCompletionThreads(int ioCompletionThreads) { } - private static bool SetMaxThreadsPortableCore(int workerThreads, int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); - private static void GetMaxThreadsPortableCore(out int workerThreads, out int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); - private static bool SetMinThreadsPortableCore(int workerThreads, int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); - private static void GetMinThreadsPortableCore(out int workerThreads, out int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); - private static void GetAvailableThreadsPortableCore(out int workerThreads, out int completionPortThreads) => - PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); - /// /// Gets the number of thread pool threads that currently exist. /// @@ -73,23 +62,6 @@ private static void GetAvailableThreadsPortableCore(out int workerThreads, out i /// public static long CompletedWorkItemCount => PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; - /// - /// This method is called to request a new thread pool worker to handle pending work. - /// - private static void RequestWorkerThreadPortableCore() => PortableThreadPool.ThreadPoolInstance.RequestWorker(); - - private static void NotifyWorkItemProgressPortableCore() => PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool NotifyWorkItemCompletePortableCore(object? threadLocalCompletionCountObject, int currentTimeMs) => - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); - - private static bool NotifyThreadBlockedPortableCore() => PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); - private static void NotifyThreadUnblockedPortableCore() => PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); - - private static object GetOrCreateThreadLocalCompletionCountObjectPortableCore() => - PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); - private static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, 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 3ae0362ede062..913171a8de056 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -53,12 +53,12 @@ internal static void InitializeForThreadPoolThread() internal static object GetOrCreateThreadLocalCompletionCountObject() => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject() : - GetOrCreateThreadLocalCompletionCountObjectPortableCore(); + PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads) : - SetMaxThreadsPortableCore(workerThreads, completionPortThreads); + PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) { @@ -68,12 +68,14 @@ public static void GetMaxThreads(out int workerThreads, out int completionPortTh } else { - GetMaxThreadsPortableCore(out workerThreads, out completionPortThreads); + PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); } } public static bool SetMinThreads(int workerThreads, int completionPortThreads) => - SetMinThreadsPortableCore(workerThreads, completionPortThreads); + ThreadPool.UseWindowsThreadPool ? + WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads) : + PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); public static void GetMinThreads(out int workerThreads, out int completionPortThreads) { @@ -83,7 +85,7 @@ public static void GetMinThreads(out int workerThreads, out int completionPortTh } else { - GetMinThreadsPortableCore(out workerThreads, out completionPortThreads); + PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); } } @@ -95,7 +97,7 @@ public static void GetAvailableThreads(out int workerThreads, out int completion } else { - GetAvailableThreadsPortableCore(out workerThreads, out completionPortThreads); + PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); } } @@ -108,7 +110,7 @@ internal static void NotifyWorkItemProgress() } else { - NotifyWorkItemProgressPortableCore(); + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); } } @@ -116,12 +118,12 @@ internal static void NotifyWorkItemProgress() internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs) : - NotifyWorkItemCompletePortableCore(threadLocalCompletionCountObject, currentTimeMs); + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); internal static bool NotifyThreadBlocked() => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.NotifyThreadBlocked() : - NotifyThreadBlockedPortableCore(); + PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); internal static void NotifyThreadUnblocked() { @@ -131,10 +133,13 @@ internal static void NotifyThreadUnblocked() } else { - NotifyThreadUnblockedPortableCore(); + PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); } } + /// + /// This method is called to request a new thread pool worker to handle pending work. + /// internal static unsafe void RequestWorkerThread() { if (ThreadPool.UseWindowsThreadPool) @@ -143,7 +148,7 @@ internal static unsafe void RequestWorkerThread() } else { - RequestWorkerThreadPortableCore(); + PortableThreadPool.ThreadPoolInstance.RequestWorker(); } } From 64b279be2ce65fe4a17919bfcaf581dd11480158 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 28 Apr 2023 15:05:11 -0700 Subject: [PATCH 038/168] Move imports of Interop.ThreadPool.cs and Interop.ThreadPoolIO.cs to libraries --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 6 ------ .../src/System.Private.CoreLib.csproj | 6 ------ .../src/System.Private.CoreLib.Shared.projitems | 6 ++++++ .../System.Private.CoreLib/System.Private.CoreLib.csproj | 6 ------ 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index cf353c09bbc96..81b6cc7120a32 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -289,12 +289,6 @@ Common\Interop\Windows\OleAut32\Interop.VariantClear.cs - - Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs - - - Interop\Windows\Kernel32\Interop.ThreadPool.cs - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index cc1ad16bb053a..f7508d7cf9ced 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -284,9 +284,6 @@ Interop\Windows\Kernel32\Interop.GetTickCount64.cs - - Interop\Windows\Kernel32\Interop.ThreadPool.cs - Interop\Windows\Kernel32\Interop.Timer.cs @@ -295,9 +292,6 @@ Interop\Windows\Kernel32\Interop.DynamicLoad.cs - - Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs - diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 9f8eb80d5aac3..3f89b42724915 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1590,6 +1590,12 @@ Common\Interop\Windows\Kernel32\Interop.CreateFile.cs + + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs + + + Interop\Windows\Kernel32\Interop.ThreadPool.cs + Common\Interop\Windows\NtDll\Interop.NtCreateFile.cs diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 5ce849019b7ae..1cc7d38b44fb1 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -271,12 +271,6 @@ CommonSystem\Collections\Generic\ArrayBuilder.cs - - Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs - - - Interop\Windows\Kernel32\Interop.ThreadPool.cs - From 4a97daf1d9d0e404901b4814f1911a6ef9f4c858 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 28 Apr 2023 15:16:55 -0700 Subject: [PATCH 039/168] Make *Core methods private --- .../src/System/Threading/ThreadPool.Portable.Windows.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs index 96b87d96d4f79..d1e959ce89c31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs @@ -10,9 +10,8 @@ namespace System.Threading { public static partial class ThreadPool { - [CLSCompliant(false)] [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) + private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) { if (overlapped == null) { @@ -28,14 +27,14 @@ public static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapp [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - public static bool BindHandlePortableCore(IntPtr osHandle) + private static bool BindHandlePortableCore(IntPtr osHandle) { PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); return true; } [SupportedOSPlatform("windows")] - public static bool BindHandlePortableCore(SafeHandle osHandle) + private static bool BindHandlePortableCore(SafeHandle osHandle) { ArgumentNullException.ThrowIfNull(osHandle); From 272455c9a37ed694ef12aeff8bd786bd3ec7f592 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 28 Apr 2023 15:22:19 -0700 Subject: [PATCH 040/168] Restore compability supressions --- .../src/CompatibilitySuppressions.xml | 1 - .../src/CompatibilitySuppressions.xml | 1 - .../src/CompatibilitySuppressions.xml | 1 - .../src/CompatibilitySuppressions.xml | 1 - .../CompatibilitySuppressions.xml | 39 ------------------- 5 files changed, 43 deletions(-) diff --git a/src/libraries/System.Collections/src/CompatibilitySuppressions.xml b/src/libraries/System.Collections/src/CompatibilitySuppressions.xml index f281317339d43..c313881d43172 100644 --- a/src/libraries/System.Collections/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Collections/src/CompatibilitySuppressions.xml @@ -1,5 +1,4 @@  - CP0001 diff --git a/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml b/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml index 9846cd5c1057d..d7f1235b6f415 100644 --- a/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Diagnostics.Debug/src/CompatibilitySuppressions.xml @@ -1,5 +1,4 @@  - CP0002 diff --git a/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml index b4c6a06271867..ebabc977812cb 100644 --- a/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices/src/CompatibilitySuppressions.xml @@ -1,5 +1,4 @@  - CP0001 diff --git a/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml index 51e030bd88c0c..c9bc62741a6b1 100644 --- a/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime/src/CompatibilitySuppressions.xml @@ -1,5 +1,4 @@  - CP0002 diff --git a/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml index b4cd2364e28b3..6836172aa2e68 100644 --- a/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml +++ b/src/mono/System.Private.CoreLib/CompatibilitySuppressions.xml @@ -1,46 +1,7 @@  - - - CP0001 - T:Internal.Console - - - CP0001 - T:System.Runtime.CompilerServices.ICastable - - - CP0002 - F:System.Resources.ResourceManager.BaseNameField - - - CP0002 - F:System.Resources.ResourceSet.Reader - - - CP0002 - M:System.Threading.ThreadPool.BindHandlePortableCore(System.IntPtr) - ref/net8.0/System.Private.CoreLib.dll - lib/net8.0/System.Private.CoreLib.dll - - - CP0002 - M:System.Threading.ThreadPool.BindHandlePortableCore(System.Runtime.InteropServices.SafeHandle) - ref/net8.0/System.Private.CoreLib.dll - lib/net8.0/System.Private.CoreLib.dll - - - CP0002 - M:System.Threading.ThreadPool.UnsafeQueueNativeOverlappedPortableCore(System.Threading.NativeOverlapped*) - ref/net8.0/System.Private.CoreLib.dll - lib/net8.0/System.Private.CoreLib.dll - CP0014 M:System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(System.Object)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute] - - CP0014 - M:System.Runtime.InteropServices.Marshal.CreateWrapperOfType(System.Object,System.Type)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute] - \ No newline at end of file From a8278b5b6d4a5e717c5de156a0e1916cbf8b4593 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 28 Apr 2023 16:22:31 -0700 Subject: [PATCH 041/168] Remove ThreadPool.CoreCLR.cs --- .../System.Private.CoreLib.csproj | 1 - .../System/Threading/ThreadPool.CoreCLR.cs | 84 ------------------- 2 files changed, 85 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 81b6cc7120a32..64ae40f8da2d9 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -233,7 +233,6 @@ - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs deleted file mode 100644 index 42be2217932a3..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** -** -** Purpose: Class for creating and managing a threadpool -** -** -=============================================================================*/ - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Threading -{ - public static partial class ThreadPool - { - internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); - - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) - { - return PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); - } - - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) - { - PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); - } - - public static bool SetMinThreads(int workerThreads, int completionPortThreads) - { - return PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); - } - - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) - { - PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); - } - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) - { - PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); - } - - internal static void RequestWorkerThread() - { - PortableThreadPool.ThreadPoolInstance.RequestWorker(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs) - { - return - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete( - threadLocalCompletionCountObject, - currentTimeMs); - } - - internal static void ReportThreadStatus(bool isWorking) - { - PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); - } - - internal static void NotifyWorkItemProgress() - { - PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); - } - - internal static bool NotifyThreadBlocked() => PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); - - internal static void NotifyThreadUnblocked() - { - PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); - } - - internal static object? GetOrCreateThreadLocalCompletionCountObject() => - PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); - } -} From 03dfd3e005d87da1c216877a17493e11167ff1fe Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 28 Apr 2023 16:41:28 -0700 Subject: [PATCH 042/168] Remove PreAllocatedOverlapped.Portable.cs --- .../System.Private.CoreLib.Shared.projitems | 1 - .../PreAllocatedOverlapped.Portable.Core.cs | 38 ++++++ .../PreAllocatedOverlapped.Portable.cs | 122 ------------------ .../Threading/PreAllocatedOverlapped.cs | 36 +++++- .../Threading/ThreadPool.Portable.Unix.cs | 6 +- 5 files changed, 76 insertions(+), 127 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3f89b42724915..a00a6e6dbcab2 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2553,7 +2553,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs index e169fe734dfa2..01acc082ae7ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -11,6 +11,41 @@ public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDispo { internal readonly ThreadPoolBoundHandleOverlapped? _overlapped_portable_core; + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation + /// of the callback. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// private static PreAllocatedOverlapped UnsafeCreatePortableCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); @@ -24,6 +59,9 @@ private void ReleasePortableCore() _lifetime.Release(this); } + /// + /// Frees the resources associated with this instance. + /// private void DisposePortableCore() { _lifetime.Dispose(this); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs index c50c664eb3378..e69de29bb2d1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Threading -{ - /// - /// Represents pre-allocated state for native overlapped I/O operations. - /// - /// - public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable - { - DeferredDisposableLifetime _lifetime; - - /// - /// Initializes a new instance of the class, specifying - /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when each asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes instance produced from this - /// object from other instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operations. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// The new instance can be passed to - /// , to produce - /// a instance that can be passed to the operating system in overlapped - /// I/O operations. A single instance can only be used for - /// a single native I/O operation at a time. However, the state stored in the - /// instance can be reused for subsequent native operations. - /// - /// The buffers specified in are pinned until is called. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// - [CLSCompliant(false)] - public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : - this(callback, state, pinData, flowExecutionContext: true) - { - } - - /// - /// Initializes a new instance of the class, specifying - /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when each asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes instance produced from this - /// object from other instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operations. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// The new instance can be passed to - /// , to produce - /// a instance that can be passed to the operating system in overlapped - /// I/O operations. A single instance can only be used for - /// a single native I/O operation at a time. However, the state stored in the - /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation - /// of the callback. - /// - /// The buffers specified in are pinned until is called. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// - [CLSCompliant(false)] - public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeCreatePortableCore(callback, state, pinData); - - private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - - _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); - } - - internal bool AddRef() => AddRefPortableCore(); - - internal void Release() => ReleasePortableCore(); - - /// - /// Frees the resources associated with this instance. - /// - public void Dispose() - { - DisposePortableCore(); - } - - ~PreAllocatedOverlapped() - { - Dispose(); - } - - unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) - { - IDeferredDisposableOnFinalReleasePortableCore(disposed); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index f8f48f2acc479..028536d42e1f9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -12,7 +12,41 @@ namespace System.Threading public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { private DeferredDisposableLifetime _lifetime; - + + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// [CLSCompliant(false)] public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : this(callback, state, pinData, flowExecutionContext: true) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs index 1d4e0c171496f..6d341722ca2fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs @@ -10,16 +10,16 @@ public static partial class ThreadPool { [CLSCompliant(false)] [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => + private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) => + private static bool BindHandlePortableCore(IntPtr osHandle) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) => + private static bool BindHandlePortableCore(SafeHandle osHandle) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } } From 26bd6ace92585ad44461ef937da703b882047983 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 28 Apr 2023 16:54:49 -0700 Subject: [PATCH 043/168] Move CompleteWaitThreadPoolWorkItem to libraries --- .../src/System/Threading/ThreadPool.CoreCLR.Core.cs | 10 ---------- .../src/System/Threading/ThreadPool.cs | 11 +++++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs index 2aa60acf0573b..23f60b94b3a98 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs @@ -18,16 +18,6 @@ namespace System.Threading { - internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem - { - void IThreadPoolWorkItem.Execute() => CompleteWait(); - - // Entry point from unmanaged code - private void CompleteWait() - { - PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); - } - } public static partial class ThreadPool { 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 913171a8de056..026b0a5558c25 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -8,6 +8,17 @@ namespace System.Threading { + internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem + { + void IThreadPoolWorkItem.Execute() => CompleteWait(); + + // Entry point from unmanaged code + private void CompleteWait() + { + PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); + } + } + public static partial class ThreadPool { internal static readonly bool UseWindowsThreadPool = From 42f3317f3e2909151f7e6542968f60da25bd8219 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 May 2023 11:43:03 -0700 Subject: [PATCH 044/168] Move most of ThreadPool.CoreCLR.Core to libraries --- .../Threading/ThreadPool.CoreCLR.Core.cs | 99 ----------------- .../Threading/PreAllocatedOverlapped.cs | 2 +- .../src/System/Threading/ThreadPool.cs | 100 ++++++++++++++++++ 3 files changed, 101 insertions(+), 100 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs index 23f60b94b3a98..c7f804344fcb5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs @@ -79,104 +79,5 @@ private static extern unsafe int GetNextConfigUInt32Value( private static bool GetEnableWorkerTracking() => AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); - /// - /// Gets the number of thread pool threads that currently exist. - /// - /// - /// For a thread pool implementation that may have different types of threads, the count includes all types. - /// - public static int ThreadCount - { - get - { - return PortableThreadPool.ThreadPoolInstance.ThreadCount; - } - } - - /// - /// Gets the number of work items that have been processed so far. - /// - /// - /// For a thread pool implementation that may have different types of work items, the count includes all types. - /// - public static long CompletedWorkItemCount - { - get - { - return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; - } - } - - private static void InitializeForThreadPoolThreadPortableCore() { } - - [SupportedOSPlatform("windows")] - private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) - { - if (overlapped == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); - } - - // OS doesn't signal handle, so do it here - overlapped->InternalLow = IntPtr.Zero; - - PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); - return true; - } - - [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] - [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(IntPtr osHandle) - { - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); - return true; - } - - [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(SafeHandle osHandle) - { - ArgumentNullException.ThrowIfNull(osHandle); - - bool mustReleaseSafeHandle = false; - try - { - osHandle.DangerousAddRef(ref mustReleaseSafeHandle); - - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); - return true; - } - finally - { - if (mustReleaseSafeHandle) - osHandle.DangerousRelease(); - } - } - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - object? state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(waitObject); - ArgumentNullException.ThrowIfNull(callBack); - - RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( - waitObject, - new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), - (int)millisecondsTimeOutInterval, - !executeOnlyOnce); - - PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); - - return registeredWaitHandle; - } - - private static void ReportThreadStatusCore(bool isWorking) - { - PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index 028536d42e1f9..800681da24a8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -12,7 +12,7 @@ namespace System.Threading public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { private DeferredDisposableLifetime _lifetime; - + /// /// Initializes a new instance of the class, specifying /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided 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 026b0a5558c25..c5216e8bda176 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -168,5 +168,105 @@ internal static void ReportThreadStatus(bool isWorking) Debug.Assert(!ThreadPool.UseWindowsThreadPool); ReportThreadStatusCore(isWorking); } + + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount + { + get + { + return PortableThreadPool.ThreadPoolInstance.ThreadCount; + } + } + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount + { + get + { + return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; + } + } + + private static void InitializeForThreadPoolThreadPortableCore() { } + + [SupportedOSPlatform("windows")] + private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) + { + if (overlapped == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); + } + + // OS doesn't signal handle, so do it here + overlapped->InternalLow = IntPtr.Zero; + + PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); + return true; + } + + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] + [SupportedOSPlatform("windows")] + private static bool BindHandlePortableCore(IntPtr osHandle) + { + PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); + return true; + } + + [SupportedOSPlatform("windows")] + private static bool BindHandlePortableCore(SafeHandle osHandle) + { + ArgumentNullException.ThrowIfNull(osHandle); + + bool mustReleaseSafeHandle = false; + try + { + osHandle.DangerousAddRef(ref mustReleaseSafeHandle); + + PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); + return true; + } + finally + { + if (mustReleaseSafeHandle) + osHandle.DangerousRelease(); + } + } + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object? state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(waitObject); + ArgumentNullException.ThrowIfNull(callBack); + + RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( + waitObject, + new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), + (int)millisecondsTimeOutInterval, + !executeOnlyOnce); + + PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); + + return registeredWaitHandle; + } + + private static void ReportThreadStatusCore(bool isWorking) + { + PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); + } } } From 3645b3ade51be2c76864f6708f22788b06077cd9 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 May 2023 17:49:22 -0700 Subject: [PATCH 045/168] Use code in libraries instead of Win32ThreadPoolBoundHandle --- .../src/System.Private.CoreLib.csproj | 2 - .../System/Threading/ThreadPool.NativeAot.cs | 26 -------- .../System/Threading/ThreadPool.Windows.cs | 66 ------------------- .../Threading/Win32ThreadPoolBoundHandle.cs | 65 ------------------ .../System.Private.CoreLib.Shared.projitems | 6 +- .../src/System/Threading/ThreadPool.cs | 36 +++++----- 6 files changed, 21 insertions(+), 180 deletions(-) delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index f7508d7cf9ced..1ea98c5133a81 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -236,7 +236,6 @@ - @@ -296,7 +295,6 @@ - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs deleted file mode 100644 index 2fe7d1dd2c5d6..0000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** -** -** Purpose: Class for creating and managing a threadpool -** -** -=============================================================================*/ - -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Threading -{ - public static partial class ThreadPool - { - [Conditional("unnecessary")] - internal static void ReportThreadStatus(bool isWorking) - { - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 8f8f60d4ffb04..34607a9ee57c7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -23,71 +23,5 @@ public static partial class ThreadPool // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread // injection. internal static bool YieldFromDispatchLoop => WindowsThreadPool.YieldFromDispatchLoop; - - internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); - - internal static object GetOrCreateThreadLocalCompletionCountObject() => WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject(); - - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads); - - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); - - public static bool SetMinThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads); - - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); - - /// - /// Gets the number of thread pool threads that currently exist. - /// - /// - /// For a thread pool implementation that may have different types of threads, the count includes all types. - /// - public static int ThreadCount => WindowsThreadPool.ThreadCount; - - /// - /// Gets the number of work items that have been processed so far. - /// - /// - /// For a thread pool implementation that may have different types of work items, the count includes all types. - /// - public static long CompletedWorkItemCount => WindowsThreadPool.CompletedWorkItemCount; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int _ /*currentTimeMs*/) => WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, _); - - internal static bool NotifyThreadBlocked() => WindowsThreadPool.NotifyThreadBlocked(); - internal static void NotifyThreadUnblocked() => WindowsThreadPool.NotifyThreadUnblocked(); - - internal static unsafe void RequestWorkerThread() => WindowsThreadPool.RequestWorkerThread(); - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - object state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); - } - - [CLSCompliant(false)] - [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); - - [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] - [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) => WindowsThreadPool.BindHandle(osHandle); - - [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) => WindowsThreadPool.BindHandle(osHandle); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs deleted file mode 100644 index 239081651dd37..0000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; -using System.IO; - -namespace System.Threading -{ - // - // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool - // - public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable - { - private readonly SafeHandle _handle; - private readonly SafeThreadPoolIOHandle _threadPoolHandle; - private DeferredDisposableLifetime _lifetime; - - private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) - { - _threadPoolHandle = threadPoolHandle; - _handle = handle; - } - - public SafeHandle Handle - { - get { return _handle; } - } - - public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandleCore(handle); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlappedCore(callback, state, pinData); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeAllocateNativeOverlappedCore(callback, state, pinData); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => - AllocateNativeOverlappedCore(preAllocated); - - [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedCore(overlapped); - - [CLSCompliant(false)] - public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); - - public void Dispose() => DisposeCore(); - - ~ThreadPoolBoundHandle() - { - FinalizeCore(); - } - - void IDeferredDisposable.OnFinalRelease(bool disposed) - { - IDeferredDisposableOnFinalReleaseCore(disposed); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a00a6e6dbcab2..8e279874dc284 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1196,6 +1196,8 @@ + + @@ -2529,9 +2531,6 @@ - - - @@ -2557,7 +2556,6 @@ - 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 c5216e8bda176..07cb430fcecb3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -166,7 +166,7 @@ internal static unsafe void RequestWorkerThread() internal static void ReportThreadStatus(bool isWorking) { Debug.Assert(!ThreadPool.UseWindowsThreadPool); - ReportThreadStatusCore(isWorking); + PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); } /// @@ -179,7 +179,7 @@ public static int ThreadCount { get { - return PortableThreadPool.ThreadPoolInstance.ThreadCount; + return ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.ThreadCount : PortableThreadPool.ThreadPoolInstance.ThreadCount; } } @@ -193,7 +193,7 @@ public static long CompletedWorkItemCount { get { - return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; + return ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.CompletedWorkItemCount : PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; } } @@ -250,23 +250,25 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( bool executeOnlyOnce, bool flowExecutionContext) { - ArgumentNullException.ThrowIfNull(waitObject); - ArgumentNullException.ThrowIfNull(callBack); - - RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( - waitObject, - new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), - (int)millisecondsTimeOutInterval, - !executeOnlyOnce); + if (ThreadPool.UseWindowsThreadPool) + { + return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + } + else + { + ArgumentNullException.ThrowIfNull(waitObject); + ArgumentNullException.ThrowIfNull(callBack); - PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); + RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( + waitObject, + new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), + (int)millisecondsTimeOutInterval, + !executeOnlyOnce); - return registeredWaitHandle; - } + PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); - private static void ReportThreadStatusCore(bool isWorking) - { - PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); + return registeredWaitHandle; + } } } } From adcc33b58ecc5bf61a9b65bbd62abd21bc579d72 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 May 2023 17:51:26 -0700 Subject: [PATCH 046/168] ThreadPool.CoreCLR.Core.cs -> ThreadPool.CoreCLR.cs | Trimming missing for nativeaot --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 2 +- .../{ThreadPool.CoreCLR.Core.cs => ThreadPool.CoreCLR.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/coreclr/System.Private.CoreLib/src/System/Threading/{ThreadPool.CoreCLR.Core.cs => ThreadPool.CoreCLR.cs} (100%) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 64ae40f8da2d9..19cf2c20e69d1 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -233,7 +233,7 @@ - + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.Core.cs rename to src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs From 0db9013de8a9ddd517692dcc4b38cffdbf917ced Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2023 15:43:45 -0700 Subject: [PATCH 047/168] Add switch to ILLink.Substitutions.Shared.xml --- .../src/ILLink/ILLink.Substitutions.Shared.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index 9765f3ab4f662..ea7cd9fa94be6 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -37,5 +37,9 @@ + + + + From 0cb6eddb2619564ab5918683fe8fb0770c278822 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2023 17:55:30 -0700 Subject: [PATCH 048/168] Camel case used in overlapped files --- .../Threading/Win32ThreadPoolRegisteredWaitHandle.cs | 1 - .../PreAllocatedOverlapped.Portable.Core.cs | 12 ++++++------ .../src/System/Threading/PreAllocatedOverlapped.cs | 4 ++-- .../Threading/ThreadPoolBoundHandle.Portable.Core.cs | 2 +- .../ThreadPoolBoundHandle.WindowsThreadPool.cs | 4 ++-- ...adPoolPreAllocatedOverlapped.WindowsThreadPool.cs | 10 +++++----- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs index 40a4b856bb63b..32688f29dc2ee 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs @@ -37,7 +37,6 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO { _lock = new Lock(); - // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) waitHandle.DangerousAddRef(); _waitHandle = waitHandle; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs index 01acc082ae7ac..2cff8b737a7ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -9,7 +9,7 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly ThreadPoolBoundHandleOverlapped? _overlapped_portable_core; + internal readonly ThreadPoolBoundHandleOverlapped? _overlappedPortableCore; /// /// Initializes a new instance of the class, specifying @@ -70,17 +70,17 @@ private void DisposePortableCore() private unsafe void IDeferredDisposableOnFinalReleasePortableCore(bool disposed) { - if (_overlapped_portable_core != null) // protect against ctor throwing exception and leaving field uninitialized + if (_overlappedPortableCore != null) // protect against ctor throwing exception and leaving field uninitialized { if (disposed) { - Overlapped.Free(_overlapped_portable_core._nativeOverlapped); + Overlapped.Free(_overlappedPortableCore._nativeOverlapped); } else { - _overlapped_portable_core._boundHandle = null; - _overlapped_portable_core._completed = false; - *_overlapped_portable_core._nativeOverlapped = default; + _overlappedPortableCore._boundHandle = null; + _overlappedPortableCore._completed = false; + *_overlappedPortableCore._nativeOverlapped = default; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index 800681da24a8c..1643ab3530523 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -63,13 +63,13 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta { ArgumentNullException.ThrowIfNull(callback); - _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + _overlappedCore = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); } else { ArgumentNullException.ThrowIfNull(callback); - _overlapped_portable_core = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); + _overlappedPortableCore = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index 36ecb85e3c15f..47268fa35971b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -50,7 +50,7 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) preAllocated.AddRef(); try { - ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped_portable_core!; + ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlappedPortableCore!; if (overlapped._boundHandle != null) throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index 8409297a94a0e..fc70430d955f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -76,7 +76,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) addedRefToThis = AddRef(); addedRefToPreAllocated = preAllocated.AddRef(); - Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped_core->Data; + Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlappedCore->Data; if (data._boundHandle != null) throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); @@ -84,7 +84,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!); - return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped_core); + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedCore); } catch { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs index ba25d06a4c297..da5b4d32d80ec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs @@ -7,7 +7,7 @@ namespace System.Threading { public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlapped_core; + internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlappedCore; private static PreAllocatedOverlapped UnsafeCreateCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); @@ -22,7 +22,7 @@ private void ReleaseCore() _lifetime.Release(this); } - internal unsafe bool IsUserObject(byte[]? buffer) => _overlapped_core->IsUserObject(buffer); + internal unsafe bool IsUserObject(byte[]? buffer) => _overlappedCore->IsUserObject(buffer); private void DisposeCore() { @@ -32,12 +32,12 @@ private void DisposeCore() private unsafe void IDeferredDisposableOnFinalReleaseCore(bool disposed) { - if (_overlapped_core != null) + if (_overlappedCore != null) { if (disposed) - Win32ThreadPoolNativeOverlapped.Free(_overlapped_core); + Win32ThreadPoolNativeOverlapped.Free(_overlappedCore); else - *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlapped_core) = default(NativeOverlapped); + *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlappedCore) = default(NativeOverlapped); } } } From 4296e0a8c815293c97ff60ee96c3e87a809b5953 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2023 17:57:09 -0700 Subject: [PATCH 049/168] Revert changes in RuntimeEventSource --- .../src/System/Diagnostics/Tracing/RuntimeEventSource.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs index 7f373f9f41723..5c01e52e0e467 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs @@ -97,10 +97,10 @@ protected override void OnEventCommand(EventCommandEventArgs command) _gen0GCCounter ??= new IncrementingPollingCounter("gen-0-gc-count", this, () => GC.CollectionCount(0)) { DisplayName = "Gen 0 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; _gen1GCCounter ??= new IncrementingPollingCounter("gen-1-gc-count", this, () => GC.CollectionCount(1)) { DisplayName = "Gen 1 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; _gen2GCCounter ??= new IncrementingPollingCounter("gen-2-gc-count", this, () => GC.CollectionCount(2)) { DisplayName = "Gen 2 GC Count", DisplayRateTimeScale = new TimeSpan(0, 1, 0) }; - _threadPoolThreadCounter ??= new PollingCounter("threadpool-thread-count", this, () => WindowsThreadPool.ThreadCount) { DisplayName = "ThreadPool Thread Count" }; + _threadPoolThreadCounter ??= new PollingCounter("threadpool-thread-count", this, () => ThreadPool.ThreadCount) { DisplayName = "ThreadPool Thread Count" }; _monitorContentionCounter ??= new IncrementingPollingCounter("monitor-lock-contention-count", this, () => Monitor.LockContentionCount) { DisplayName = "Monitor Lock Contention Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _threadPoolQueueCounter ??= new PollingCounter("threadpool-queue-length", this, () => ThreadPool.PendingWorkItemCount) { DisplayName = "ThreadPool Queue Length" }; - _completedItemsCounter ??= new IncrementingPollingCounter("threadpool-completed-items-count", this, () => WindowsThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; + _completedItemsCounter ??= new IncrementingPollingCounter("threadpool-completed-items-count", this, () => ThreadPool.CompletedWorkItemCount) { DisplayName = "ThreadPool Completed Work Item Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _allocRateCounter ??= new IncrementingPollingCounter("alloc-rate", this, () => GC.GetTotalAllocatedBytes()) { DisplayName = "Allocation Rate", DisplayUnits = "B", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _timerCounter ??= new PollingCounter("active-timer-count", this, () => Timer.ActiveCount) { DisplayName = "Number of Active Timers" }; _fragmentationCounter ??= new PollingCounter("gc-fragmentation", this, () => { From 89f10b5a919ffc8b3a51664744a466cc25ee8fdc Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2023 18:24:54 -0700 Subject: [PATCH 050/168] Fix NativeAOT build --- .../src/System.Private.CoreLib.csproj | 1 + .../System/Threading/ThreadPool.NativeAot.cs | 26 ++++++++ .../System/Threading/ThreadPool.Windows.cs | 66 +++++++++++++++++++ .../Win32ThreadPoolPreAllocatedOverlapped.cs | 2 +- .../System.Private.CoreLib.Shared.projitems | 5 +- .../Threading/Win32ThreadPoolBoundHandle.cs | 65 ++++++++++++++++++ 6 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 1ea98c5133a81..50e229cc6a9f0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -236,6 +236,7 @@ + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs new file mode 100644 index 0000000000000..2fe7d1dd2c5d6 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================================= +** +** +** +** Purpose: Class for creating and managing a threadpool +** +** +=============================================================================*/ + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + public static partial class ThreadPool + { + [Conditional("unnecessary")] + internal static void ReportThreadStatus(bool isWorking) + { + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 34607a9ee57c7..8f8f60d4ffb04 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -23,5 +23,71 @@ public static partial class ThreadPool // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread // injection. internal static bool YieldFromDispatchLoop => WindowsThreadPool.YieldFromDispatchLoop; + + internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); + + internal static object GetOrCreateThreadLocalCompletionCountObject() => WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject(); + + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads); + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); + + public static bool SetMinThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads); + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); + + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount => WindowsThreadPool.ThreadCount; + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount => WindowsThreadPool.CompletedWorkItemCount; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int _ /*currentTimeMs*/) => WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, _); + + internal static bool NotifyThreadBlocked() => WindowsThreadPool.NotifyThreadBlocked(); + internal static void NotifyThreadUnblocked() => WindowsThreadPool.NotifyThreadUnblocked(); + + internal static unsafe void RequestWorkerThread() => WindowsThreadPool.RequestWorkerThread(); + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + } + + [CLSCompliant(false)] + [SupportedOSPlatform("windows")] + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); + + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] + [SupportedOSPlatform("windows")] + public static bool BindHandle(IntPtr osHandle) => WindowsThreadPool.BindHandle(osHandle); + + [SupportedOSPlatform("windows")] + public static bool BindHandle(SafeHandle osHandle) => WindowsThreadPool.BindHandle(osHandle); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs index ecbcab81ef927..e9a7dc9dabbc3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs @@ -23,7 +23,7 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta { ArgumentNullException.ThrowIfNull(callback); - _overlapped_core = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + _overlappedCore = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); } internal bool AddRef() => AddRefCore(); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 8e279874dc284..a6ff49a442370 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1196,8 +1196,6 @@ - - @@ -2531,6 +2529,7 @@ + @@ -2556,6 +2555,7 @@ + @@ -2587,6 +2587,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs new file mode 100644 index 0000000000000..239081651dd37 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; + +namespace System.Threading +{ + // + // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool + // + public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable + { + private readonly SafeHandle _handle; + private readonly SafeThreadPoolIOHandle _threadPoolHandle; + private DeferredDisposableLifetime _lifetime; + + private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) + { + _threadPoolHandle = threadPoolHandle; + _handle = handle; + } + + public SafeHandle Handle + { + get { return _handle; } + } + + public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandleCore(handle); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlappedCore(callback, state, pinData); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + UnsafeAllocateNativeOverlappedCore(callback, state, pinData); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => + AllocateNativeOverlappedCore(preAllocated); + + [CLSCompliant(false)] + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedCore(overlapped); + + [CLSCompliant(false)] + public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); + + public void Dispose() => DisposeCore(); + + ~ThreadPoolBoundHandle() + { + FinalizeCore(); + } + + void IDeferredDisposable.OnFinalRelease(bool disposed) + { + IDeferredDisposableOnFinalReleaseCore(disposed); + } + } +} From 3c5d3f654b722eb3f51b4a196d44133e268eb67f Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 3 May 2023 18:33:59 -0700 Subject: [PATCH 051/168] Move Win32ThreadPoolPreAllocatedOverlapped.cs and Win32ThreadPoolRegisteredWaitHandle.cs to libraries --- .../System.Private.CoreLib/src/System.Private.CoreLib.csproj | 2 -- .../src/System.Private.CoreLib.Shared.projitems | 2 ++ .../System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs | 0 .../src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs (100%) rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs (100%) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 50e229cc6a9f0..24ef96d41a532 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -296,8 +296,6 @@ - - diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a6ff49a442370..3636efcbbcf14 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2588,6 +2588,8 @@ + + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs From a6f967bc4f52f694c858f4811d92332f9a7c0c49 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 May 2023 11:59:09 -0700 Subject: [PATCH 052/168] Revert changes in Thread.cs and ThreadPool.Portable.Unix.cs. Rename the latter to ThreadPool.Unix.cs --- .../src/System/Threading/Thread.cs | 12 +----------- ...hreadPool.Portable.Unix.cs => ThreadPool.Unix.cs} | 6 +++--- 2 files changed, 4 insertions(+), 14 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{ThreadPool.Portable.Unix.cs => ThreadPool.Unix.cs} (76%) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index fe6430272215d..d80024f7b8076 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -369,17 +369,7 @@ public static void Sleep(int millisecondsTimeout) #if !NATIVEAOT /// Returns the operating system identifier for the current thread. - internal static ulong CurrentOSThreadId - { - get - { -#if CORECLR && WINDOWS - return ThreadPool.WindowsThreadPool ? CurrentOSThreadIdCore : GetCurrentOSThreadId(); -#else - return GetCurrentOSThreadId(); -#endif - } - } + internal static ulong CurrentOSThreadId => GetCurrentOSThreadId(); #endif public ExecutionContext? ExecutionContext => ExecutionContext.Capture(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs similarity index 76% rename from src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 6d341722ca2fa..1d4e0c171496f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -10,16 +10,16 @@ public static partial class ThreadPool { [CLSCompliant(false)] [SupportedOSPlatform("windows")] - private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) => + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(IntPtr osHandle) => + public static bool BindHandle(IntPtr osHandle) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(SafeHandle osHandle) => + public static bool BindHandle(SafeHandle osHandle) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } } From a76d74ce6293c78fdbc925dd9c167e6caab15844 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 May 2023 13:24:39 -0700 Subject: [PATCH 053/168] Allow NativeAOT to switch between windows and portable threadpoopl --- .../src/System.Private.CoreLib.csproj | 2 - .../System/Threading/ThreadPool.NativeAot.cs | 26 ----- .../System.Private.CoreLib.Shared.projitems | 11 +-- .../System/Threading/ThreadPool.Portable.cs | 45 --------- .../src/System/Threading/ThreadPool.cs | 43 -------- .../System/Threading/ThreadPoolWorkQueue.cs | 4 - .../Threading/Win32ThreadPoolBoundHandle.cs | 65 ------------ .../Win32ThreadPoolPreAllocatedOverlapped.cs | 49 --------- .../Win32ThreadPoolRegisteredWaitHandle.cs | 99 ------------------- 9 files changed, 4 insertions(+), 340 deletions(-) delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 24ef96d41a532..574fcc23568d6 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -236,7 +236,6 @@ - @@ -294,7 +293,6 @@ - diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs deleted file mode 100644 index 2fe7d1dd2c5d6..0000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** -** -** Purpose: Class for creating and managing a threadpool -** -** -=============================================================================*/ - -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Threading -{ - public static partial class ThreadPool - { - [Conditional("unnecessary")] - internal static void ReportThreadStatus(bool isWorking) - { - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3636efcbbcf14..eb71fe7be28e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1196,6 +1196,7 @@ + @@ -1572,7 +1573,7 @@ Common\Interop\Windows\Kernel32\Interop.CancelIoEx.cs - + Common\Interop\Windows\Kernel32\Interop.CompletionPort.cs @@ -2060,6 +2061,7 @@ + @@ -2528,10 +2530,8 @@ - - + - @@ -2587,9 +2587,6 @@ - - - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index 306fae799a1e1..7016b31e71c48 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -10,11 +10,6 @@ namespace System.Threading // Portable implementation of ThreadPool // - internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem - { - void IThreadPoolWorkItem.Execute() => PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); - } - public static partial class ThreadPool { // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that @@ -33,9 +28,6 @@ private static bool EnsureConfigInitializedCore() { throw new NotImplementedException(); } - // Threadpool specific initialization of a new thread. Used by OS-provided threadpools. No-op for portable threadpool. - private static void InitializeForThreadPoolThreadPortableCore() { } - #pragma warning disable IDE0060 internal static bool CanSetMinIOCompletionThreads(int ioCompletionThreads) => false; internal static bool CanSetMaxIOCompletionThreads(int ioCompletionThreads) => false; @@ -45,42 +37,5 @@ private static void InitializeForThreadPoolThreadPortableCore() { } internal static void SetMinIOCompletionThreads(int ioCompletionThreads) { } [Conditional("unnecessary")] internal static void SetMaxIOCompletionThreads(int ioCompletionThreads) { } - - /// - /// Gets the number of thread pool threads that currently exist. - /// - /// - /// For a thread pool implementation that may have different types of threads, the count includes all types. - /// - public static int ThreadCount => PortableThreadPool.ThreadPoolInstance.ThreadCount; - - /// - /// Gets the number of work items that have been processed by the thread pool so far. - /// - /// - /// For a thread pool implementation that may have different types of work items, the count includes all types. - /// - public static long CompletedWorkItemCount => PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - object? state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(waitObject); - ArgumentNullException.ThrowIfNull(callBack); - - Thread.ThrowIfNoThreadStart(); - RegisteredWaitHandle registeredHandle = new RegisteredWaitHandle( - waitObject, - new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), - (int)millisecondsTimeOutInterval, - !executeOnlyOnce); - PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredHandle); - return registeredHandle; - } } } 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 07cb430fcecb3..1257cd573708d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -199,49 +199,6 @@ public static long CompletedWorkItemCount private static void InitializeForThreadPoolThreadPortableCore() { } - [SupportedOSPlatform("windows")] - private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlapped* overlapped) - { - if (overlapped == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.overlapped); - } - - // OS doesn't signal handle, so do it here - overlapped->InternalLow = IntPtr.Zero; - - PortableThreadPool.ThreadPoolInstance.QueueNativeOverlapped(overlapped); - return true; - } - - [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] - [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(IntPtr osHandle) - { - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); - return true; - } - - [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(SafeHandle osHandle) - { - ArgumentNullException.ThrowIfNull(osHandle); - - bool mustReleaseSafeHandle = false; - try - { - osHandle.DangerousAddRef(ref mustReleaseSafeHandle); - - PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle.DangerousGetHandle()); - return true; - } - finally - { - if (mustReleaseSafeHandle) - osHandle.DangerousRelease(); - } - } - private static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 1a43908ce3d4b..c41f7e37280d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1372,10 +1372,6 @@ public static partial class ThreadPool } }; -#if NATIVEAOT - private const bool IsWorkerTrackingEnabledInConfig = false; -#endif - internal static bool EnableWorkerTracking => IsWorkerTrackingEnabledInConfig && EventSource.IsSupported; #if !FEATURE_WASM_THREADS diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs deleted file mode 100644 index 239081651dd37..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolBoundHandle.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; -using System.IO; - -namespace System.Threading -{ - // - // Implementation of ThreadPoolBoundHandle that sits on top of the Win32 ThreadPool - // - public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable - { - private readonly SafeHandle _handle; - private readonly SafeThreadPoolIOHandle _threadPoolHandle; - private DeferredDisposableLifetime _lifetime; - - private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) - { - _threadPoolHandle = threadPoolHandle; - _handle = handle; - } - - public SafeHandle Handle - { - get { return _handle; } - } - - public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandleCore(handle); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlappedCore(callback, state, pinData); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeAllocateNativeOverlappedCore(callback, state, pinData); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => - AllocateNativeOverlappedCore(preAllocated); - - [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedCore(overlapped); - - [CLSCompliant(false)] - public static unsafe object GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStateCore(overlapped); - - public void Dispose() => DisposeCore(); - - ~ThreadPoolBoundHandle() - { - FinalizeCore(); - } - - void IDeferredDisposable.OnFinalRelease(bool disposed) - { - IDeferredDisposableOnFinalReleaseCore(disposed); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs deleted file mode 100644 index e9a7dc9dabbc3..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolPreAllocatedOverlapped.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Threading -{ - public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable - { - private DeferredDisposableLifetime _lifetime; - - [CLSCompliant(false)] - public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : - this(callback, state, pinData, flowExecutionContext: true) - { - } - - [CLSCompliant(false)] - public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeCreateCore(callback, state, pinData); - - private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) - { - ArgumentNullException.ThrowIfNull(callback); - - _overlappedCore = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); - } - - internal bool AddRef() => AddRefCore(); - - internal void Release() => ReleaseCore(); - - public void Dispose() - { - DisposeCore(); - } - - ~PreAllocatedOverlapped() - { - Dispose(); - } - - unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) - { - IDeferredDisposableOnFinalReleaseCore(disposed); - } - - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs deleted file mode 100644 index 32688f29dc2ee..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Win32ThreadPoolRegisteredWaitHandle.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Threading -{ - // - // Windows-specific implementation of ThreadPool - // - // PR-Comment: This implementation was previously in ThreadPool.Windows.cs -#if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] -#endif - public sealed partial class RegisteredWaitHandle : MarshalByRefObject - { - private readonly Lock _lock; - private SafeWaitHandle _waitHandle; - private readonly _ThreadPoolWaitOrTimerCallback _callbackHelper; - private readonly uint _millisecondsTimeout; - private bool _repeating; - private bool _unregistering; - - // Handle to this object to keep it alive - private GCHandle _gcHandle; - - // Pointer to the TP_WAIT structure - private IntPtr _tpWait; - - internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, - uint millisecondsTimeout, bool repeating) - { - _lock = new Lock(); - - waitHandle.DangerousAddRef(); - _waitHandle = waitHandle; - - _callbackHelper = callbackHelper; - _millisecondsTimeout = millisecondsTimeout; - _repeating = repeating; - - // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked - _gcHandle = GCHandle.Alloc(this); - - _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); - - if (_tpWait == IntPtr.Zero) - { - _gcHandle.Free(); - throw new OutOfMemoryException(); - } - } - - public bool Unregister(WaitHandle waitObject) => UnregisterCore(waitObject); - - ~RegisteredWaitHandle() - { - // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC - Debug.Assert(!_gcHandle.IsAllocated); - - // If this object gets resurrected and another thread calls Unregister, that creates a race condition. - // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. - // The _lock may be null in case of OOM in the constructor. - if ((_lock != null) && _lock.TryAcquire(0)) - { - try - { - if (!_unregistering) - { - _unregistering = true; - - if (_tpWait != IntPtr.Zero) - { - // There must be no in-flight callbacks; just dispose resources - Interop.Kernel32.CloseThreadpoolWait(_tpWait); - _tpWait = IntPtr.Zero; - } - - if (_waitHandle != null) - { - _waitHandle.DangerousRelease(); - _waitHandle = null; - } - } - } - finally - { - _lock.Release(); - } - } - } - } - -} From e98773ab69c308a625728dd95801bfa6467f0b20 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 May 2023 13:29:27 -0700 Subject: [PATCH 054/168] Remote ThreadPool.Windows.cs --- .../System/Threading/ThreadPool.Windows.cs | 93 ------------------- 1 file changed, 93 deletions(-) delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs deleted file mode 100644 index 8f8f60d4ffb04..0000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace System.Threading -{ - // - // Windows-specific implementation of ThreadPool - // - - public static partial class ThreadPool - { - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work. - // - // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be - // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread - // injection. - internal static bool YieldFromDispatchLoop => WindowsThreadPool.YieldFromDispatchLoop; - - internal static void InitializeForThreadPoolThread() => WindowsThreadPool.InitializeForThreadPoolThread(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); - - internal static object GetOrCreateThreadLocalCompletionCountObject() => WindowsThreadPool.GetOrCreateThreadLocalCompletionCountObject(); - - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMaxThreads(workerThreads, completionPortThreads); - - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); - - public static bool SetMinThreads(int workerThreads, int completionPortThreads) => WindowsThreadPool.SetMinThreads(workerThreads, completionPortThreads); - - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) => WindowsThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); - - /// - /// Gets the number of thread pool threads that currently exist. - /// - /// - /// For a thread pool implementation that may have different types of threads, the count includes all types. - /// - public static int ThreadCount => WindowsThreadPool.ThreadCount; - - /// - /// Gets the number of work items that have been processed so far. - /// - /// - /// For a thread pool implementation that may have different types of work items, the count includes all types. - /// - public static long CompletedWorkItemCount => WindowsThreadPool.CompletedWorkItemCount; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void NotifyWorkItemProgress() => WindowsThreadPool.NotifyWorkItemProgress(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int _ /*currentTimeMs*/) => WindowsThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, _); - - internal static bool NotifyThreadBlocked() => WindowsThreadPool.NotifyThreadBlocked(); - internal static void NotifyThreadUnblocked() => WindowsThreadPool.NotifyThreadUnblocked(); - - internal static unsafe void RequestWorkerThread() => WindowsThreadPool.RequestWorkerThread(); - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle waitObject, - WaitOrTimerCallback callBack, - object state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); - } - - [CLSCompliant(false)] - [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => WindowsThreadPool.UnsafeQueueNativeOverlapped(overlapped); - - [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] - [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) => WindowsThreadPool.BindHandle(osHandle); - - [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) => WindowsThreadPool.BindHandle(osHandle); - } -} From edc7a2fc188d36050dc90d2502941afb013e5569 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 5 May 2023 12:51:50 -0700 Subject: [PATCH 055/168] Fix clr on unix --- .../System.Private.CoreLib.Shared.projitems | 16 +-- .../CompleteWaitThreadPoolWorkItem.cs | 21 ++++ .../System/Threading/PortableThreadPool.cs | 22 +++++ .../PreAllocatedOverlapped.Portable.Core.cs | 2 +- .../Threading/PreAllocatedOverlapped.Unix.cs | 86 ++++++++++++++++ .../Threading/PreAllocatedOverlapped.cs | 2 + .../src/System/Threading/ThreadPool.Unix.cs | 98 +++++++++++++++++++ .../src/System/Threading/ThreadPool.cs | 24 +---- .../ThreadPoolBoundHandle.Portable.Core.cs | 10 -- .../Threading/ThreadPoolBoundHandle.Unix.cs | 2 +- .../ThreadPoolBoundHandle.Windows.cs | 10 ++ 11 files changed, 251 insertions(+), 42 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index eb71fe7be28e8..f8f9276731248 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -74,7 +74,6 @@ - @@ -1196,7 +1195,6 @@ - @@ -2020,6 +2018,7 @@ + @@ -2061,7 +2060,7 @@ - + @@ -2531,8 +2530,10 @@ + + - + @@ -2550,12 +2551,13 @@ - + + - + - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs new file mode 100644 index 0000000000000..c318f4b3e4d86 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System.Threading +{ + internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem + { + void IThreadPoolWorkItem.Execute() => CompleteWait(); + + // Entry point from unmanaged code + private void CompleteWait() + { + PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs index 7adfe5a0b9ca4..8c9a9a074690f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs @@ -457,5 +457,27 @@ private bool OnGen2GCCallback() _memoryUsageBytes = Math.Min(gcMemoryInfo.MemoryLoadBytes, gcMemoryInfo.HighMemoryLoadThresholdBytes); return true; // continue receiving gen 2 GC callbacks } + + internal static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + ArgumentNullException.ThrowIfNull(waitObject); + ArgumentNullException.ThrowIfNull(callBack); + + RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( + waitObject, + new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), + (int)millisecondsTimeOutInterval, + !executeOnlyOnce); + + PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); + + return registeredWaitHandle; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs index 2cff8b737a7ef..280604274b503 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -9,7 +9,7 @@ namespace System.Threading /// public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly ThreadPoolBoundHandleOverlapped? _overlappedPortableCore; + internal ThreadPoolBoundHandleOverlapped? _overlappedPortableCore; /// /// Initializes a new instance of the class, specifying diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs new file mode 100644 index 0000000000000..74fb5d2dec0db --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Threading +{ + /// + /// Represents pre-allocated state for native overlapped I/O operations. + /// + /// + public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable + { + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// + [CLSCompliant(false)] + public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) : + this(callback, state, pinData, flowExecutionContext: true) + { + } + + private DeferredDisposableLifetime _lifetime; + + [CLSCompliant(false)] + public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => + UnsafeCreatePortableCore(callback, state, pinData); + + private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + { + // This construction is duplicated in PreAllocatedOverlapped.cs + // It has to either be duplicated or remove the 'readonly' part of _overlappedPortableCore + ArgumentNullException.ThrowIfNull(callback); + + _overlappedPortableCore = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); + } + + internal bool AddRef() => AddRefPortableCore(); + + public void Dispose() + { + DisposePortableCore(); + } + + internal void Release() + { + ReleasePortableCore(); + } + + unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) + { + IDeferredDisposableOnFinalReleasePortableCore(disposed); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs index 1643ab3530523..4de1a330d5dde 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs @@ -67,6 +67,8 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta } else { + // This construction is duplicated in PreAllocatedOverlapped.Unix.cs + // It has to either be duplicated or remove the 'readonly' part of _overlappedPortableCore ArgumentNullException.ThrowIfNull(callback); _overlappedPortableCore = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 1d4e0c171496f..fc78a3cdd00b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -8,6 +9,74 @@ namespace System.Threading { public static partial class ThreadPool { + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); + + internal static object GetOrCreateThreadLocalCompletionCountObject() => + PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); + + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) => + PortableThreadPool.ThreadPoolInstance.SetMaxThreads(workerThreads, completionPortThreads); + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) + { + PortableThreadPool.ThreadPoolInstance.GetMaxThreads(out workerThreads, out completionPortThreads); + } + + public static bool SetMinThreads(int workerThreads, int completionPortThreads) => + PortableThreadPool.ThreadPoolInstance.SetMinThreads(workerThreads, completionPortThreads); + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) + { + PortableThreadPool.ThreadPoolInstance.GetMinThreads(out workerThreads, out completionPortThreads); + } + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) + { + PortableThreadPool.ThreadPoolInstance.GetAvailableThreads(out workerThreads, out completionPortThreads); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void NotifyWorkItemProgress() + { + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemProgress(); + } + + internal static bool NotifyThreadBlocked() => + PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked(); + + internal static void NotifyThreadUnblocked() + { + PortableThreadPool.ThreadPoolInstance.NotifyThreadUnblocked(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs) => + PortableThreadPool.ThreadPoolInstance.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTimeMs); + + /// + /// This method is called to request a new thread pool worker to handle pending work. + /// + internal static unsafe void RequestWorkerThread() + { + PortableThreadPool.ThreadPoolInstance.RequestWorker(); + } + + internal static void ReportThreadStatus(bool isWorking) + { + PortableThreadPool.ThreadPoolInstance.ReportThreadStatus(isWorking); + } + + internal static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle waitObject, + WaitOrTimerCallback callBack, + object? state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + return PortableThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + } + [CLSCompliant(false)] [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => @@ -21,5 +90,34 @@ public static bool BindHandle(IntPtr osHandle) => [SupportedOSPlatform("windows")] public static bool BindHandle(SafeHandle osHandle) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); + + /// + /// Gets the number of thread pool threads that currently exist. + /// + /// + /// For a thread pool implementation that may have different types of threads, the count includes all types. + /// + public static int ThreadCount + { + get + { + return PortableThreadPool.ThreadPoolInstance.ThreadCount; + } + } + + /// + /// Gets the number of work items that have been processed so far. + /// + /// + /// For a thread pool implementation that may have different types of work items, the count includes all types. + /// + public static long CompletedWorkItemCount + { + get + { + return PortableThreadPool.ThreadPoolInstance.CompletedWorkItemCount; + } + } + } } 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 1257cd573708d..7bfcdf93017f9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -8,17 +8,6 @@ namespace System.Threading { - internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem - { - void IThreadPoolWorkItem.Execute() => CompleteWait(); - - // Entry point from unmanaged code - private void CompleteWait() - { - PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); - } - } - public static partial class ThreadPool { internal static readonly bool UseWindowsThreadPool = @@ -213,18 +202,7 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( } else { - ArgumentNullException.ThrowIfNull(waitObject); - ArgumentNullException.ThrowIfNull(callBack); - - RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle( - waitObject, - new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext), - (int)millisecondsTimeOutInterval, - !executeOnlyOnce); - - PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle); - - return registeredWaitHandle; + return PortableThreadPool.ThreadPoolInstance.RegisterWaitForSingleObject(waitObject, callback, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs index 47268fa35971b..5ce4e29d2ce54 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs @@ -16,16 +16,6 @@ namespace System.Threading /// public sealed partial class ThreadPoolBoundHandle : IDisposable { - private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) - { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - return BindHandleWindowsCore(handle); - } - private unsafe NativeOverlapped* AllocateNativeOverlappedPortableCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlappedPortableCore(callback, state, pinData, flowExecutionContext: true); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs index 6f809959dbca7..4909856404a82 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs @@ -8,7 +8,7 @@ namespace System.Threading { public sealed partial class ThreadPoolBoundHandle { - private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { Debug.Assert(handle != null); Debug.Assert(!handle.IsClosed); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index 19fc2ed4ef01b..ee2fc974a1b3a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -8,6 +8,16 @@ namespace System.Threading { public sealed partial class ThreadPoolBoundHandle { + private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) + { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + return BindHandleWindowsCore(handle); + } + private static ThreadPoolBoundHandle BindHandleWindowsCore(SafeHandle handle) { Debug.Assert(handle != null); From 4672af9c3e947def4a70177a6710bfa5450bfd8c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 5 May 2023 13:07:54 -0700 Subject: [PATCH 056/168] Fix clr on windows --- .../System.Private.CoreLib/src/System/Threading/ThreadPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7bfcdf93017f9..b58711322dfd3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -202,7 +202,7 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( } else { - return PortableThreadPool.ThreadPoolInstance.RegisterWaitForSingleObject(waitObject, callback, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + return PortableThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); } } } From 90edcbecf1b90134fff6da4cd70f0d5ef1f2fa68 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 May 2023 09:36:00 -0700 Subject: [PATCH 057/168] RegisteredWaitHandle properties using corresponding fields --- .../System/Threading/RegisteredWaitHandle.cs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs index 90c22e0905b3c..dab8d42ce2f3d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs @@ -19,6 +19,7 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject private SafeWaitHandle? _waitHandle; private readonly _ThreadPoolWaitOrTimerCallback? _callbackHelper; private readonly uint _millisecondsTimeout; + private int _timeoutDurationMs; private bool _repeating; private bool _unregistering; @@ -35,7 +36,6 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO _lock = new object(); - // Protect the handle from closing while we are waiting on it (VSWhidbey 285642) waitHandle.DangerousAddRef(); _waitHandle = waitHandle; @@ -62,10 +62,10 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb GC.SuppressFinalize(this); Thread.ThrowIfNoThreadStart(); - Handle = waitHandle.SafeWaitHandle; - Callback = callbackHelper; - TimeoutDurationMs = millisecondsTimeout; - Repeating = repeating; + _waitHandle = waitHandle.SafeWaitHandle; + _callbackHelper = callbackHelper; + _timeoutDurationMs = millisecondsTimeout; + _repeating = repeating; if (!IsInfiniteTimeout) { RestartTimeout(); @@ -79,26 +79,38 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb /// /// The callback to execute when the wait on either times out or completes. /// - internal _ThreadPoolWaitOrTimerCallback? Callback { get; } // PR-Comment: must be nullable as it has one constructor which exits without initializing + internal _ThreadPoolWaitOrTimerCallback? Callback + { + get => _callbackHelper; + } /// /// The that was registered. /// - internal SafeWaitHandle? Handle { get; } // PR-Comment: must be nullable as it has one constructor which exits without initializing + internal SafeWaitHandle? Handle + { + get => _waitHandle; + } /// /// The time this handle times out at in ms. /// internal int TimeoutTimeMs { get; private set; } - internal int TimeoutDurationMs { get; } + internal int TimeoutDurationMs + { + get => _timeoutDurationMs; + } internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; /// /// Whether or not the wait is a repeating wait. /// - internal bool Repeating { get; } + internal bool Repeating + { + get => _repeating; + } /// /// The the user passed in via . From 328786bffc1fc7a52af51fa245ceaa4f63610411 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 May 2023 09:48:32 -0700 Subject: [PATCH 058/168] Nit in RegisteredWaitHandle.cs --- .../src/System/Threading/RegisteredWaitHandle.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs index dab8d42ce2f3d..7d55a58036ddc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs @@ -19,7 +19,7 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject private SafeWaitHandle? _waitHandle; private readonly _ThreadPoolWaitOrTimerCallback? _callbackHelper; private readonly uint _millisecondsTimeout; - private int _timeoutDurationMs; + private readonly int _signedMillisecondsTimeout; private bool _repeating; private bool _unregistering; @@ -64,7 +64,7 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb Thread.ThrowIfNoThreadStart(); _waitHandle = waitHandle.SafeWaitHandle; _callbackHelper = callbackHelper; - _timeoutDurationMs = millisecondsTimeout; + _signedMillisecondsTimeout = millisecondsTimeout; _repeating = repeating; if (!IsInfiniteTimeout) { @@ -99,7 +99,7 @@ internal SafeWaitHandle? Handle internal int TimeoutDurationMs { - get => _timeoutDurationMs; + get => _signedMillisecondsTimeout; } internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; From 17538af789dead9bb044d57f1ae58287fb4f7af3 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 May 2023 18:19:32 -0700 Subject: [PATCH 059/168] Make UseWindowsThreadPool a property --- .../src/System/Threading/ThreadPool.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 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 b58711322dfd3..a92dbcec6bef7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -10,9 +10,10 @@ namespace System.Threading { public static partial class ThreadPool { - internal static readonly bool UseWindowsThreadPool = - Environment.GetEnvironmentVariable("DOTNET_ThreadPool_UseWindowsThreadPool") == "1" || - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", false); + internal static bool UseWindowsThreadPool + { + get => AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); + } [CLSCompliant(false)] [SupportedOSPlatform("windows")] From a893396ccf78fc20014fadca3c802e86969f8ad7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 May 2023 20:43:40 -0700 Subject: [PATCH 060/168] Add partial modifiers --- .../src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs | 2 +- .../src/System/Threading/ThreadPool.Browser.Mono.cs | 2 +- .../src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs index caad4d140aa40..f82b3fa966479 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs @@ -3,7 +3,7 @@ namespace System.Threading { - public sealed class PreAllocatedOverlapped : System.IDisposable + public sealed partial class PreAllocatedOverlapped : System.IDisposable { [CLSCompliantAttribute(false)] public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) { } diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index a5e54288a2920..492083928215e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -16,7 +16,7 @@ namespace System.Threading #if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] #endif - public sealed class RegisteredWaitHandle : MarshalByRefObject + public sealed partial class RegisteredWaitHandle : MarshalByRefObject { internal RegisteredWaitHandle() { diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs index 8e2f33e002f6b..4fe3fddbe6fe3 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs @@ -5,7 +5,7 @@ namespace System.Threading { - public sealed class ThreadPoolBoundHandle : IDisposable + public sealed partial class ThreadPoolBoundHandle : IDisposable { public SafeHandle Handle => null!; From cf13b1397ece42f19d07f57d1bb7b46e92cd3402 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 May 2023 21:08:47 -0700 Subject: [PATCH 061/168] Update UseWindowsThreadPool property + Trimming tests template --- .../src/System/Threading/ThreadPool.cs | 6 ++---- .../System.Runtime.TrimmingTests.proj | 6 ++++++ .../TrimmingTests/UseWindowsThreadPoolFalse.cs | 18 ++++++++++++++++++ .../TrimmingTests/UseWindowsThreadPoolTrue.cs | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs create mode 100644 src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs 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 a92dbcec6bef7..80e4cf3416140 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -10,10 +10,8 @@ namespace System.Threading { public static partial class ThreadPool { - internal static bool UseWindowsThreadPool - { - get => AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); - } + internal static bool UseWindowsThreadPool { get; } = + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); [CLSCompliant(false)] [SupportedOSPlatform("windows")] diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj index c29e8c3e65895..081a8b5a17d3e 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -45,6 +45,12 @@ System.Reflection.NullabilityInfoContext.IsSupported + + System.Threading.ThreadPool.UseWindowsThreadPool + + + System.Threading.ThreadPool.UseWindowsThreadPool + diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs new file mode 100644 index 0000000000000..eb1843f67800c --- /dev/null +++ b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Globalization; +using System.Threading; + +/// +/// Ensures setting UseWindowsThreadPool = false still works in a trimmed app. +/// +class Program +{ + static int Main(string[] args) + { + + return 100; + } +} \ No newline at end of file diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs new file mode 100644 index 0000000000000..f2e4b6e558fa5 --- /dev/null +++ b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Globalization; +using System.Threading; + +/// +/// Ensures setting UseWindowsThreadPool = true still works in a trimmed app. +/// +class Program +{ + static int Main(string[] args) + { + + return 100; + } +} \ No newline at end of file From f0c0f8d63a6c7038c52a92e2c1c963d032abfa58 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 May 2023 21:09:50 -0700 Subject: [PATCH 062/168] Nit in System.Runtime.TrimmingTests.proj --- .../tests/TrimmingTests/System.Runtime.TrimmingTests.proj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj index 081a8b5a17d3e..a1ef5c3f473c9 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -45,10 +45,10 @@ System.Reflection.NullabilityInfoContext.IsSupported - + System.Threading.ThreadPool.UseWindowsThreadPool - + System.Threading.ThreadPool.UseWindowsThreadPool From 0814be850e6f9489be3ad3d6356cc40d9316c315 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 9 May 2023 22:56:49 -0700 Subject: [PATCH 063/168] Fix ReportThreadStatus not found on wasm --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../src/System/Threading/ThreadPool.Browser.Mono.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f8f9276731248..d03cba80eed21 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2529,7 +2529,7 @@ - + diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index 492083928215e..654c341b3d323 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -150,5 +150,8 @@ public static bool BindHandle(SafeHandle osHandle) { throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } + + [Conditional("unnecessary")] + internal static void ReportThreadStatus(bool isWorking) => ReportThreadStatusCore(isWorking); } } From a7e65287005b571dd40643fcb2a1f60dc2f6e807 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Tue, 9 May 2023 22:57:25 -0700 Subject: [PATCH 064/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs Co-authored-by: Jan Kotas --- .../System/Threading/CompleteWaitThreadPoolWorkItem.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs index c318f4b3e4d86..cc08d82e63406 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CompleteWaitThreadPoolWorkItem.cs @@ -10,12 +10,6 @@ namespace System.Threading { internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem { - void IThreadPoolWorkItem.Execute() => CompleteWait(); - - // Entry point from unmanaged code - private void CompleteWait() - { - PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); - } + void IThreadPoolWorkItem.Execute() => PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut); } } From 2a93ef4cd1f66578d03470f84d34622aed6725cf Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 10 May 2023 14:42:54 -0700 Subject: [PATCH 065/168] Fix browser/mono/wasm --- .../ThreadPoolBoundHandle.Browser.Threads.Mono.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs index 6d06335953dca..e75f25a524598 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs @@ -8,8 +8,13 @@ namespace System.Threading public sealed partial class ThreadPoolBoundHandle : IDisposable { #pragma warning disable IDE0060 - private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } #pragma warning restore IDE0060 From 2502f201b0f2460050eefbf8858332982f3123fb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 May 2023 15:59:02 -0700 Subject: [PATCH 066/168] Temporarily removing changes in ILLink.Substitutions.Shared.xml --- .../src/ILLink/ILLink.Substitutions.Shared.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index ea7cd9fa94be6..9765f3ab4f662 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -37,9 +37,5 @@ - - - - From 4673d2b3b7e2774dcf86795106d3d75e050e4ae0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 May 2023 17:50:03 -0700 Subject: [PATCH 067/168] Safe check in BindHandle --- .../src/System/Threading/ThreadPoolBoundHandle.Unix.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs index 4909856404a82..4e5ce874d5cd4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs @@ -10,6 +10,11 @@ public sealed partial class ThreadPoolBoundHandle { private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + Debug.Assert(handle != null); Debug.Assert(!handle.IsClosed); Debug.Assert(!handle.IsInvalid); From 0c86ba4f51a977d47e26a494fb361c2613ee0e79 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 May 2023 21:18:47 -0700 Subject: [PATCH 068/168] Update ILLink.Substitutions.Shared.xml --- .../src/ILLink/ILLink.Substitutions.Shared.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index 9765f3ab4f662..ea7cd9fa94be6 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -37,5 +37,9 @@ + + + + From c2c017461bc77a5a85c483d50116e8f908135b13 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 11 May 2023 15:12:48 -0700 Subject: [PATCH 069/168] Set Trimming for Windows only --- .../src/ILLink/ILLink.Substitutions.Windows.xml | 8 ++++++++ .../src/System.Private.CoreLib.Shared.projitems | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml new file mode 100644 index 0000000000000..29c26957ff6ee --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d03cba80eed21..6d72159025b1c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -57,6 +57,7 @@ + From f8539d35d704295c58494fd5351b81e7d353f1db Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 11 May 2023 15:16:53 -0700 Subject: [PATCH 070/168] Remove switch from ILLink.Substitutions.Shared.xml --- .../src/ILLink/ILLink.Substitutions.Shared.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index ea7cd9fa94be6..9765f3ab4f662 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -37,9 +37,5 @@ - - - - From a1b785e9dbb23328d9cfecb30d8730334c432cfa Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 May 2023 10:05:40 -0700 Subject: [PATCH 071/168] Add Trimming tests --- .../TrimmingTests/UseWindowsThreadPoolFalse.cs | 13 ++++++++++++- .../tests/TrimmingTests/UseWindowsThreadPoolTrue.cs | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs index eb1843f67800c..b6f89fc16b554 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs +++ b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolFalse.cs @@ -12,7 +12,18 @@ class Program { static int Main(string[] args) { - + // SetMinThreads should work when using PortableThreadPool, this call should return true + if (!ThreadPool.SetMinThreads(1, 1)) + { + return -1; + } + + // SetMaxThreads should work when using PortableThreadPool, this call should return true + if (!ThreadPool.SetMaxThreads(10, 10)) + { + return -1; + } + return 100; } } \ No newline at end of file diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs index f2e4b6e558fa5..f0aeb5632fa71 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs +++ b/src/libraries/System.Runtime/tests/TrimmingTests/UseWindowsThreadPoolTrue.cs @@ -12,7 +12,18 @@ class Program { static int Main(string[] args) { - + // SetMinThreads is not supported in WindowsThreadPool class, this call should return false + if (ThreadPool.SetMinThreads(1, 1)) + { + return -1; + } + + // SetMaxThreads is not supported in WindowsThreadPool class, this call should return false + if (ThreadPool.SetMaxThreads(10, 10)) + { + return -1; + } + return 100; } } \ No newline at end of file From f1808b71ea7e8decee68fc63049f247232f5be1c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 May 2023 10:53:04 -0700 Subject: [PATCH 072/168] Update System.Runtime.TrimmingTests.proj - run tests on Windows only --- .../tests/TrimmingTests/System.Runtime.TrimmingTests.proj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj index a1ef5c3f473c9..c885b8db6fc10 100644 --- a/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -45,6 +45,8 @@ System.Reflection.NullabilityInfoContext.IsSupported + + System.Threading.ThreadPool.UseWindowsThreadPool From fba93f3fb2bd7b7375bc58e95021bf33b4178025 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 12 May 2023 16:09:57 -0700 Subject: [PATCH 073/168] Setting default value of UseWindowsThreadPool to true. Just for testing --- .../src/System/Threading/PortableThreadPool.WaitThread.cs | 4 ++-- .../src/System/Threading/PreAllocatedOverlapped.Portable.cs | 0 .../System.Private.CoreLib/src/System/Threading/ThreadPool.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs index af20c2c2d4ae4..6cab05dd093db 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs @@ -347,7 +347,7 @@ private int ProcessRemovals() _numUserWaits = numUserWaits - 1; _pendingRemoves[i] = null; - waitHandleToRemove.Handle!.DangerousRelease(); // PR-Comment: Make Handle non-nullable if all instances of .Handle are used without checking for null + waitHandleToRemove.Handle!.DangerousRelease(); } _numPendingRemoves = 0; @@ -401,7 +401,7 @@ public bool RegisterWaitHandle(RegisteredWaitHandle handle) } bool success = false; - handle.Handle!.DangerousAddRef(ref success); // PR-Comment: Make Handle non-nullable if all instances of .Handle are used without checking for null + handle.Handle!.DangerousAddRef(ref success); Debug.Assert(success); _registeredWaits[_numUserWaits] = handle; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs deleted file mode 100644 index e69de29bb2d1d..0000000000000 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 80e4cf3416140..2f3161bea9f62 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -11,7 +11,7 @@ namespace System.Threading public static partial class ThreadPool { internal static bool UseWindowsThreadPool { get; } = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool", true); [CLSCompliant(false)] [SupportedOSPlatform("windows")] From 352cb739dded12394348965b0432cb9dced582c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 May 2023 15:36:34 -0700 Subject: [PATCH 074/168] Enable ThreadPoolCallbackWrapper in NativeAOT --- .../src/System.Private.CoreLib.csproj | 2 +- .../RegisteredWaitHandle.WindowsThreadPool.cs | 10 +++++++--- .../ThreadPoolBoundHandle.WindowsThreadPool.cs | 10 +++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 574fcc23568d6..9380efcdc7333 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -292,7 +292,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 5fe9a32f9683e..cb88a536c9ea2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -19,8 +19,10 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject [UnmanagedCallersOnly] internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { - // Commenting this one might be wrong, not sure yet - // var wrapper = ThreadPoolCallbackWrapper.Enter(); + // Enabling for NativeAot first +#if NATIVEAOT + var wrapper = ThreadPoolCallbackWrapper.Enter(); +#endif GCHandle handle = (GCHandle)context; RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); @@ -28,7 +30,9 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); registeredWaitHandle.PerformCallbackCore(timedOut); ThreadPool.IncrementCompletedWorkItemCount(); - // wrapper.Exit(); +#if NATIVEAOT + wrapper.Exit(); +#endif } #pragma warning restore IDE0060 diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index fc70430d955f6..18a5f491b71ff 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -144,8 +144,10 @@ private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlapp [UnmanagedCallersOnly] private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, nuint numberOfBytesTransferred, IntPtr ioPtr) { - // PR-Comment: Assuming it's not necessary, might be wrong about this - // var wrapper = ThreadPoolCallbackWrapper.Enter(); + // Enabling for NativeAot first +#if NATIVEAOT + var wrapper = ThreadPoolCallbackWrapper.Enter(); +#endif Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; ThreadPoolBoundHandle? boundHandle = overlapped->Data._boundHandle; @@ -156,7 +158,9 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); ThreadPool.IncrementCompletedWorkItemCount(); - // wrapper.Exit(); +#if NATIVEAOT + wrapper.Exit(); +#endif } private bool AddRef() From 64b99618c132a115cc60da661d124d21a017e1fc Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 12:13:50 -0700 Subject: [PATCH 075/168] Restore ManagedThreadId.cs --- .../src/System/Threading/ManagedThreadId.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs index 377ae3d2476c6..38672d2348e5a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ManagedThreadId.cs @@ -155,7 +155,7 @@ public ImmutableIdDispenser AllocateId(out int id) int childSize = ChildSize; if (id < childSize) { - left = left!.RecycleId(id); + left = left.RecycleId(id); } else { @@ -167,7 +167,7 @@ public ImmutableIdDispenser AllocateId(out int id) } else { - right = right!.RecycleId(id - BitsPerNode); + right = right.RecycleId(id - BitsPerNode); } } return new ImmutableIdDispenser(left, right, _used - 1, _size, bitmap); @@ -185,7 +185,7 @@ public ImmutableIdDispenser AllocateId(out int id) // when a Thread object cannot be created yet. We also store it in the Thread.CurrentThread object, // because that object may have longer lifetime than the OS thread. [ThreadStatic] - private static ManagedThreadId? t_currentThreadId; + private static ManagedThreadId t_currentThreadId; [ThreadStatic] private static int t_currentManagedThreadId; @@ -238,7 +238,7 @@ public static void RecycleId(int id) var priorIdDispenser = Volatile.Read(ref s_idDispenser); for (;;) { - var updatedIdDispenser = s_idDispenser!.RecycleId(id); + var updatedIdDispenser = s_idDispenser.RecycleId(id); var interlockedResult = Interlocked.CompareExchange(ref s_idDispenser, updatedIdDispenser, priorIdDispenser); if (object.ReferenceEquals(priorIdDispenser, interlockedResult)) break; @@ -265,7 +265,7 @@ public static ManagedThreadId GetCurrentThreadId() if (t_currentManagedThreadId == IdNone) MakeForCurrentThread(); - return t_currentThreadId!; + return t_currentThreadId; } private static int MakeForCurrentThread() From 3ea8ef1d5d889beadd85b163b389fab95f74379d Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 17 May 2023 12:16:11 -0700 Subject: [PATCH 076/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs Co-authored-by: Koundinya Veluri --- .../System.Private.CoreLib/src/System/Threading/ThreadPool.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 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 2f3161bea9f62..8eac638ab82bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @@ -197,11 +197,11 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject( { if (ThreadPool.UseWindowsThreadPool) { - return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + return WindowsThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); } else { - return PortableThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + return PortableThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); } } } From c182642f1f6ed5a147a2b050bfd8286ffa975e17 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 17 May 2023 12:16:22 -0700 Subject: [PATCH 077/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/WindowsThreadPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 256ec0cf3a70f..41053332d77fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -182,7 +182,7 @@ internal static unsafe void RequestWorkerThread() internal static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, - object state, + object? state, uint millisecondsTimeOutInterval, bool executeOnlyOnce, bool flowExecutionContext) From e29c0d08f4c812f8f3b8a6f4da1e8c0f212318f3 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 17 May 2023 12:17:10 -0700 Subject: [PATCH 078/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/ThreadPool.Unix.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index fc78a3cdd00b9..0cd2d2b425241 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -74,7 +74,8 @@ internal static RegisteredWaitHandle RegisterWaitForSingleObject( bool executeOnlyOnce, bool flowExecutionContext) { - return PortableThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state!, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); + Thread.ThrowIfNoThreadStart(); + return PortableThreadPool.RegisterWaitForSingleObject(waitObject, callBack, state, millisecondsTimeOutInterval, executeOnlyOnce, flowExecutionContext); } [CLSCompliant(false)] From 4fb0a4aa8cef5b02af9eb23f591f0d58709da645 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 17 May 2023 12:17:18 -0700 Subject: [PATCH 079/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/PortableThreadPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs index 8c9a9a074690f..9ada201e134c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs @@ -461,7 +461,7 @@ private bool OnGen2GCCallback() internal static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, - object state, + object? state, uint millisecondsTimeOutInterval, bool executeOnlyOnce, bool flowExecutionContext) From 65428f208e83bacee53f721d7885a1e435590b7c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 12:28:28 -0700 Subject: [PATCH 080/168] Rename ThreadPool.cs to ThreadPool.Windows.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../System/Threading/{ThreadPool.cs => ThreadPool.Windows.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{ThreadPool.cs => ThreadPool.Windows.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 6d72159025b1c..4055662268942 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2061,7 +2061,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs From f4ee52f748f2fe82533889e1425c8f6e19306594 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 17 May 2023 12:49:27 -0700 Subject: [PATCH 081/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/ThreadPool.Portable.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index 7016b31e71c48..67f09c4a28ecf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -24,9 +24,7 @@ public static partial class ThreadPool #endif - private static bool EnsureConfigInitializedCore() { - throw new NotImplementedException(); - } + private static bool EnsureConfigInitializedCore() => true; #pragma warning disable IDE0060 internal static bool CanSetMinIOCompletionThreads(int ioCompletionThreads) => false; From 569798b848301aa7d457ea479fbbb6fd6e70ff24 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 14:33:23 -0700 Subject: [PATCH 082/168] Restore condition for PortableThreadPool --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../src/System/Threading/ThreadPool.Windows.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 4055662268942..f95d00fcb0158 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2530,7 +2530,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 8eac638ab82bc..c44d22e102d3d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -11,7 +11,7 @@ namespace System.Threading public static partial class ThreadPool { internal static bool UseWindowsThreadPool { get; } = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool", true); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); [CLSCompliant(false)] [SupportedOSPlatform("windows")] From b0e71720502efb953556bb1f8e173240ee2263ee Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 16:13:45 -0700 Subject: [PATCH 083/168] Make Handle property in RegisteredWaitHandle non-nullable. Set FeaturePortableThreadPool=true for NativeAOT --- .../src/System.Private.CoreLib.csproj | 3 +-- .../System/Threading/PortableThreadPool.WaitThread.cs | 4 ++-- .../src/System/Threading/RegisteredWaitHandle.cs | 9 +++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 9380efcdc7333..e79741bf05a37 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -26,8 +26,7 @@ - false - true + true false diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs index 6cab05dd093db..34f7b3e1f54bf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs @@ -347,7 +347,7 @@ private int ProcessRemovals() _numUserWaits = numUserWaits - 1; _pendingRemoves[i] = null; - waitHandleToRemove.Handle!.DangerousRelease(); + waitHandleToRemove.Handle.DangerousRelease(); } _numPendingRemoves = 0; @@ -401,7 +401,7 @@ public bool RegisterWaitHandle(RegisteredWaitHandle handle) } bool success = false; - handle.Handle!.DangerousAddRef(ref success); + handle.Handle.DangerousAddRef(ref success); Debug.Assert(success); _registeredWaits[_numUserWaits] = handle; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs index 7d55a58036ddc..8f077a8f7ff9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs @@ -87,9 +87,14 @@ internal _ThreadPoolWaitOrTimerCallback? Callback /// /// The that was registered. /// - internal SafeWaitHandle? Handle + internal SafeWaitHandle Handle { - get => _waitHandle; + // get => _waitHandle; + get + { + Debug.Assert(_waitHandle != null); + return _waitHandle; + } } /// From 76e0bcb52cfa8056b1c59c4cf2ea9cdb226ebd0f Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 16:23:02 -0700 Subject: [PATCH 084/168] Rename PreAllocatedOverlapped.cs to PreAllocatedOverlapped.Windows.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../src/System/Threading/PreAllocatedOverlapped.Unix.cs | 2 +- ...AllocatedOverlapped.cs => PreAllocatedOverlapped.Windows.cs} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{PreAllocatedOverlapped.cs => PreAllocatedOverlapped.Windows.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f95d00fcb0158..fb15e087d4935 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2552,7 +2552,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs index 74fb5d2dec0db..f62ea0dd88951 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs @@ -59,7 +59,7 @@ public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { - // This construction is duplicated in PreAllocatedOverlapped.cs + // This construction is duplicated in PreAllocatedOverlapped.Windows.cs // It has to either be duplicated or remove the 'readonly' part of _overlappedPortableCore ArgumentNullException.ThrowIfNull(callback); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs From 9a33eeb71ab4e8df2e311c24adc39b89f868a523 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 17:50:25 -0700 Subject: [PATCH 085/168] Adding xml to PreAllocatedOverlapped::UnsafeCreate --- .../PreAllocatedOverlapped.Windows.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs index 4de1a330d5dde..7f6604ae99331 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs @@ -53,6 +53,41 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje { } + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation + /// of the callback. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// [CLSCompliant(false)] public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => ThreadPool.UseWindowsThreadPool ? UnsafeCreateCore(callback, state, pinData) : UnsafeCreatePortableCore(callback, state, pinData); From bb8c78d746b928778a88c703996ff99a9c769858 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 18:04:12 -0700 Subject: [PATCH 086/168] Remove xml from PreAllocatedOverlapped::UnsafeCreatePortableCore --- .../PreAllocatedOverlapped.Portable.Core.cs | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs index 280604274b503..8d6e4f481e56b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs @@ -11,41 +11,6 @@ public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDispo { internal ThreadPoolBoundHandleOverlapped? _overlappedPortableCore; - /// - /// Initializes a new instance of the class, specifying - /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when each asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes instance produced from this - /// object from other instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operations. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// The new instance can be passed to - /// , to produce - /// a instance that can be passed to the operating system in overlapped - /// I/O operations. A single instance can only be used for - /// a single native I/O operation at a time. However, the state stored in the - /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation - /// of the callback. - /// - /// The buffers specified in are pinned until is called. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// private static PreAllocatedOverlapped UnsafeCreatePortableCore(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); From e0744101734a7b8d7a56c734d24af8c0af811dcb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 18:12:58 -0700 Subject: [PATCH 087/168] Sort usings --- .../src/System/Threading/ThreadPoolBoundHandle.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs index e30b5050abe98..72f0504fc5b5c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs @@ -1,12 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Win32.SafeHandles; using System.Diagnostics; +using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; -using System.IO; +using Microsoft.Win32.SafeHandles; namespace System.Threading { From 4d1c2dd388ab9075cb73127ef7353267289d853b Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 17 May 2023 18:15:10 -0700 Subject: [PATCH 088/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/ThreadPoolWorkQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index c41f7e37280d0..3fd6b04d54cb4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1042,7 +1042,7 @@ public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) workStealingQueue = new ThreadPoolWorkQueue.WorkStealingQueue(); ThreadPoolWorkQueue.WorkStealingQueueList.Add(workStealingQueue); currentThread = Thread.CurrentThread; - threadLocalCompletionCountObject = ThreadPool.GetOrCreateThreadLocalCompletionCountObject()!; + threadLocalCompletionCountObject = ThreadPool.GetOrCreateThreadLocalCompletionCountObject(); } public void TransferLocalWork() From 86087bc3713ebafbab8e221d6229e767bdbdfca1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 18:45:56 -0700 Subject: [PATCH 089/168] Renaming: *Core to *WindowsThreadPool --- .../PreAllocatedOverlapped.Windows.cs | 12 ++++---- .../RegisteredWaitHandle.WindowsThreadPool.cs | 6 ++-- .../System/Threading/RegisteredWaitHandle.cs | 4 +-- ...ThreadPoolBoundHandle.WindowsThreadPool.cs | 28 +++++++++---------- .../System/Threading/ThreadPoolBoundHandle.cs | 18 ++++++------ ...reAllocatedOverlapped.WindowsThreadPool.cs | 20 ++++++------- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs index 7f6604ae99331..1bc5d212245e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs @@ -90,7 +90,7 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje /// [CLSCompliant(false)] public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => - ThreadPool.UseWindowsThreadPool ? UnsafeCreateCore(callback, state, pinData) : UnsafeCreatePortableCore(callback, state, pinData); + ThreadPool.UseWindowsThreadPool ? UnsafeCreateWindowsThreadPool(callback, state, pinData) : UnsafeCreatePortableCore(callback, state, pinData); private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { @@ -98,7 +98,7 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta { ArgumentNullException.ThrowIfNull(callback); - _overlappedCore = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); + _overlappedWindowsThreadPool = Win32ThreadPoolNativeOverlapped.Allocate(callback, state, pinData, this, flowExecutionContext); } else { @@ -110,13 +110,13 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta } } - internal bool AddRef() => ThreadPool.UseWindowsThreadPool ? AddRefCore() : AddRefPortableCore(); + internal bool AddRef() => ThreadPool.UseWindowsThreadPool ? AddRefWindowsThreadPool() : AddRefPortableCore(); internal void Release() { if (ThreadPool.UseWindowsThreadPool) { - ReleaseCore(); + ReleaseWindowsThreadPool(); } else { @@ -127,7 +127,7 @@ public void Dispose() { if (ThreadPool.UseWindowsThreadPool) { - DisposeCore(); + DisposeWindowsThreadPool(); } else { @@ -144,7 +144,7 @@ unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) { if (ThreadPool.UseWindowsThreadPool) { - IDeferredDisposableOnFinalReleaseCore(disposed); + IDeferredDisposableOnFinalReleaseWindowsThreadPool(disposed); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index cb88a536c9ea2..41810cd9df99b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -28,7 +28,7 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); - registeredWaitHandle.PerformCallbackCore(timedOut); + registeredWaitHandle.PerformCallbackWindowsThreadPool(timedOut); ThreadPool.IncrementCompletedWorkItemCount(); #if NATIVEAOT wrapper.Exit(); @@ -36,7 +36,7 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int } #pragma warning restore IDE0060 - private void PerformCallbackCore(bool timedOut) + private void PerformCallbackWindowsThreadPool(bool timedOut) { // New logic might be wrong here, not sure yet // If another thread is running Unregister, no need to restart the timer or clean up @@ -76,7 +76,7 @@ internal unsafe void RestartWait() Interop.Kernel32.SetThreadpoolWait(_tpWait, _waitHandle!.DangerousGetHandle(), (IntPtr)pTimeout); } - private bool UnregisterCore(WaitHandle waitObject) + private bool UnregisterWindowsThreadPool(WaitHandle waitObject) { // Hold the lock during the synchronous part of Unregister (as in CoreCLR) lock(_lock!) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs index 8f077a8f7ff9f..b518da7f47019 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs @@ -151,13 +151,13 @@ internal bool Repeating /// internal PortableThreadPool.WaitThread? WaitThread { get; set; } - public bool Unregister(WaitHandle waitObject) => ThreadPool.UseWindowsThreadPool ? UnregisterCore(waitObject) : UnregisterPortableCore(waitObject); + public bool Unregister(WaitHandle waitObject) => ThreadPool.UseWindowsThreadPool ? UnregisterWindowsThreadPool(waitObject) : UnregisterPortableCore(waitObject); internal void PerformCallback(bool timedOut) { if (ThreadPool.UseWindowsThreadPool) { - PerformCallbackCore(timedOut); + PerformCallbackWindowsThreadPool(timedOut); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index 18a5f491b71ff..d22ddcb94e4cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -15,7 +15,7 @@ namespace System.Threading // public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable { - private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + private static unsafe ThreadPoolBoundHandle BindHandleWindowsThreadPool(SafeHandle handle) { ArgumentNullException.ThrowIfNull(handle); @@ -38,13 +38,13 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) return new ThreadPoolBoundHandle(handle, threadPoolHandle); } - private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: true); + private unsafe NativeOverlapped* AllocateNativeOverlappedWindowsThreadPool(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlappedWindowsThreadPool(callback, state, pinData, flowExecutionContext: true); - private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlappedCore(callback, state, pinData, flowExecutionContext: false); + private unsafe NativeOverlapped* UnsafeAllocateNativeOverlappedWindowsThreadPool(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlappedWindowsThreadPool(callback, state, pinData, flowExecutionContext: false); - private unsafe NativeOverlapped* AllocateNativeOverlappedCore(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) + private unsafe NativeOverlapped* AllocateNativeOverlappedWindowsThreadPool(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext) { ArgumentNullException.ThrowIfNull(callback); @@ -65,7 +65,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) } } - private unsafe NativeOverlapped* AllocateNativeOverlappedCore(PreAllocatedOverlapped preAllocated) + private unsafe NativeOverlapped* AllocateNativeOverlappedWindowsThreadPool(PreAllocatedOverlapped preAllocated) { ArgumentNullException.ThrowIfNull(preAllocated); @@ -76,7 +76,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) addedRefToThis = AddRef(); addedRefToPreAllocated = preAllocated.AddRef(); - Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlappedCore->Data; + Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlappedWindowsThreadPool->Data; if (data._boundHandle != null) throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, nameof(preAllocated)); @@ -84,7 +84,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) Interop.Kernel32.StartThreadpoolIo(_threadPoolHandle!); - return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedCore); + return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlappedWindowsThreadPool); } catch { @@ -96,7 +96,7 @@ private static unsafe ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) } } - private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) + private unsafe void FreeNativeOverlappedWindowsThreadPool(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -118,7 +118,7 @@ private unsafe void FreeNativeOverlappedCore(NativeOverlapped* overlapped) Win32ThreadPoolNativeOverlapped.Free(threadPoolOverlapped); } - private static unsafe object? GetNativeOverlappedStateCore(NativeOverlapped* overlapped) + private static unsafe object? GetNativeOverlappedStateWindowsThreadPool(NativeOverlapped* overlapped) { ArgumentNullException.ThrowIfNull(overlapped); @@ -173,13 +173,13 @@ private void Release() _lifetime.Release(this); } - private void DisposeCore() + private void DisposeWindowsThreadPool() { _lifetime.Dispose(this); GC.SuppressFinalize(this); } - private void FinalizeCore() + private void FinalizeWindowsThreadPool() { // // During shutdown, don't automatically clean up, because this instance may still be @@ -189,7 +189,7 @@ private void FinalizeCore() Dispose(); } - private void IDeferredDisposableOnFinalReleaseCore(bool disposed) + private void IDeferredDisposableOnFinalReleaseWindowsThreadPool(bool disposed) { if (disposed) _threadPoolHandle!.Dispose(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs index 72f0504fc5b5c..d776bbc0af69b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs @@ -41,24 +41,24 @@ private ThreadPoolBoundHandle(SafeHandle handle) public SafeHandle Handle => _handle; public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => - ThreadPool.UseWindowsThreadPool ? BindHandleCore(handle) : BindHandlePortableCore(handle); + ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandlePortableCore(handle); [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => ThreadPool.UseWindowsThreadPool ? - AllocateNativeOverlappedCore(callback, state, pinData) : + AllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : AllocateNativeOverlappedPortableCore(callback, state, pinData); [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => ThreadPool.UseWindowsThreadPool ? - UnsafeAllocateNativeOverlappedCore(callback, state, pinData) : + UnsafeAllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => ThreadPool.UseWindowsThreadPool ? - AllocateNativeOverlappedCore(preAllocated) : + AllocateNativeOverlappedWindowsThreadPool(preAllocated) : AllocateNativeOverlappedPortableCore(preAllocated); [CLSCompliant(false)] @@ -66,7 +66,7 @@ public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) { if (ThreadPool.UseWindowsThreadPool) { - FreeNativeOverlappedCore(overlapped); + FreeNativeOverlappedWindowsThreadPool(overlapped); } else { @@ -77,14 +77,14 @@ public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) [CLSCompliant(false)] public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => ThreadPool.UseWindowsThreadPool ? - GetNativeOverlappedStateCore(overlapped) : + GetNativeOverlappedStateWindowsThreadPool(overlapped) : GetNativeOverlappedStatePortableCore(overlapped); public void Dispose() { if (ThreadPool.UseWindowsThreadPool) { - DisposeCore(); + DisposeWindowsThreadPool(); } else { @@ -96,7 +96,7 @@ public void Dispose() { if (ThreadPool.UseWindowsThreadPool) { - FinalizeCore(); + FinalizeWindowsThreadPool(); } } @@ -104,7 +104,7 @@ void IDeferredDisposable.OnFinalRelease(bool disposed) { if (ThreadPool.UseWindowsThreadPool) { - IDeferredDisposableOnFinalReleaseCore(disposed); + IDeferredDisposableOnFinalReleaseWindowsThreadPool(disposed); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs index da5b4d32d80ec..0ad038cf84a8a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs @@ -7,37 +7,37 @@ namespace System.Threading { public sealed partial class PreAllocatedOverlapped : IDisposable, IDeferredDisposable { - internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlappedCore; + internal readonly unsafe Win32ThreadPoolNativeOverlapped* _overlappedWindowsThreadPool; - private static PreAllocatedOverlapped UnsafeCreateCore(IOCompletionCallback callback, object? state, object? pinData) => + private static PreAllocatedOverlapped UnsafeCreateWindowsThreadPool(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false); - private bool AddRefCore() + private bool AddRefWindowsThreadPool() { return _lifetime.AddRef(); } - private void ReleaseCore() + private void ReleaseWindowsThreadPool() { _lifetime.Release(this); } - internal unsafe bool IsUserObject(byte[]? buffer) => _overlappedCore->IsUserObject(buffer); + internal unsafe bool IsUserObject(byte[]? buffer) => _overlappedWindowsThreadPool->IsUserObject(buffer); - private void DisposeCore() + private void DisposeWindowsThreadPool() { _lifetime.Dispose(this); GC.SuppressFinalize(this); } - private unsafe void IDeferredDisposableOnFinalReleaseCore(bool disposed) + private unsafe void IDeferredDisposableOnFinalReleaseWindowsThreadPool(bool disposed) { - if (_overlappedCore != null) + if (_overlappedWindowsThreadPool != null) { if (disposed) - Win32ThreadPoolNativeOverlapped.Free(_overlappedCore); + Win32ThreadPoolNativeOverlapped.Free(_overlappedWindowsThreadPool); else - *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlappedCore) = default(NativeOverlapped); + *Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(_overlappedWindowsThreadPool) = default(NativeOverlapped); } } } From cea582ba69b517f94b4e1b7c6604b091a3c10ab2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 19:00:37 -0700 Subject: [PATCH 090/168] Merging ThreadPoolBoundHandle.cs and ThreadPoolBoundHandle.Windows.cs --- .../System.Private.CoreLib.Shared.projitems | 1 - .../ThreadPoolBoundHandle.Windows.cs | 104 +++++++++++++++- .../System/Threading/ThreadPoolBoundHandle.cs | 111 ------------------ 3 files changed, 102 insertions(+), 114 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index fb15e087d4935..f2ce98ae42066 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2558,7 +2558,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index ee2fc974a1b3a..c51ebb5fa5cc4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -1,13 +1,45 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Win32.SafeHandles; namespace System.Threading { - public sealed partial class ThreadPoolBoundHandle + /// + /// Represents an I/O handle that is bound to the system thread pool and enables low-level + /// components to receive notifications for asynchronous I/O operations. + /// + public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable { + private readonly SafeHandle _handle; + private readonly SafeThreadPoolIOHandle? _threadPoolHandle; + private DeferredDisposableLifetime _lifetime; + private bool _isDisposed; + + private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) + { + _threadPoolHandle = threadPoolHandle; + _handle = handle; + } + + private ThreadPoolBoundHandle(SafeHandle handle) + { + _handle = handle; + } + + /// + /// Gets the bound operating system handle. + /// + /// + /// A object that holds the bound operating system handle. + /// + public SafeHandle Handle => _handle; + private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { ArgumentNullException.ThrowIfNull(handle); @@ -47,5 +79,73 @@ private static ThreadPoolBoundHandle BindHandleWindowsCore(SafeHandle handle) return new ThreadPoolBoundHandle(handle); } + + public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => + ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandlePortableCore(handle); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + ThreadPool.UseWindowsThreadPool ? + AllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : + AllocateNativeOverlappedPortableCore(callback, state, pinData); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + ThreadPool.UseWindowsThreadPool ? + UnsafeAllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : + UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); + + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => + ThreadPool.UseWindowsThreadPool ? + AllocateNativeOverlappedWindowsThreadPool(preAllocated) : + AllocateNativeOverlappedPortableCore(preAllocated); + + [CLSCompliant(false)] + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) + { + if (ThreadPool.UseWindowsThreadPool) + { + FreeNativeOverlappedWindowsThreadPool(overlapped); + } + else + { + FreeNativeOverlappedPortableCore(overlapped); + } + } + + [CLSCompliant(false)] + public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => + ThreadPool.UseWindowsThreadPool ? + GetNativeOverlappedStateWindowsThreadPool(overlapped) : + GetNativeOverlappedStatePortableCore(overlapped); + + public void Dispose() + { + if (ThreadPool.UseWindowsThreadPool) + { + DisposeWindowsThreadPool(); + } + else + { + DisposePortableCore(); + } + } + + ~ThreadPoolBoundHandle() + { + if (ThreadPool.UseWindowsThreadPool) + { + FinalizeWindowsThreadPool(); + } + } + + void IDeferredDisposable.OnFinalRelease(bool disposed) + { + if (ThreadPool.UseWindowsThreadPool) + { + IDeferredDisposableOnFinalReleaseWindowsThreadPool(disposed); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs deleted file mode 100644 index d776bbc0af69b..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; -using Microsoft.Win32.SafeHandles; - -namespace System.Threading -{ - /// - /// Represents an I/O handle that is bound to the system thread pool and enables low-level - /// components to receive notifications for asynchronous I/O operations. - /// - public sealed partial class ThreadPoolBoundHandle : IDisposable, IDeferredDisposable - { - private readonly SafeHandle _handle; - private readonly SafeThreadPoolIOHandle? _threadPoolHandle; - private DeferredDisposableLifetime _lifetime; - private bool _isDisposed; - - private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPoolHandle) - { - _threadPoolHandle = threadPoolHandle; - _handle = handle; - } - - private ThreadPoolBoundHandle(SafeHandle handle) - { - _handle = handle; - } - - /// - /// Gets the bound operating system handle. - /// - /// - /// A object that holds the bound operating system handle. - /// - public SafeHandle Handle => _handle; - - public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => - ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandlePortableCore(handle); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - ThreadPool.UseWindowsThreadPool ? - AllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : - AllocateNativeOverlappedPortableCore(callback, state, pinData); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - ThreadPool.UseWindowsThreadPool ? - UnsafeAllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : - UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); - - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => - ThreadPool.UseWindowsThreadPool ? - AllocateNativeOverlappedWindowsThreadPool(preAllocated) : - AllocateNativeOverlappedPortableCore(preAllocated); - - [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) - { - if (ThreadPool.UseWindowsThreadPool) - { - FreeNativeOverlappedWindowsThreadPool(overlapped); - } - else - { - FreeNativeOverlappedPortableCore(overlapped); - } - } - - [CLSCompliant(false)] - public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => - ThreadPool.UseWindowsThreadPool ? - GetNativeOverlappedStateWindowsThreadPool(overlapped) : - GetNativeOverlappedStatePortableCore(overlapped); - - public void Dispose() - { - if (ThreadPool.UseWindowsThreadPool) - { - DisposeWindowsThreadPool(); - } - else - { - DisposePortableCore(); - } - } - - ~ThreadPoolBoundHandle() - { - if (ThreadPool.UseWindowsThreadPool) - { - FinalizeWindowsThreadPool(); - } - } - - void IDeferredDisposable.OnFinalRelease(bool disposed) - { - if (ThreadPool.UseWindowsThreadPool) - { - IDeferredDisposableOnFinalReleaseWindowsThreadPool(disposed); - } - } - } -} From b97d1757813b53318234e90e7e2a12e377f1871e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 19:15:25 -0700 Subject: [PATCH 091/168] Renaming: RegisteredWaitHandle.cs to RegisteredWaitHandle.Windows.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- ...{RegisteredWaitHandle.cs => RegisteredWaitHandle.Windows.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{RegisteredWaitHandle.cs => RegisteredWaitHandle.Windows.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f2ce98ae42066..3f0cadcd9ade8 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2555,7 +2555,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs From eb2c9fd1c17a926a9c4d71ae66b9caac4433b0bc Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 May 2023 19:46:19 -0700 Subject: [PATCH 092/168] Make threadLocalCompletionCountObject in ThreadPoolWorkQueue.cs nullable --- .../src/System/Threading/ThreadPoolWorkQueue.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 3fd6b04d54cb4..90c4dc22a133f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -935,7 +935,7 @@ internal static bool Dispatch() // us to return the thread to the pool or not. // int currentTickCount = Environment.TickCount; - if (!ThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, currentTickCount)) + if (!ThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject!, currentTickCount)) { // This thread is being parked and may remain inactive for a while. Transfer any thread-local work items // to ensure that they would not be heavily delayed. Tell the caller that this thread was requested to stop @@ -1032,7 +1032,7 @@ internal sealed class ThreadPoolWorkQueueThreadLocals public readonly ThreadPoolWorkQueue workQueue; public readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue; public readonly Thread currentThread; - public readonly object threadLocalCompletionCountObject; + public readonly object? threadLocalCompletionCountObject; public readonly Random.XoshiroImpl random = new Random.XoshiroImpl(); public ThreadPoolWorkQueueThreadLocals(ThreadPoolWorkQueue tpq) From a40bb4e47a2d549d27ac756a55673d52adfb3621 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 18 May 2023 17:47:25 -0700 Subject: [PATCH 093/168] Testing: disable trimming + enable windows threadpool by default --- .../src/ILLink/ILLink.Substitutions.Windows.xml | 4 ++-- .../src/System/Threading/ThreadPool.Windows.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml index 29c26957ff6ee..f832f7f2899d6 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml @@ -1,8 +1,8 @@ - - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index c44d22e102d3d..8eac638ab82bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -11,7 +11,7 @@ namespace System.Threading public static partial class ThreadPool { internal static bool UseWindowsThreadPool { get; } = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool", true); [CLSCompliant(false)] [SupportedOSPlatform("windows")] From 988d53f28dc06c6dd0a166451b15284366fa4f3b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 12:02:51 -0700 Subject: [PATCH 094/168] Add wrapper to WindowsThreadPool::DispatchCallback --- .../src/System/Threading/WindowsThreadPool.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 41053332d77fc..9fd2d5681f471 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -155,13 +155,17 @@ internal static void NotifyThreadUnblocked() { } private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { // PR-Comment: Assuming this is no longer necessary, might be wrong about this - // var wrapper = ThreadPoolCallbackWrapper.Enter(); +#if NATIVEAOT + var wrapper = ThreadPoolCallbackWrapper.Enter(); +#endif Debug.Assert(s_work == work); Interlocked.Increment(ref s_workingThreadCounter.Count); ThreadPoolWorkQueue.Dispatch(); Interlocked.Decrement(ref s_workingThreadCounter.Count); // We reset the thread after executing each callback - // wrapper.Exit(resetThread: false); +#if NATIVEAOT + wrapper.Exit(resetThread: false); +#endif } internal static unsafe void RequestWorkerThread() From fcb97c621325e0ac213cdf16907a08128e9d4ee6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 12:21:22 -0700 Subject: [PATCH 095/168] Add xml to ThreadPoolBoundHandle::BindHandle --- .../ThreadPoolBoundHandle.Windows.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index c51ebb5fa5cc4..002d5f877f28b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -80,6 +80,45 @@ private static ThreadPoolBoundHandle BindHandleWindowsCore(SafeHandle handle) return new ThreadPoolBoundHandle(handle); } + /// + /// Returns a for the specific handle, + /// which is bound to the system thread pool. + /// + /// + /// A object that holds the operating system handle. The + /// handle must have been opened for overlapped I/O on the unmanaged side. + /// + /// + /// for , which + /// is bound to the system thread pool. + /// + /// + /// is . + /// + /// + /// has been disposed. + /// + /// -or- + /// + /// does not refer to a valid I/O handle. + /// + /// -or- + /// + /// refers to a handle that has not been opened + /// for overlapped I/O. + /// + /// -or- + /// + /// refers to a handle that has already been bound. + /// + /// + /// This method should be called once per handle. + /// + /// -or- + /// + /// does not take ownership of , + /// it remains the responsibility of the caller to call . + /// public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandlePortableCore(handle); From b9c462f4c7271cda932d192a9f4186f66c905b58 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 12:49:54 -0700 Subject: [PATCH 096/168] Merging ThreadPoolBoundHandle.Portable.cs with ThreadPoolBoundHandle.Unix.cs --- .../System.Private.CoreLib.Shared.projitems | 1 - .../System/Threading/ThreadPool.Windows.cs | 2 +- .../ThreadPoolBoundHandle.Portable.cs | 247 ------------------ .../Threading/ThreadPoolBoundHandle.Unix.cs | 238 ++++++++++++++++- ...eadPoolBoundHandle.Browser.Threads.Mono.cs | 5 - 5 files changed, 237 insertions(+), 256 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 756bce5a536c5..d69346c04a144 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2562,7 +2562,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 8eac638ab82bc..c44d22e102d3d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -11,7 +11,7 @@ namespace System.Threading public static partial class ThreadPool { internal static bool UseWindowsThreadPool { get; } = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool", true); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); [CLSCompliant(false)] [SupportedOSPlatform("windows")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs deleted file mode 100644 index ae33397f95aef..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs +++ /dev/null @@ -1,247 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - // - // Implementation of ThreadPoolBoundHandle that sits on top of the CLR's ThreadPool and Overlapped infrastructure - // - - /// - /// Represents an I/O handle that is bound to the system thread pool and enables low-level - /// components to receive notifications for asynchronous I/O operations. - /// - public sealed partial class ThreadPoolBoundHandle : IDisposable - { - private readonly SafeHandle _handle; - private bool _isDisposed; - - private ThreadPoolBoundHandle(SafeHandle handle) - { - _handle = handle; - } - - /// - /// Gets the bound operating system handle. - /// - /// - /// A object that holds the bound operating system handle. - /// - public SafeHandle Handle => _handle; - - /// - /// Returns a for the specific handle, - /// which is bound to the system thread pool. - /// - /// - /// A object that holds the operating system handle. The - /// handle must have been opened for overlapped I/O on the unmanaged side. - /// - /// - /// for , which - /// is bound to the system thread pool. - /// - /// - /// is . - /// - /// - /// has been disposed. - /// - /// -or- - /// - /// does not refer to a valid I/O handle. - /// - /// -or- - /// - /// refers to a handle that has not been opened - /// for overlapped I/O. - /// - /// -or- - /// - /// refers to a handle that has already been bound. - /// - /// - /// This method should be called once per handle. - /// - /// -or- - /// - /// does not take ownership of , - /// it remains the responsibility of the caller to call . - /// - public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) => BindHandlePortableCore(handle); - - /// - /// Returns an unmanaged pointer to a structure, specifying - /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when the asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes this from other - /// instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operation. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// An unmanaged pointer to a structure. - /// - /// - /// - /// The unmanaged pointer returned by this method can be passed to the operating system in - /// overlapped I/O operations. The structure is fixed in - /// physical memory until is called. - /// - /// - /// The buffer or buffers specified in must be the same as those passed - /// to the unmanaged operating system function that performs the asynchronous I/O. - /// - /// - /// The buffers specified in are pinned for the duration of - /// the I/O operation. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - AllocateNativeOverlappedPortableCore(callback, state, pinData); - - /// - /// Returns an unmanaged pointer to a structure, specifying - /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided - /// object providing context, and managed objects that serve as buffers. - /// - /// - /// An delegate that represents the callback method - /// invoked when the asynchronous I/O operation completes. - /// - /// - /// A user-provided object that distinguishes this from other - /// instances. Can be . - /// - /// - /// An object or array of objects representing the input or output buffer for the operation. Each - /// object represents a buffer, for example an array of bytes. Can be . - /// - /// - /// An unmanaged pointer to a structure. - /// - /// - /// - /// The unmanaged pointer returned by this method can be passed to the operating system in - /// overlapped I/O operations. The structure is fixed in - /// physical memory until is called. - /// - /// - /// The buffer or buffers specified in must be the same as those passed - /// to the unmanaged operating system function that performs the asynchronous I/O. - /// - /// - /// is not flowed to the invocation of the callback. - /// - /// - /// The buffers specified in are pinned for the duration of - /// the I/O operation. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// - [CLSCompliant(false)] - public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => - UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); - - /// - /// Returns an unmanaged pointer to a structure, using the callback, - /// state, and buffers associated with the specified object. - /// - /// - /// A object from which to create the NativeOverlapped pointer. - /// - /// - /// An unmanaged pointer to a structure. - /// - /// - /// - /// The unmanaged pointer returned by this method can be passed to the operating system in - /// overlapped I/O operations. The structure is fixed in - /// physical memory until is called. - /// - /// - /// - /// is . - /// - /// - /// is currently in use for another I/O operation. - /// - /// - /// This method was called after the was disposed, or - /// this method was called after was disposed. - /// - /// - [CLSCompliant(false)] - public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => AllocateNativeOverlappedPortableCore(preAllocated); - - /// - /// Frees the unmanaged memory associated with a structure - /// allocated by the method. - /// - /// - /// An unmanaged pointer to the structure to be freed. - /// - /// - /// - /// You must call the method exactly once - /// on every unmanaged pointer allocated using the - /// method. - /// If you do not call the method, you will - /// leak memory. If you call the method more - /// than once on the same unmanaged pointer, memory will be corrupted. - /// - /// - /// - /// is . - /// - /// - /// This method was called after the was disposed. - /// - [CLSCompliant(false)] - public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedPortableCore(overlapped); - - /// - /// Returns the user-provided object specified when the instance was - /// allocated using the . - /// - /// - /// An unmanaged pointer to the structure from which to return the - /// associated user-provided object. - /// - /// - /// A user-provided object that distinguishes this - /// from other instances, otherwise, if one was - /// not specified when the instance was allocated using . - /// - /// - /// is . - /// - [CLSCompliant(false)] - public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStatePortableCore(overlapped); - - public void Dispose() => DisposePortableCore(); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs index 4e5ce874d5cd4..db23fc0105667 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs @@ -6,9 +6,72 @@ namespace System.Threading { - public sealed partial class ThreadPoolBoundHandle + // + // Implementation of ThreadPoolBoundHandle that sits on top of the CLR's ThreadPool and Overlapped infrastructure + // + + /// + /// Represents an I/O handle that is bound to the system thread pool and enables low-level + /// components to receive notifications for asynchronous I/O operations. + /// + public sealed partial class ThreadPoolBoundHandle : IDisposable { - private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) + private readonly SafeHandle _handle; + private bool _isDisposed; + + private ThreadPoolBoundHandle(SafeHandle handle) + { + _handle = handle; + } + + /// + /// Gets the bound operating system handle. + /// + /// + /// A object that holds the bound operating system handle. + /// + public SafeHandle Handle => _handle; + + /// + /// Returns a for the specific handle, + /// which is bound to the system thread pool. + /// + /// + /// A object that holds the operating system handle. The + /// handle must have been opened for overlapped I/O on the unmanaged side. + /// + /// + /// for , which + /// is bound to the system thread pool. + /// + /// + /// is . + /// + /// + /// has been disposed. + /// + /// -or- + /// + /// does not refer to a valid I/O handle. + /// + /// -or- + /// + /// refers to a handle that has not been opened + /// for overlapped I/O. + /// + /// -or- + /// + /// refers to a handle that has already been bound. + /// + /// + /// This method should be called once per handle. + /// + /// -or- + /// + /// does not take ownership of , + /// it remains the responsibility of the caller to call . + /// + public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) { ArgumentNullException.ThrowIfNull(handle); @@ -21,5 +84,176 @@ private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } + + /// + /// Returns an unmanaged pointer to a structure, specifying + /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when the asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes this from other + /// instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operation. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// An unmanaged pointer to a structure. + /// + /// + /// + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The structure is fixed in + /// physical memory until is called. + /// + /// + /// The buffer or buffers specified in must be the same as those passed + /// to the unmanaged operating system function that performs the asynchronous I/O. + /// + /// + /// The buffers specified in are pinned for the duration of + /// the I/O operation. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + AllocateNativeOverlappedPortableCore(callback, state, pinData); + + /// + /// Returns an unmanaged pointer to a structure, specifying + /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when the asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes this from other + /// instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operation. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// An unmanaged pointer to a structure. + /// + /// + /// + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The structure is fixed in + /// physical memory until is called. + /// + /// + /// The buffer or buffers specified in must be the same as those passed + /// to the unmanaged operating system function that performs the asynchronous I/O. + /// + /// + /// is not flowed to the invocation of the callback. + /// + /// + /// The buffers specified in are pinned for the duration of + /// the I/O operation. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// + [CLSCompliant(false)] + public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => + UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); + + /// + /// Returns an unmanaged pointer to a structure, using the callback, + /// state, and buffers associated with the specified object. + /// + /// + /// A object from which to create the NativeOverlapped pointer. + /// + /// + /// An unmanaged pointer to a structure. + /// + /// + /// + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The structure is fixed in + /// physical memory until is called. + /// + /// + /// + /// is . + /// + /// + /// is currently in use for another I/O operation. + /// + /// + /// This method was called after the was disposed, or + /// this method was called after was disposed. + /// + /// + [CLSCompliant(false)] + public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => AllocateNativeOverlappedPortableCore(preAllocated); + + /// + /// Frees the unmanaged memory associated with a structure + /// allocated by the method. + /// + /// + /// An unmanaged pointer to the structure to be freed. + /// + /// + /// + /// You must call the method exactly once + /// on every unmanaged pointer allocated using the + /// method. + /// If you do not call the method, you will + /// leak memory. If you call the method more + /// than once on the same unmanaged pointer, memory will be corrupted. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// + [CLSCompliant(false)] + public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) => FreeNativeOverlappedPortableCore(overlapped); + + /// + /// Returns the user-provided object specified when the instance was + /// allocated using the . + /// + /// + /// An unmanaged pointer to the structure from which to return the + /// associated user-provided object. + /// + /// + /// A user-provided object that distinguishes this + /// from other instances, otherwise, if one was + /// not specified when the instance was allocated using . + /// + /// + /// is . + /// + [CLSCompliant(false)] + public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => GetNativeOverlappedStatePortableCore(overlapped); + + public void Dispose() => DisposePortableCore(); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs index e75f25a524598..027dcf67c0dd3 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs @@ -10,11 +10,6 @@ public sealed partial class ThreadPoolBoundHandle : IDisposable #pragma warning disable IDE0060 private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } #pragma warning restore IDE0060 From f66a503aacc662aa2d95496e8374cfc2eb7b4425 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 12:53:13 -0700 Subject: [PATCH 097/168] Renaming: ThreadPoolBoundHandle.Portable.Core.cs to ThreadPoolBoundHandle.Portable.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- ...andle.Portable.Core.cs => ThreadPoolBoundHandle.Portable.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{ThreadPoolBoundHandle.Portable.Core.cs => ThreadPoolBoundHandle.Portable.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d69346c04a144..a1309315f9817 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2562,7 +2562,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.Core.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs From a769503bf541e589ff0b13bc580adcd96d854ef9 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 12:55:26 -0700 Subject: [PATCH 098/168] Removing IsWorkerTrackingEnabledInConfig from WindowsThreadPool class --- .../src/System/Threading/WindowsThreadPool.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 9fd2d5681f471..0c48baa229846 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -16,8 +16,6 @@ namespace System.Threading // PR-Comment: Making it internal just to make it build internal static class WindowsThreadPool { - internal const bool IsWorkerTrackingEnabledInConfig = false; - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work. // From 16cdae421a3505ba42356276912431df57a386b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 13:25:34 -0700 Subject: [PATCH 099/168] Refactor IsWorkerTrackingEnabledInConfig --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 2 -- .../src/System/Threading/ThreadPool.Portable.cs | 8 -------- .../src/System/Threading/ThreadPool.Unix.cs | 9 +++++++++ .../src/System/Threading/ThreadPool.Windows.cs | 11 +++++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index c7f804344fcb5..4b35dc8d4fa38 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -32,8 +32,6 @@ private static bool EnsureConfigInitializedCore() // the runtime may use the thread for processing other work internal static bool YieldFromDispatchLoop => false; - private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking(); - private static unsafe bool InitializeConfig() { int configVariableIndex = 1; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index d7103d000bb3e..1fa3d66858706 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -18,14 +18,6 @@ public static partial class ThreadPool internal static bool YieldFromDispatchLoop => false; #endif -#if NATIVEAOT - private const bool IsWorkerTrackingEnabledInConfig = false; -#else - private static readonly bool IsWorkerTrackingEnabledInConfig = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); -#endif - - private static bool EnsureConfigInitializedCore() => true; #pragma warning disable IDE0060 diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 0cd2d2b425241..bef00ebdceec6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -9,6 +9,15 @@ namespace System.Threading { public static partial class ThreadPool { +#if CORECLR + private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking(); +#elif NATIVEAOT + private const bool IsWorkerTrackingEnabledInConfig = false; +#else + private static readonly bool IsWorkerTrackingEnabledInConfig = + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); +#endif + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); internal static object GetOrCreateThreadLocalCompletionCountObject() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index c44d22e102d3d..eb76e4f9c8f3a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -13,6 +13,17 @@ public static partial class ThreadPool internal static bool UseWindowsThreadPool { get; } = AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); +#if CORECLR + // consider using internal const bool + private static readonly bool IsWorkerTrackingEnabledInConfig = + UseWindowsThreadPool ? false : GetEnableWorkerTracking(); +#elif NATIVEAOT + private const bool IsWorkerTrackingEnabledInConfig = false; +#else + private static readonly bool IsWorkerTrackingEnabledInConfig = + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); +#endif + [CLSCompliant(false)] [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => From 09b3f2e2a9c21ca661b563551fbe34de259c9b35 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 19 May 2023 13:34:47 -0700 Subject: [PATCH 100/168] Refactor YieldFromDispatchLoop --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 4 ---- .../src/System/Threading/ThreadPool.Portable.cs | 6 ------ .../src/System/Threading/ThreadPool.Unix.cs | 12 +++++++++--- .../src/System/Threading/ThreadPool.Windows.cs | 12 ++++++++++++ .../src/System/Threading/WindowsThreadPool.cs | 8 -------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 4b35dc8d4fa38..4d0f5c4d6e747 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -28,10 +28,6 @@ private static bool EnsureConfigInitializedCore() private static readonly bool s_initialized = InitializeConfig(); - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work - internal static bool YieldFromDispatchLoop => false; - private static unsafe bool InitializeConfig() { int configVariableIndex = 1; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs index 1fa3d66858706..bad11c3cb9eaf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs @@ -12,12 +12,6 @@ namespace System.Threading public static partial class ThreadPool { - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work -#if !(TARGET_BROWSER && FEATURE_WASM_THREADS) - internal static bool YieldFromDispatchLoop => false; -#endif - private static bool EnsureConfigInitializedCore() => true; #pragma warning disable IDE0060 diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index bef00ebdceec6..8d00d91ad3eaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -13,11 +13,17 @@ public static partial class ThreadPool private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking(); #elif NATIVEAOT private const bool IsWorkerTrackingEnabledInConfig = false; -#else +#else private static readonly bool IsWorkerTrackingEnabledInConfig = AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); -#endif - +#endif + +#if CORECLR || !(TARGET_BROWSER && FEATURE_WASM_THREADS) + // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that + // the runtime may use the thread for processing other work. + internal static bool YieldFromDispatchLoop => false; +#elif + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); internal static object GetOrCreateThreadLocalCompletionCountObject() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index eb76e4f9c8f3a..341e93a4ee8d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -24,6 +24,18 @@ public static partial class ThreadPool AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); #endif +#if CORECLR + // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that + // the runtime may use the thread for processing other work. + // + // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be + // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread + // injection. + internal static bool YieldFromDispatchLoop => UseWindowsThreadPool ? true : false; +#elif !(TARGET_BROWSER && FEATURE_WASM_THREADS) + internal static bool YieldFromDispatchLoop => false; +#endif + [CLSCompliant(false)] [SupportedOSPlatform("windows")] public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 0c48baa229846..ad56b8ce709f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -16,14 +16,6 @@ namespace System.Threading // PR-Comment: Making it internal just to make it build internal static class WindowsThreadPool { - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work. - // - // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be - // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread - // injection. - internal static bool YieldFromDispatchLoop => true; - /// /// The maximum number of threads in the default thread pool on Windows 10 as computed by /// TppComputeDefaultMaxThreads(TppMaxGlobalPool). From 45787e4b2dedabcd2358d844d3575388760413a4 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 19 May 2023 14:07:34 -0700 Subject: [PATCH 101/168] Fix ThreadPool.Unix.cs --- .../src/System/Threading/ThreadPool.Unix.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 8d00d91ad3eaa..e62a33ba5506a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -18,11 +18,11 @@ public static partial class ThreadPool AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); #endif -#if CORECLR || !(TARGET_BROWSER && FEATURE_WASM_THREADS) +#if CORECLR || !(TARGET_BROWSER && FEATURE_WASM_THREADS) // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work. internal static bool YieldFromDispatchLoop => false; -#elif +#endif internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); From 6d85f82aa3eef0789dd303f3fd57e20ff0651401 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 19 May 2023 16:36:18 -0700 Subject: [PATCH 102/168] Update System.Private.CoreLib.Shared.projitems --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a1309315f9817..19c35fba4718b 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2564,7 +2564,7 @@ - + From 52880823d68471d7e43125cb11736ccbba87bf36 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 16:38:15 -0700 Subject: [PATCH 103/168] Renaming RegisteredWaitHandle files --- .../System.Private.CoreLib.Shared.projitems | 9 + .../RegisteredWaitHandle.Portable.Core.cs | 231 ----------------- .../RegisteredWaitHandle.Portable.cs | 242 ++++++++++++++---- .../Threading/RegisteredWaitHandle.Unix.cs | 101 ++++++++ 4 files changed, 296 insertions(+), 287 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 19c35fba4718b..a10c49773f4e4 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2559,10 +2559,19 @@ +<<<<<<< Updated upstream +======= + + + + + + +>>>>>>> Stashed changes diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs deleted file mode 100644 index 79c42b9bbc1f8..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.Core.cs +++ /dev/null @@ -1,231 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.Versioning; -using Microsoft.Win32.SafeHandles; - -namespace System.Threading -{ - /// - /// An object representing the registration of a via . - /// - public sealed partial class RegisteredWaitHandle : MarshalByRefObject - { - private static AutoResetEvent RentEvent() => - Interlocked.Exchange(ref s_cachedEvent, null) ?? - new AutoResetEvent(false); - - private static void ReturnEvent(AutoResetEvent resetEvent) - { - if (Interlocked.CompareExchange(ref s_cachedEvent, resetEvent, null) != null) - { - resetEvent.Dispose(); - } - } - - internal void RestartTimeout() - { - Debug.Assert(!IsInfiniteTimeout); - TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs; - } - - private bool UnregisterPortableCore(WaitHandle waitObject) - { - // The registered wait handle must have been registered by this time, otherwise the instance is not handed out to - // the caller of the public variants of RegisterWaitForSingleObject - Debug.Assert(WaitThread != null); - - s_callbackLock.Acquire(); - bool needToRollBackRefCountOnException = false; - try - { - if (_unregisterCalled) - { - return false; - } - - UserUnregisterWaitHandle = waitObject?.SafeWaitHandle; - UserUnregisterWaitHandle?.DangerousAddRef(ref needToRollBackRefCountOnException); - - UserUnregisterWaitHandleValue = UserUnregisterWaitHandle?.DangerousGetHandle() ?? IntPtr.Zero; - - if (_unregistered) - { - SignalUserWaitHandle(); - return true; - } - - if (IsBlocking) - { - _callbacksComplete = RentEvent(); - } - else - { - _removed = RentEvent(); - } - } - catch (Exception) // Rollback state on exception - { - if (_removed != null) - { - ReturnEvent(_removed); - _removed = null; - } - else if (_callbacksComplete != null) - { - ReturnEvent(_callbacksComplete); - _callbacksComplete = null; - } - - UserUnregisterWaitHandleValue = IntPtr.Zero; - - if (needToRollBackRefCountOnException) - { - UserUnregisterWaitHandle?.DangerousRelease(); - } - - UserUnregisterWaitHandle = null; - throw; - } - finally - { - _unregisterCalled = true; - s_callbackLock.Release(); - } - - WaitThread!.UnregisterWait(this); - return true; - } - - /// - /// Signal if it has not been signaled yet and is a valid handle. - /// - private void SignalUserWaitHandle() - { - s_callbackLock.VerifyIsLocked(); - SafeWaitHandle? handle = UserUnregisterWaitHandle; - IntPtr handleValue = UserUnregisterWaitHandleValue; - try - { - if (handleValue != IntPtr.Zero && handleValue != InvalidHandleValue) - { - Debug.Assert(handleValue == handle!.DangerousGetHandle()); - EventWaitHandle.Set(handle); - } - } - finally - { - handle?.DangerousRelease(); - _callbacksComplete?.Set(); - _unregistered = true; - } - } - - /// - /// Perform the registered callback if the has not been signaled. - /// - /// Whether or not the wait timed out. - internal void PerformCallbackPortableCore(bool timedOut) - { -#if DEBUG - s_callbackLock.Acquire(); - try - { - Debug.Assert(_numRequestedCallbacks != 0); - } - finally - { - s_callbackLock.Release(); - } -#endif - - _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback!, timedOut); - CompleteCallbackRequest(); - } - - /// - /// Tell this handle that there is a callback queued on the thread pool for this handle. - /// - internal void RequestCallback() - { - s_callbackLock.Acquire(); - try - { - _numRequestedCallbacks++; - } - finally - { - s_callbackLock.Release(); - } - } - - /// - /// Called when the wait thread removes this handle registration. This will signal the user's event if there are no callbacks pending, - /// or note that the user's event must be signaled when the callbacks complete. - /// - internal void OnRemoveWait() - { - s_callbackLock.Acquire(); - try - { - _removed?.Set(); - if (_numRequestedCallbacks == 0) - { - SignalUserWaitHandle(); - } - else - { - _signalAfterCallbacksComplete = true; - } - } - finally - { - s_callbackLock.Release(); - } - } - - /// - /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it. - /// - private void CompleteCallbackRequest() - { - s_callbackLock.Acquire(); - try - { - --_numRequestedCallbacks; - if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete) - { - SignalUserWaitHandle(); - } - } - finally - { - s_callbackLock.Release(); - } - } - - /// - /// Wait for all queued callbacks and the full unregistration to complete. - /// - internal void WaitForCallbacks() - { - Debug.Assert(IsBlocking); - Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. - - _callbacksComplete!.WaitOne(); - ReturnEvent(_callbacksComplete); - _callbacksComplete = null; - } - - internal void WaitForRemoval() - { - Debug.Assert(!IsBlocking); - Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. - - _removed!.WaitOne(); - ReturnEvent(_removed); - _removed = null; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index 79820b6b39936..79c42b9bbc1f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; @@ -10,92 +10,222 @@ namespace System.Threading /// /// An object representing the registration of a via . /// -#if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] -#endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, - int millisecondsTimeout, bool repeating) + private static AutoResetEvent RentEvent() => + Interlocked.Exchange(ref s_cachedEvent, null) ?? + new AutoResetEvent(false); + + private static void ReturnEvent(AutoResetEvent resetEvent) { - Thread.ThrowIfNoThreadStart(); - Handle = waitHandle.SafeWaitHandle; - Callback = callbackHelper; - TimeoutDurationMs = millisecondsTimeout; - Repeating = repeating; - if (!IsInfiniteTimeout) + if (Interlocked.CompareExchange(ref s_cachedEvent, resetEvent, null) != null) { - RestartTimeout(); + resetEvent.Dispose(); } } - private static AutoResetEvent? s_cachedEvent; + internal void RestartTimeout() + { + Debug.Assert(!IsInfiniteTimeout); + TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs; + } - private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); + private bool UnregisterPortableCore(WaitHandle waitObject) + { + // The registered wait handle must have been registered by this time, otherwise the instance is not handed out to + // the caller of the public variants of RegisterWaitForSingleObject + Debug.Assert(WaitThread != null); - /// - /// The callback to execute when the wait on either times out or completes. - /// - internal _ThreadPoolWaitOrTimerCallback Callback { get; } + s_callbackLock.Acquire(); + bool needToRollBackRefCountOnException = false; + try + { + if (_unregisterCalled) + { + return false; + } + + UserUnregisterWaitHandle = waitObject?.SafeWaitHandle; + UserUnregisterWaitHandle?.DangerousAddRef(ref needToRollBackRefCountOnException); + + UserUnregisterWaitHandleValue = UserUnregisterWaitHandle?.DangerousGetHandle() ?? IntPtr.Zero; + + if (_unregistered) + { + SignalUserWaitHandle(); + return true; + } + + if (IsBlocking) + { + _callbacksComplete = RentEvent(); + } + else + { + _removed = RentEvent(); + } + } + catch (Exception) // Rollback state on exception + { + if (_removed != null) + { + ReturnEvent(_removed); + _removed = null; + } + else if (_callbacksComplete != null) + { + ReturnEvent(_callbacksComplete); + _callbacksComplete = null; + } + + UserUnregisterWaitHandleValue = IntPtr.Zero; + + if (needToRollBackRefCountOnException) + { + UserUnregisterWaitHandle?.DangerousRelease(); + } + + UserUnregisterWaitHandle = null; + throw; + } + finally + { + _unregisterCalled = true; + s_callbackLock.Release(); + } - /// - /// The that was registered. - /// - internal SafeWaitHandle Handle { get; } + WaitThread!.UnregisterWait(this); + return true; + } /// - /// The time this handle times out at in ms. + /// Signal if it has not been signaled yet and is a valid handle. /// - internal int TimeoutTimeMs { get; private set; } - - internal int TimeoutDurationMs { get; } - - internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; + private void SignalUserWaitHandle() + { + s_callbackLock.VerifyIsLocked(); + SafeWaitHandle? handle = UserUnregisterWaitHandle; + IntPtr handleValue = UserUnregisterWaitHandleValue; + try + { + if (handleValue != IntPtr.Zero && handleValue != InvalidHandleValue) + { + Debug.Assert(handleValue == handle!.DangerousGetHandle()); + EventWaitHandle.Set(handle); + } + } + finally + { + handle?.DangerousRelease(); + _callbacksComplete?.Set(); + _unregistered = true; + } + } /// - /// Whether or not the wait is a repeating wait. + /// Perform the registered callback if the has not been signaled. /// - internal bool Repeating { get; } + /// Whether or not the wait timed out. + internal void PerformCallbackPortableCore(bool timedOut) + { +#if DEBUG + s_callbackLock.Acquire(); + try + { + Debug.Assert(_numRequestedCallbacks != 0); + } + finally + { + s_callbackLock.Release(); + } +#endif + + _ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback(Callback!, timedOut); + CompleteCallbackRequest(); + } /// - /// The the user passed in via . + /// Tell this handle that there is a callback queued on the thread pool for this handle. /// - private SafeWaitHandle? UserUnregisterWaitHandle { get; set; } - - private IntPtr UserUnregisterWaitHandleValue { get; set; } - - private static IntPtr InvalidHandleValue => new IntPtr(-1); - - internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue; + internal void RequestCallback() + { + s_callbackLock.Acquire(); + try + { + _numRequestedCallbacks++; + } + finally + { + s_callbackLock.Release(); + } + } /// - /// The number of callbacks that are currently queued on the Thread Pool or executing. + /// Called when the wait thread removes this handle registration. This will signal the user's event if there are no callbacks pending, + /// or note that the user's event must be signaled when the callbacks complete. /// - private int _numRequestedCallbacks; + internal void OnRemoveWait() + { + s_callbackLock.Acquire(); + try + { + _removed?.Set(); + if (_numRequestedCallbacks == 0) + { + SignalUserWaitHandle(); + } + else + { + _signalAfterCallbacksComplete = true; + } + } + finally + { + s_callbackLock.Release(); + } + } /// - /// Notes if we need to signal the user's unregister event after all callbacks complete. + /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it. /// - private bool _signalAfterCallbacksComplete; - - private bool _unregisterCalled; - - private bool _unregistered; - - private AutoResetEvent? _callbacksComplete; - - private AutoResetEvent? _removed; + private void CompleteCallbackRequest() + { + s_callbackLock.Acquire(); + try + { + --_numRequestedCallbacks; + if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete) + { + SignalUserWaitHandle(); + } + } + finally + { + s_callbackLock.Release(); + } + } /// - /// The this was registered on. + /// Wait for all queued callbacks and the full unregistration to complete. /// - internal PortableThreadPool.WaitThread? WaitThread { get; set; } + internal void WaitForCallbacks() + { + Debug.Assert(IsBlocking); + Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. - public bool Unregister(WaitHandle waitObject) => UnregisterPortableCore(waitObject); + _callbacksComplete!.WaitOne(); + ReturnEvent(_callbacksComplete); + _callbacksComplete = null; + } - internal void PerformCallback(bool timedOut) + internal void WaitForRemoval() { - PerformCallbackPortableCore(timedOut); + Debug.Assert(!IsBlocking); + Debug.Assert(_unregisterCalled); // Should only be called when the wait is unregistered by the user. + + _removed!.WaitOne(); + ReturnEvent(_removed); + _removed = null; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs new file mode 100644 index 0000000000000..79820b6b39936 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.Versioning; +using Microsoft.Win32.SafeHandles; + +namespace System.Threading +{ + /// + /// An object representing the registration of a via . + /// +#if !FEATURE_WASM_THREADS + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] +#endif + public sealed partial class RegisteredWaitHandle : MarshalByRefObject + { + internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + int millisecondsTimeout, bool repeating) + { + Thread.ThrowIfNoThreadStart(); + Handle = waitHandle.SafeWaitHandle; + Callback = callbackHelper; + TimeoutDurationMs = millisecondsTimeout; + Repeating = repeating; + if (!IsInfiniteTimeout) + { + RestartTimeout(); + } + } + + private static AutoResetEvent? s_cachedEvent; + + private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); + + /// + /// The callback to execute when the wait on either times out or completes. + /// + internal _ThreadPoolWaitOrTimerCallback Callback { get; } + + /// + /// The that was registered. + /// + internal SafeWaitHandle Handle { get; } + + /// + /// The time this handle times out at in ms. + /// + internal int TimeoutTimeMs { get; private set; } + + internal int TimeoutDurationMs { get; } + + internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; + + /// + /// Whether or not the wait is a repeating wait. + /// + internal bool Repeating { get; } + + /// + /// The the user passed in via . + /// + private SafeWaitHandle? UserUnregisterWaitHandle { get; set; } + + private IntPtr UserUnregisterWaitHandleValue { get; set; } + + private static IntPtr InvalidHandleValue => new IntPtr(-1); + + internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue; + + /// + /// The number of callbacks that are currently queued on the Thread Pool or executing. + /// + private int _numRequestedCallbacks; + + /// + /// Notes if we need to signal the user's unregister event after all callbacks complete. + /// + private bool _signalAfterCallbacksComplete; + + private bool _unregisterCalled; + + private bool _unregistered; + + private AutoResetEvent? _callbacksComplete; + + private AutoResetEvent? _removed; + + /// + /// The this was registered on. + /// + internal PortableThreadPool.WaitThread? WaitThread { get; set; } + + public bool Unregister(WaitHandle waitObject) => UnregisterPortableCore(waitObject); + + internal void PerformCallback(bool timedOut) + { + PerformCallbackPortableCore(timedOut); + } + } +} From db02dacdc193b818dd54cc9ff7025a932f743a19 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 16:54:44 -0700 Subject: [PATCH 104/168] Fix System.Private.CoreLib.Shared.projitems --- .../src/System.Private.CoreLib.Shared.projitems | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a10c49773f4e4..314ed8442792c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2559,20 +2559,11 @@ -<<<<<<< Updated upstream - - - -======= - - - - ->>>>>>> Stashed changes + From 78a9764a51764fc338dab4a568ebcb529c6f1b58 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 16:57:39 -0700 Subject: [PATCH 105/168] Renaming: PreAllocatedOverlapped.Portable.Core.cs to PreAllocatedOverlapped.Portable.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- ...pped.Portable.Core.cs => PreAllocatedOverlapped.Portable.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{PreAllocatedOverlapped.Portable.Core.cs => PreAllocatedOverlapped.Portable.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 314ed8442792c..5aa2488746f6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2558,7 +2558,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.Core.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs From 8e1fee37233a09c36180762d7e9f55360226e14f Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 17:01:59 -0700 Subject: [PATCH 106/168] Moving BindHandlePortableCore to ThreadPoolBoundHandle.Portable --- .../ThreadPoolBoundHandle.Portable.cs | 35 ++++++++++++++++ .../ThreadPoolBoundHandle.Windows.cs | 40 ------------------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs index 5ce4e29d2ce54..adfdacf416887 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs @@ -16,6 +16,41 @@ namespace System.Threading /// public sealed partial class ThreadPoolBoundHandle : IDisposable { + private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) + { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + Debug.Assert(handle != null); + Debug.Assert(!handle.IsClosed); + Debug.Assert(!handle.IsInvalid); + + try + { + Debug.Assert(OperatingSystem.IsWindows()); + // ThreadPool.BindHandle will always return true, otherwise, it throws. + bool succeeded = ThreadPool.BindHandle(handle); + Debug.Assert(succeeded); + } + catch (Exception ex) + { // BindHandle throws ApplicationException on full CLR and Exception on CoreCLR. + // We do not let either of these leak and convert them to ArgumentException to + // indicate that the specified handles are invalid. + + if (ex.HResult == HResults.E_HANDLE) // Bad handle + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + if (ex.HResult == HResults.E_INVALIDARG) // Handle already bound or sync handle + throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); + + throw; + } + + return new ThreadPoolBoundHandle(handle); + } + private unsafe NativeOverlapped* AllocateNativeOverlappedPortableCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlappedPortableCore(callback, state, pinData, flowExecutionContext: true); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index 002d5f877f28b..ac426a4fe2f07 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -40,46 +40,6 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// public SafeHandle Handle => _handle; - private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) - { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - return BindHandleWindowsCore(handle); - } - - private static ThreadPoolBoundHandle BindHandleWindowsCore(SafeHandle handle) - { - Debug.Assert(handle != null); - Debug.Assert(!handle.IsClosed); - Debug.Assert(!handle.IsInvalid); - - try - { - Debug.Assert(OperatingSystem.IsWindows()); - // ThreadPool.BindHandle will always return true, otherwise, it throws. - bool succeeded = ThreadPool.BindHandle(handle); - Debug.Assert(succeeded); - } - catch (Exception ex) - { // BindHandle throws ApplicationException on full CLR and Exception on CoreCLR. - // We do not let either of these leak and convert them to ArgumentException to - // indicate that the specified handles are invalid. - - if (ex.HResult == HResults.E_HANDLE) // Bad handle - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - if (ex.HResult == HResults.E_INVALIDARG) // Handle already bound or sync handle - throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); - - throw; - } - - return new ThreadPoolBoundHandle(handle); - } - /// /// Returns a for the specific handle, /// which is bound to the system thread pool. From b80a136628eebc58571c28d0ebe0f230c4c6e60f Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Fri, 19 May 2023 17:03:39 -0700 Subject: [PATCH 107/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/ThreadPoolBoundHandle.Windows.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index ac426a4fe2f07..8582510e433b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -29,7 +29,10 @@ private ThreadPoolBoundHandle(SafeHandle handle, SafeThreadPoolIOHandle threadPo private ThreadPoolBoundHandle(SafeHandle handle) { + Debug.Assert(!ThreadPool.UseWindowsThreadPool); + _handle = handle; + GC.SuppressFinalize(this); } /// From bf6e4719943b113e13dbbe202d38402115ea57f6 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 17:05:17 -0700 Subject: [PATCH 108/168] Assertion in ThreadPoolBoundHandle finalizer --- .../src/System/Threading/ThreadPoolBoundHandle.Windows.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index 8582510e433b6..9281c55a81dbd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -136,10 +136,8 @@ public void Dispose() ~ThreadPoolBoundHandle() { - if (ThreadPool.UseWindowsThreadPool) - { - FinalizeWindowsThreadPool(); - } + Debug.Assert(ThreadPool.UseWindowsThreadPool); + FinalizeWindowsThreadPool(); } void IDeferredDisposable.OnFinalRelease(bool disposed) From e673550238e115956a3e8f03edbc01e74439d439 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 18:08:24 -0700 Subject: [PATCH 109/168] Delete some comments --- .../System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs | 1 - .../src/System/Threading/WindowsThreadPool.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index d22ddcb94e4cf..295fbe076e1f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -144,7 +144,6 @@ private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlapp [UnmanagedCallersOnly] private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, nuint numberOfBytesTransferred, IntPtr ioPtr) { - // Enabling for NativeAot first #if NATIVEAOT var wrapper = ThreadPoolCallbackWrapper.Enter(); #endif diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index ad56b8ce709f8..3dd74674bf00f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -13,7 +13,6 @@ namespace System.Threading // // Windows-specific implementation of ThreadPool // - // PR-Comment: Making it internal just to make it build internal static class WindowsThreadPool { /// @@ -144,7 +143,6 @@ internal static void NotifyThreadUnblocked() { } [UnmanagedCallersOnly] private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { - // PR-Comment: Assuming this is no longer necessary, might be wrong about this #if NATIVEAOT var wrapper = ThreadPoolCallbackWrapper.Enter(); #endif From 028005fe79554e6b46b9c54910dce01b3c41f911 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 19 May 2023 18:12:03 -0700 Subject: [PATCH 110/168] Add some comments --- .../Threading/RegisteredWaitHandle.WindowsThreadPool.cs | 2 +- .../src/System/Threading/WindowsThreadPool.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 41810cd9df99b..0762624ee4ad6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -15,7 +15,7 @@ namespace System.Threading // public sealed partial class RegisteredWaitHandle : MarshalByRefObject { -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 // Remove unused parameter [UnmanagedCallersOnly] internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index 3dd74674bf00f..d7a9a032b44f9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -72,7 +72,7 @@ private static object CreateThreadLocalCompletionCountObject() return threadLocalCompletionCountObject; } -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 // Remove unused parameter public static bool SetMaxThreads(int workerThreads, int completionPortThreads) { // Not supported at present @@ -88,7 +88,7 @@ public static void GetMaxThreads(out int workerThreads, out int completionPortTh completionPortThreads = MaxThreadCount; } -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 // Remove unused parameter public static bool SetMinThreads(int workerThreads, int completionPortThreads) { // Not supported at present @@ -213,7 +213,7 @@ public static bool BindHandle(IntPtr osHandle) throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 // Remove unused parameter [SupportedOSPlatform("windows")] public static bool BindHandle(SafeHandle osHandle) { From e68c5b91337a434ea6b077aee16a7edf358db019 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 23 May 2023 08:10:36 -0700 Subject: [PATCH 111/168] Fix BindHandlePortableCore --- .../Threading/ThreadPool.Portable.Windows.cs | 4 +- .../System/Threading/ThreadPool.Windows.cs | 4 +- .../ThreadPoolBoundHandle.Portable.cs | 35 ------------------ .../ThreadPoolBoundHandle.Windows.cs | 37 ++++++++++++++++++- ...eadPoolBoundHandle.Browser.Threads.Mono.cs | 2 +- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs index d1e959ce89c31..d6579c374df24 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs @@ -27,14 +27,14 @@ private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlap [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(IntPtr osHandle) + private static bool BindHandleCore(IntPtr osHandle) { PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); return true; } [SupportedOSPlatform("windows")] - private static bool BindHandlePortableCore(SafeHandle osHandle) + private static bool BindHandleCore(SafeHandle osHandle) { ArgumentNullException.ThrowIfNull(osHandle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 341e93a4ee8d9..1584573c889d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -48,13 +48,13 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp public static bool BindHandle(IntPtr osHandle) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.BindHandle(osHandle) : - BindHandlePortableCore(osHandle); + BindHandleCore(osHandle); [SupportedOSPlatform("windows")] public static bool BindHandle(SafeHandle osHandle) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.BindHandle(osHandle) : - BindHandlePortableCore(osHandle); + BindHandleCore(osHandle); internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs index adfdacf416887..5ce4e29d2ce54 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Portable.cs @@ -16,41 +16,6 @@ namespace System.Threading /// public sealed partial class ThreadPoolBoundHandle : IDisposable { - private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) - { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - Debug.Assert(handle != null); - Debug.Assert(!handle.IsClosed); - Debug.Assert(!handle.IsInvalid); - - try - { - Debug.Assert(OperatingSystem.IsWindows()); - // ThreadPool.BindHandle will always return true, otherwise, it throws. - bool succeeded = ThreadPool.BindHandle(handle); - Debug.Assert(succeeded); - } - catch (Exception ex) - { // BindHandle throws ApplicationException on full CLR and Exception on CoreCLR. - // We do not let either of these leak and convert them to ArgumentException to - // indicate that the specified handles are invalid. - - if (ex.HResult == HResults.E_HANDLE) // Bad handle - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - - if (ex.HResult == HResults.E_INVALIDARG) // Handle already bound or sync handle - throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); - - throw; - } - - return new ThreadPoolBoundHandle(handle); - } - private unsafe NativeOverlapped* AllocateNativeOverlappedPortableCore(IOCompletionCallback callback, object? state, object? pinData) => AllocateNativeOverlappedPortableCore(callback, state, pinData, flowExecutionContext: true); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index 9281c55a81dbd..fe136e1cde0d2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -43,6 +43,41 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// public SafeHandle Handle => _handle; + private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + Debug.Assert(handle != null); + Debug.Assert(!handle.IsClosed); + Debug.Assert(!handle.IsInvalid); + + try + { + Debug.Assert(OperatingSystem.IsWindows()); + // ThreadPool.BindHandle will always return true, otherwise, it throws. + bool succeeded = ThreadPool.BindHandle(handle); + Debug.Assert(succeeded); + } + catch (Exception ex) + { // BindHandle throws ApplicationException on full CLR and Exception on CoreCLR. + // We do not let either of these leak and convert them to ArgumentException to + // indicate that the specified handles are invalid. + + if (ex.HResult == HResults.E_HANDLE) // Bad handle + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + + if (ex.HResult == HResults.E_INVALIDARG) // Handle already bound or sync handle + throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle)); + + throw; + } + + return new ThreadPoolBoundHandle(handle); + } + /// /// Returns a for the specific handle, /// which is bound to the system thread pool. @@ -83,7 +118,7 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// it remains the responsibility of the caller to call . /// public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => - ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandlePortableCore(handle); + ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandleCore(handle); [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs index 027dcf67c0dd3..6d06335953dca 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs @@ -8,7 +8,7 @@ namespace System.Threading public sealed partial class ThreadPoolBoundHandle : IDisposable { #pragma warning disable IDE0060 - private static ThreadPoolBoundHandle BindHandlePortableCore(SafeHandle handle) + private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } From 288c04d049e06cb98f9a2ebcc37a2988866eede1 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:18:43 -0700 Subject: [PATCH 112/168] Unnecesarry checks in ThreadPoolBoundHandle.Unix.cs removed --- .../src/System/Threading/ThreadPoolBoundHandle.Unix.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs index db23fc0105667..c3c98c9eab8cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs @@ -73,11 +73,6 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) { - ArgumentNullException.ThrowIfNull(handle); - - if (handle.IsClosed || handle.IsInvalid) - throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - Debug.Assert(handle != null); Debug.Assert(!handle.IsClosed); Debug.Assert(!handle.IsInvalid); From 2b4fdf50e8330e5c30c7ca4de9fcac198ea2a3c6 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:23:17 -0700 Subject: [PATCH 113/168] Partial modified removed in ThreadPoolBoundHandle.Browser.Mono.cs --- .../src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs index 4fe3fddbe6fe3..8e2f33e002f6b 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Mono.cs @@ -5,7 +5,7 @@ namespace System.Threading { - public sealed partial class ThreadPoolBoundHandle : IDisposable + public sealed class ThreadPoolBoundHandle : IDisposable { public SafeHandle Handle => null!; From c87c53be614550355fcf142c13b1af6d20fd85a4 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:31:42 -0700 Subject: [PATCH 114/168] Remove ReportThreadStatusCore --- .../System/Threading/ThreadPool.Browser.Mono.cs | 5 ++++- .../src/System/Threading/ThreadPool.Mono.cs | 17 ----------------- 2 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index 654c341b3d323..196ee72feea55 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -152,6 +152,9 @@ public static bool BindHandle(SafeHandle osHandle) } [Conditional("unnecessary")] - internal static void ReportThreadStatus(bool isWorking) => ReportThreadStatusCore(isWorking); + internal static void ReportThreadStatus(bool isWorking) + { + + } } } diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs deleted file mode 100644 index b5e1eef9e736f..0000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Mono.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Diagnostics; - -namespace System.Threading -{ - public static partial class ThreadPool - { - [Conditional("unnecessary")] - internal static void ReportThreadStatusCore(bool isWorking) - { - } - } -} From cfb7b8be7b1d8e417a8d62bac555ce601b9aaf34 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:41:52 -0700 Subject: [PATCH 115/168] Remove ThreadPool.Mono.cs --- .../System.Private.CoreLib/System.Private.CoreLib.csproj | 1 - .../src/System/Threading/ThreadPool.Browser.Mono.cs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index b8e53f4ed0fea..e85ded0d50897 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -267,7 +267,6 @@ - CommonSystem\Collections\Generic\ArrayBuilder.cs diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index 196ee72feea55..492083928215e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -150,11 +150,5 @@ public static bool BindHandle(SafeHandle osHandle) { throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } - - [Conditional("unnecessary")] - internal static void ReportThreadStatus(bool isWorking) - { - - } } } From 0c722a62a4f862a178432ce0f38c1f2056adbc66 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:43:24 -0700 Subject: [PATCH 116/168] Partial modified in ThreadPool.Browser.Mono.cs removed --- .../src/System/Threading/ThreadPool.Browser.Mono.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index 492083928215e..a5e54288a2920 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -16,7 +16,7 @@ namespace System.Threading #if !FEATURE_WASM_THREADS [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] #endif - public sealed partial class RegisteredWaitHandle : MarshalByRefObject + public sealed class RegisteredWaitHandle : MarshalByRefObject { internal RegisteredWaitHandle() { From 3b73bf2429682e3cf6346868995aaeb741aa250f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:46:43 -0700 Subject: [PATCH 117/168] Partial modifier in PreAllocatedOverlapped.Browser.Mono.cs removed --- .../src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs index f82b3fa966479..caad4d140aa40 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Browser.Mono.cs @@ -3,7 +3,7 @@ namespace System.Threading { - public sealed partial class PreAllocatedOverlapped : System.IDisposable + public sealed class PreAllocatedOverlapped : System.IDisposable { [CLSCompliantAttribute(false)] public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) { } From 711546dbc476c7c3093d71498d0f6548a5a2ef97 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 08:49:03 -0700 Subject: [PATCH 118/168] ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs renamed to PreAllocatedOverlapped.WindowsThreadPool.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- ...hreadPool.cs => PreAllocatedOverlapped.WindowsThreadPool.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs => PreAllocatedOverlapped.WindowsThreadPool.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 5aa2488746f6e..9a51f7fb2ca6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2067,7 +2067,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.WindowsThreadPool.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolPreAllocatedOverlapped.WindowsThreadPool.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.WindowsThreadPool.cs From d393fd5e571da610787a8cbeca86c1d75c95cd15 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 11:26:48 -0700 Subject: [PATCH 119/168] Place fields and properties from RegisteredWaitHandle.Windows.cs to the correct files --- .../RegisteredWaitHandle.Portable.cs | 54 +++++++ .../Threading/RegisteredWaitHandle.Windows.cs | 134 +----------------- .../RegisteredWaitHandle.WindowsThreadPool.cs | 74 ++++++++++ 3 files changed, 132 insertions(+), 130 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index 79c42b9bbc1f8..4e74f995a873d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -12,6 +12,60 @@ namespace System.Threading /// public sealed partial class RegisteredWaitHandle : MarshalByRefObject { + private static AutoResetEvent? s_cachedEvent; + private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); + + /// + /// The the user passed in via . + /// + private SafeWaitHandle? UserUnregisterWaitHandle { get; set; } + + private IntPtr UserUnregisterWaitHandleValue { get; set; } + + private static IntPtr InvalidHandleValue => new IntPtr(-1); + + internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue; + + /// + /// The number of callbacks that are currently queued on the Thread Pool or executing. + /// + private int _numRequestedCallbacks; + + /// + /// Notes if we need to signal the user's unregister event after all callbacks complete. + /// + private bool _signalAfterCallbacksComplete; + + private bool _unregisterCalled; + + private bool _unregistered; + + private AutoResetEvent? _callbacksComplete; + + private AutoResetEvent? _removed; + + /// + /// The this was registered on. + /// + internal PortableThreadPool.WaitThread? WaitThread { get; set; } + + internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + int millisecondsTimeout, bool repeating) + { + Debug.Assert(!ThreadPool.UseWindowsThreadPool); + GC.SuppressFinalize(this); + + Thread.ThrowIfNoThreadStart(); + _waitHandle = waitHandle.SafeWaitHandle; + _callbackHelper = callbackHelper; + _signedMillisecondsTimeout = millisecondsTimeout; + _repeating = repeating; + if (!IsInfiniteTimeout) + { + RestartTimeout(); + } + } + private static AutoResetEvent RentEvent() => Interlocked.Exchange(ref s_cachedEvent, null) ?? new AutoResetEvent(false); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs index b518da7f47019..4bd3afc740522 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs @@ -15,66 +15,10 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - private readonly object? _lock; - private SafeWaitHandle? _waitHandle; private readonly _ThreadPoolWaitOrTimerCallback? _callbackHelper; private readonly uint _millisecondsTimeout; private readonly int _signedMillisecondsTimeout; private bool _repeating; - private bool _unregistering; - - // Handle to this object to keep it alive - private GCHandle _gcHandle; - - // Pointer to the TP_WAIT structure - private IntPtr _tpWait; - - internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, - uint millisecondsTimeout, bool repeating) - { - Debug.Assert(ThreadPool.UseWindowsThreadPool); - - _lock = new object(); - - waitHandle.DangerousAddRef(); - _waitHandle = waitHandle; - - _callbackHelper = callbackHelper; - _millisecondsTimeout = millisecondsTimeout; - _repeating = repeating; - - // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked - _gcHandle = GCHandle.Alloc(this); - - _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); - - if (_tpWait == IntPtr.Zero) - { - _gcHandle.Free(); - throw new OutOfMemoryException(); - } - } - - internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, - int millisecondsTimeout, bool repeating) - { - Debug.Assert(!ThreadPool.UseWindowsThreadPool); - GC.SuppressFinalize(this); - - Thread.ThrowIfNoThreadStart(); - _waitHandle = waitHandle.SafeWaitHandle; - _callbackHelper = callbackHelper; - _signedMillisecondsTimeout = millisecondsTimeout; - _repeating = repeating; - if (!IsInfiniteTimeout) - { - RestartTimeout(); - } - } - - private static AutoResetEvent? s_cachedEvent; - - private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); /// /// The callback to execute when the wait on either times out or completes. @@ -89,7 +33,6 @@ internal _ThreadPoolWaitOrTimerCallback? Callback /// internal SafeWaitHandle Handle { - // get => _waitHandle; get { Debug.Assert(_waitHandle != null); @@ -117,41 +60,10 @@ internal bool Repeating get => _repeating; } - /// - /// The the user passed in via . - /// - private SafeWaitHandle? UserUnregisterWaitHandle { get; set; } - - private IntPtr UserUnregisterWaitHandleValue { get; set; } - - private static IntPtr InvalidHandleValue => new IntPtr(-1); - - internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue; - - /// - /// The number of callbacks that are currently queued on the Thread Pool or executing. - /// - private int _numRequestedCallbacks; - - /// - /// Notes if we need to signal the user's unregister event after all callbacks complete. - /// - private bool _signalAfterCallbacksComplete; - - private bool _unregisterCalled; - - private bool _unregistered; - - private AutoResetEvent? _callbacksComplete; - - private AutoResetEvent? _removed; - - /// - /// The this was registered on. - /// - internal PortableThreadPool.WaitThread? WaitThread { get; set; } - - public bool Unregister(WaitHandle waitObject) => ThreadPool.UseWindowsThreadPool ? UnregisterWindowsThreadPool(waitObject) : UnregisterPortableCore(waitObject); + public bool Unregister(WaitHandle waitObject) => + ThreadPool.UseWindowsThreadPool ? + UnregisterWindowsThreadPool(waitObject) : + UnregisterPortableCore(waitObject); internal void PerformCallback(bool timedOut) { @@ -164,43 +76,5 @@ internal void PerformCallback(bool timedOut) PerformCallbackPortableCore(timedOut); } } - - ~RegisteredWaitHandle() - { - Debug.Assert(ThreadPool.UseWindowsThreadPool); - // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC - Debug.Assert(!_gcHandle.IsAllocated); - - // If this object gets resurrected and another thread calls Unregister, that creates a race condition. - // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. - // The _lock may be null in case of OOM in the constructor. - if ((_lock != null) && Monitor.TryEnter(_lock)) - { - try - { - if (!_unregistering) - { - _unregistering = true; - - if (_tpWait != IntPtr.Zero) - { - // There must be no in-flight callbacks; just dispose resources - Interop.Kernel32.CloseThreadpoolWait(_tpWait); - _tpWait = IntPtr.Zero; - } - - if (_waitHandle != null) - { - _waitHandle.DangerousRelease(); - _waitHandle = null; - } - } - } - finally - { - Monitor.Exit(_lock); - } - } - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 0762624ee4ad6..f5221dd0f64e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -15,6 +15,42 @@ namespace System.Threading // public sealed partial class RegisteredWaitHandle : MarshalByRefObject { + private readonly object? _lock; + private SafeWaitHandle? _waitHandle; + private bool _unregistering; + + // Handle to this object to keep it alive + private GCHandle _gcHandle; + + // Pointer to the TP_WAIT structure + private IntPtr _tpWait; + + internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, + uint millisecondsTimeout, bool repeating) + { + Debug.Assert(ThreadPool.UseWindowsThreadPool); + + _lock = new object(); + + waitHandle.DangerousAddRef(); + _waitHandle = waitHandle; + + _callbackHelper = callbackHelper; + _millisecondsTimeout = millisecondsTimeout; + _repeating = repeating; + + // Allocate _gcHandle and _tpWait as the last step and make sure they are never leaked + _gcHandle = GCHandle.Alloc(this); + + _tpWait = Interop.Kernel32.CreateThreadpoolWait(&RegisteredWaitCallback, (IntPtr)_gcHandle, IntPtr.Zero); + + if (_tpWait == IntPtr.Zero) + { + _gcHandle.Free(); + throw new OutOfMemoryException(); + } + } + #pragma warning disable IDE0060 // Remove unused parameter [UnmanagedCallersOnly] internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) @@ -144,6 +180,44 @@ private void FinishUnregisteringAsync(object? waitObject) Interop.Kernel32.SetEvent(safeWaitHandle); } } + + ~RegisteredWaitHandle() + { + Debug.Assert(ThreadPool.UseWindowsThreadPool); + // If _gcHandle is allocated, it points to this object, so this object must not be collected by the GC + Debug.Assert(!_gcHandle.IsAllocated); + + // If this object gets resurrected and another thread calls Unregister, that creates a race condition. + // Do not block the finalizer thread. If another thread is running Unregister, it will clean up for us. + // The _lock may be null in case of OOM in the constructor. + if ((_lock != null) && Monitor.TryEnter(_lock)) + { + try + { + if (!_unregistering) + { + _unregistering = true; + + if (_tpWait != IntPtr.Zero) + { + // There must be no in-flight callbacks; just dispose resources + Interop.Kernel32.CloseThreadpoolWait(_tpWait); + _tpWait = IntPtr.Zero; + } + + if (_waitHandle != null) + { + _waitHandle.DangerousRelease(); + _waitHandle = null; + } + } + } + finally + { + Monitor.Exit(_lock); + } + } + } } } From dd02c7986242c303082443af10e3a3b7fe3bbd78 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 23 May 2023 13:07:32 -0700 Subject: [PATCH 120/168] Fix RegisteredWaitHandle on Unix --- .../RegisteredWaitHandle.Portable.cs | 2 + .../Threading/RegisteredWaitHandle.Unix.cs | 79 ++++++------------- .../Threading/RegisteredWaitHandle.Windows.cs | 1 + .../RegisteredWaitHandle.WindowsThreadPool.cs | 1 - 4 files changed, 27 insertions(+), 56 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index 4e74f995a873d..d1b63130f2e66 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -52,8 +52,10 @@ public sealed partial class RegisteredWaitHandle : MarshalByRefObject internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, int millisecondsTimeout, bool repeating) { +#if WINDOWS Debug.Assert(!ThreadPool.UseWindowsThreadPool); GC.SuppressFinalize(this); +#endif Thread.ThrowIfNoThreadStart(); _waitHandle = waitHandle.SafeWaitHandle; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs index 79820b6b39936..4e4dd2de36675 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs @@ -15,81 +15,50 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { - internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper, - int millisecondsTimeout, bool repeating) - { - Thread.ThrowIfNoThreadStart(); - Handle = waitHandle.SafeWaitHandle; - Callback = callbackHelper; - TimeoutDurationMs = millisecondsTimeout; - Repeating = repeating; - if (!IsInfiniteTimeout) - { - RestartTimeout(); - } - } - - private static AutoResetEvent? s_cachedEvent; - - private static readonly LowLevelLock s_callbackLock = new LowLevelLock(); + private SafeWaitHandle? _waitHandle; + private readonly _ThreadPoolWaitOrTimerCallback? _callbackHelper; + private readonly int _signedMillisecondsTimeout; + private bool _repeating; /// /// The callback to execute when the wait on either times out or completes. /// - internal _ThreadPoolWaitOrTimerCallback Callback { get; } + internal _ThreadPoolWaitOrTimerCallback? Callback + { + get => _callbackHelper; + } /// /// The that was registered. /// - internal SafeWaitHandle Handle { get; } + internal SafeWaitHandle Handle + { + get + { + Debug.Assert(_waitHandle != null); + return _waitHandle; + } + } /// /// The time this handle times out at in ms. /// internal int TimeoutTimeMs { get; private set; } - internal int TimeoutDurationMs { get; } + internal int TimeoutDurationMs + { + get => _signedMillisecondsTimeout; + } internal bool IsInfiniteTimeout => TimeoutDurationMs == -1; /// /// Whether or not the wait is a repeating wait. /// - internal bool Repeating { get; } - - /// - /// The the user passed in via . - /// - private SafeWaitHandle? UserUnregisterWaitHandle { get; set; } - - private IntPtr UserUnregisterWaitHandleValue { get; set; } - - private static IntPtr InvalidHandleValue => new IntPtr(-1); - - internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue; - - /// - /// The number of callbacks that are currently queued on the Thread Pool or executing. - /// - private int _numRequestedCallbacks; - - /// - /// Notes if we need to signal the user's unregister event after all callbacks complete. - /// - private bool _signalAfterCallbacksComplete; - - private bool _unregisterCalled; - - private bool _unregistered; - - private AutoResetEvent? _callbacksComplete; - - private AutoResetEvent? _removed; - - /// - /// The this was registered on. - /// - internal PortableThreadPool.WaitThread? WaitThread { get; set; } + internal bool Repeating + { + get => _repeating; + } public bool Unregister(WaitHandle waitObject) => UnregisterPortableCore(waitObject); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs index 4bd3afc740522..0ec3f15c75c88 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs @@ -15,6 +15,7 @@ namespace System.Threading #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { + private SafeWaitHandle? _waitHandle; private readonly _ThreadPoolWaitOrTimerCallback? _callbackHelper; private readonly uint _millisecondsTimeout; private readonly int _signedMillisecondsTimeout; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index f5221dd0f64e2..b8cb8bac59467 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -16,7 +16,6 @@ namespace System.Threading public sealed partial class RegisteredWaitHandle : MarshalByRefObject { private readonly object? _lock; - private SafeWaitHandle? _waitHandle; private bool _unregistering; // Handle to this object to keep it alive From afd1057466b050967f5f63bdf59b45205149f095 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 13:17:52 -0700 Subject: [PATCH 121/168] Renamed ThreadPool.Portable.cs to ThreadPool.NonCoreCLR.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../{ThreadPool.Portable.cs => ThreadPool.NonCoreCLR.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{ThreadPool.Portable.cs => ThreadPool.NonCoreCLR.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 9a51f7fb2ca6e..d29e9912562be 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2537,7 +2537,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs From 24bb2df08acf1b67aa9658e421853f3162c37293 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 23 May 2023 13:46:02 -0700 Subject: [PATCH 122/168] Add ReportThreadStatus to ThreadPool.Browser.Mono.cs --- .../src/System/Threading/ThreadPool.Browser.Mono.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index a5e54288a2920..d019bf3e95713 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -150,5 +150,11 @@ public static bool BindHandle(SafeHandle osHandle) { throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle } + + [Conditional("unnecessary")] + internal static void ReportThreadStatus(bool isWorking) + { + + } } } From 3e51debc00ae4b7055550f8154e12f45fdb5bdc3 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 14:46:32 -0700 Subject: [PATCH 123/168] Place XML on public methods --- .../PreAllocatedOverlapped.Portable.cs | 3 - .../Threading/PreAllocatedOverlapped.Unix.cs | 38 +++++ .../PreAllocatedOverlapped.Windows.cs | 4 + .../Threading/RegisteredWaitHandle.Unix.cs | 4 + .../Threading/RegisteredWaitHandle.Windows.cs | 4 + .../System/Threading/ThreadPool.Windows.cs | 1 + .../ThreadPoolBoundHandle.Windows.cs | 152 ++++++++++++++++++ 7 files changed, 203 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs index 8d6e4f481e56b..c481ac6cefc0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Portable.cs @@ -24,9 +24,6 @@ private void ReleasePortableCore() _lifetime.Release(this); } - /// - /// Frees the resources associated with this instance. - /// private void DisposePortableCore() { _lifetime.Dispose(this); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs index f62ea0dd88951..d638e6773f2ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Unix.cs @@ -53,6 +53,41 @@ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, obje private DeferredDisposableLifetime _lifetime; + /// + /// Initializes a new instance of the class, specifying + /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when each asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes instance produced from this + /// object from other instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operations. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// The new instance can be passed to + /// , to produce + /// a instance that can be passed to the operating system in overlapped + /// I/O operations. A single instance can only be used for + /// a single native I/O operation at a time. However, the state stored in the + /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation + /// of the callback. + /// + /// The buffers specified in are pinned until is called. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// [CLSCompliant(false)] public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => UnsafeCreatePortableCore(callback, state, pinData); @@ -68,6 +103,9 @@ private unsafe PreAllocatedOverlapped(IOCompletionCallback callback, object? sta internal bool AddRef() => AddRefPortableCore(); + /// + /// Frees the resources associated with this instance. + /// public void Dispose() { DisposePortableCore(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs index 1bc5d212245e6..32149a7295496 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.Windows.cs @@ -123,6 +123,10 @@ internal void Release() ReleasePortableCore(); } } + + /// + /// Frees the resources associated with this instance. + /// public void Dispose() { if (ThreadPool.UseWindowsThreadPool) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs index 4e4dd2de36675..f9401f1ac520b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs @@ -62,6 +62,10 @@ internal bool Repeating public bool Unregister(WaitHandle waitObject) => UnregisterPortableCore(waitObject); + /// + /// Perform the registered callback if the has not been signaled. + /// + /// Whether or not the wait timed out. internal void PerformCallback(bool timedOut) { PerformCallbackPortableCore(timedOut); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs index 0ec3f15c75c88..964391ee06b98 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs @@ -66,6 +66,10 @@ public bool Unregister(WaitHandle waitObject) => UnregisterWindowsThreadPool(waitObject) : UnregisterPortableCore(waitObject); + /// + /// Perform the registered callback if the has not been signaled. + /// + /// Whether or not the wait timed out. internal void PerformCallback(bool timedOut) { if (ThreadPool.UseWindowsThreadPool) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 1584573c889d1..f6c5d140859cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -69,6 +69,7 @@ internal static void InitializeForThreadPoolThread() InitializeForThreadPoolThreadPortableCore(); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void IncrementCompletedWorkItemCount() => WindowsThreadPool.IncrementCompletedWorkItemCount(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index fe136e1cde0d2..69ee529c0aa6d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -120,24 +120,160 @@ private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) public static unsafe ThreadPoolBoundHandle BindHandle(SafeHandle handle) => ThreadPool.UseWindowsThreadPool ? BindHandleWindowsThreadPool(handle) : BindHandleCore(handle); + /// + /// Returns an unmanaged pointer to a structure, specifying + /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when the asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes this from other + /// instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operation. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// An unmanaged pointer to a structure. + /// + /// + /// + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The structure is fixed in + /// physical memory until is called. + /// + /// + /// The buffer or buffers specified in must be the same as those passed + /// to the unmanaged operating system function that performs the asynchronous I/O. + /// + /// + /// The buffers specified in are pinned for the duration of + /// the I/O operation. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => ThreadPool.UseWindowsThreadPool ? AllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : AllocateNativeOverlappedPortableCore(callback, state, pinData); + /// + /// Returns an unmanaged pointer to a structure, specifying + /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided + /// object providing context, and managed objects that serve as buffers. + /// + /// + /// An delegate that represents the callback method + /// invoked when the asynchronous I/O operation completes. + /// + /// + /// A user-provided object that distinguishes this from other + /// instances. Can be . + /// + /// + /// An object or array of objects representing the input or output buffer for the operation. Each + /// object represents a buffer, for example an array of bytes. Can be . + /// + /// + /// An unmanaged pointer to a structure. + /// + /// + /// + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The structure is fixed in + /// physical memory until is called. + /// + /// + /// The buffer or buffers specified in must be the same as those passed + /// to the unmanaged operating system function that performs the asynchronous I/O. + /// + /// + /// is not flowed to the invocation of the callback. + /// + /// + /// The buffers specified in are pinned for the duration of + /// the I/O operation. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// [CLSCompliant(false)] public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) => ThreadPool.UseWindowsThreadPool ? UnsafeAllocateNativeOverlappedWindowsThreadPool(callback, state, pinData) : UnsafeAllocateNativeOverlappedPortableCore(callback, state, pinData); + /// + /// Returns an unmanaged pointer to a structure, using the callback, + /// state, and buffers associated with the specified object. + /// + /// + /// A object from which to create the NativeOverlapped pointer. + /// + /// + /// An unmanaged pointer to a structure. + /// + /// + /// + /// The unmanaged pointer returned by this method can be passed to the operating system in + /// overlapped I/O operations. The structure is fixed in + /// physical memory until is called. + /// + /// + /// + /// is . + /// + /// + /// is currently in use for another I/O operation. + /// + /// + /// This method was called after the was disposed, or + /// this method was called after was disposed. + /// + /// [CLSCompliant(false)] public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated) => ThreadPool.UseWindowsThreadPool ? AllocateNativeOverlappedWindowsThreadPool(preAllocated) : AllocateNativeOverlappedPortableCore(preAllocated); + /// + /// Frees the unmanaged memory associated with a structure + /// allocated by the method. + /// + /// + /// An unmanaged pointer to the structure to be freed. + /// + /// + /// + /// You must call the method exactly once + /// on every unmanaged pointer allocated using the + /// method. + /// If you do not call the method, you will + /// leak memory. If you call the method more + /// than once on the same unmanaged pointer, memory will be corrupted. + /// + /// + /// + /// is . + /// + /// + /// This method was called after the was disposed. + /// [CLSCompliant(false)] public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) { @@ -151,6 +287,22 @@ public unsafe void FreeNativeOverlapped(NativeOverlapped* overlapped) } } + /// + /// Returns the user-provided object specified when the instance was + /// allocated using the . + /// + /// + /// An unmanaged pointer to the structure from which to return the + /// associated user-provided object. + /// + /// + /// A user-provided object that distinguishes this + /// from other instances, otherwise, if one was + /// not specified when the instance was allocated using . + /// + /// + /// is . + /// [CLSCompliant(false)] public static unsafe object? GetNativeOverlappedState(NativeOverlapped* overlapped) => ThreadPool.UseWindowsThreadPool ? From 7673336c81a1eab8729d9b4eb3af70a50c7336f1 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 May 2023 22:44:46 -0700 Subject: [PATCH 124/168] Fix ThreadPoolCallbackWrapper --- .../src/System.Private.CoreLib.csproj | 2 +- ...=> ThreadPoolCallbackWrapper.NativeAot.cs} | 0 .../System.Private.CoreLib.Shared.projitems | 1 + .../RegisteredWaitHandle.WindowsThreadPool.cs | 7 ++--- ...ThreadPoolBoundHandle.WindowsThreadPool.cs | 6 ++-- .../Threading/ThreadPoolCallbackWrapper.cs | 30 +++++++++++++++++++ .../src/System/Threading/WindowsThreadPool.cs | 6 ++-- 7 files changed, 38 insertions(+), 14 deletions(-) rename src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/{ThreadPoolCallbackWrapper.cs => ThreadPoolCallbackWrapper.NativeAot.cs} (100%) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 738ec45aff836..4e382a5e7a683 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -290,7 +290,7 @@ - + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.NativeAot.cs similarity index 100% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs rename to src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.NativeAot.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d29e9912562be..c1fb2d646a152 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2567,6 +2567,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index b8cb8bac59467..3573732b9ae58 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -54,10 +54,8 @@ internal unsafe RegisteredWaitHandle(SafeWaitHandle waitHandle, _ThreadPoolWaitO [UnmanagedCallersOnly] internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, IntPtr wait, uint waitResult) { - // Enabling for NativeAot first -#if NATIVEAOT var wrapper = ThreadPoolCallbackWrapper.Enter(); -#endif + GCHandle handle = (GCHandle)context; RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)handle.Target!; Debug.Assert((handle == registeredWaitHandle._gcHandle) && (wait == registeredWaitHandle._tpWait)); @@ -65,9 +63,8 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int bool timedOut = (waitResult == (uint)Interop.Kernel32.WAIT_TIMEOUT); registeredWaitHandle.PerformCallbackWindowsThreadPool(timedOut); ThreadPool.IncrementCompletedWorkItemCount(); -#if NATIVEAOT + wrapper.Exit(); -#endif } #pragma warning restore IDE0060 diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs index 295fbe076e1f1..26098e17f2e8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.WindowsThreadPool.cs @@ -144,9 +144,8 @@ private static unsafe Win32ThreadPoolNativeOverlapped.OverlappedData GetOverlapp [UnmanagedCallersOnly] private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, IntPtr overlappedPtr, uint ioResult, nuint numberOfBytesTransferred, IntPtr ioPtr) { -#if NATIVEAOT var wrapper = ThreadPoolCallbackWrapper.Enter(); -#endif + Win32ThreadPoolNativeOverlapped* overlapped = (Win32ThreadPoolNativeOverlapped*)overlappedPtr; ThreadPoolBoundHandle? boundHandle = overlapped->Data._boundHandle; @@ -157,9 +156,8 @@ private static unsafe void OnNativeIOCompleted(IntPtr instance, IntPtr context, Win32ThreadPoolNativeOverlapped.CompleteWithCallback(ioResult, (uint)numberOfBytesTransferred, overlapped); ThreadPool.IncrementCompletedWorkItemCount(); -#if NATIVEAOT + wrapper.Exit(); -#endif } private bool AddRef() diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs new file mode 100644 index 0000000000000..249a498949338 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading +{ + internal struct ThreadPoolCallbackWrapper + { + private Thread _currentThread; + + public static ThreadPoolCallbackWrapper Enter() + { + if (!Thread.CurrentThread.IsThreadPoolThread) + { + Thread.CurrentThread.IsThreadPoolThread = true; + } + return new ThreadPoolCallbackWrapper + { + _currentThread = Thread.CurrentThread, + }; + } + + public void Exit(bool resetThread = true) + { + if (resetThread) + { + _currentThread.ResetThreadPoolThread(); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs index d7a9a032b44f9..d0637d2fd62f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WindowsThreadPool.cs @@ -143,17 +143,15 @@ internal static void NotifyThreadUnblocked() { } [UnmanagedCallersOnly] private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { -#if NATIVEAOT var wrapper = ThreadPoolCallbackWrapper.Enter(); -#endif + Debug.Assert(s_work == work); Interlocked.Increment(ref s_workingThreadCounter.Count); ThreadPoolWorkQueue.Dispatch(); Interlocked.Decrement(ref s_workingThreadCounter.Count); + // We reset the thread after executing each callback -#if NATIVEAOT wrapper.Exit(resetThread: false); -#endif } internal static unsafe void RequestWorkerThread() From e6bbd3b2f8f0246bed972ffe3ce598472e1aabc9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 09:49:35 -0700 Subject: [PATCH 125/168] Cache Thread.CurrentThread --- .../src/System/Threading/ThreadPoolCallbackWrapper.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs index 249a498949338..d6829bf3615a9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs @@ -9,13 +9,14 @@ internal struct ThreadPoolCallbackWrapper public static ThreadPoolCallbackWrapper Enter() { - if (!Thread.CurrentThread.IsThreadPoolThread) + Thread currentThread = Thread.CurrentThread; + if (!currentThread.IsThreadPoolThread) { - Thread.CurrentThread.IsThreadPoolThread = true; + currentThread.IsThreadPoolThread = true; } return new ThreadPoolCallbackWrapper { - _currentThread = Thread.CurrentThread, + _currentThread = currentThread, }; } From 8ea3735136e4fb4c501741cf77830f964392b257 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 11:27:11 -0700 Subject: [PATCH 126/168] Set WorkerTracking to false when Windows thread pool is enabled --- .../src/System/Threading/ThreadPool.Windows.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index f6c5d140859cd..8b34844948f92 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -13,17 +13,17 @@ public static partial class ThreadPool internal static bool UseWindowsThreadPool { get; } = AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); -#if CORECLR - // consider using internal const bool +#if NATIVEAOT + private const bool IsWorkerTrackingEnabledInConfig = false; +#elif CORECLR private static readonly bool IsWorkerTrackingEnabledInConfig = UseWindowsThreadPool ? false : GetEnableWorkerTracking(); -#elif NATIVEAOT - private const bool IsWorkerTrackingEnabledInConfig = false; #else private static readonly bool IsWorkerTrackingEnabledInConfig = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); + UseWindowsThreadPool ? false : AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); #endif + #if CORECLR // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work. From 0c58c8bf850d3fdf0ffb1a53d2e21f2bc0ca7083 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 11:31:36 -0700 Subject: [PATCH 127/168] Update comment on RegisteredWaitHandle::PerformCallbackWindowsThreadPool --- .../System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs index 3573732b9ae58..cad89f3f0709c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.WindowsThreadPool.cs @@ -70,7 +70,8 @@ internal static void RegisteredWaitCallback(IntPtr instance, IntPtr context, Int private void PerformCallbackWindowsThreadPool(bool timedOut) { - // New logic might be wrong here, not sure yet + // Prevent the race condition with Unregister and the previous PerformCallback call, which may still be + // holding the _lock. // If another thread is running Unregister, no need to restart the timer or clean up lock (_lock!) { From b38ff77e857b4d633d50ecfdd3c7f7934761c16d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 11:33:45 -0700 Subject: [PATCH 128/168] Remove assertions in ThreadPoolBoundHandle::BindHandle --- .../src/System/Threading/ThreadPoolBoundHandle.Unix.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs index c3c98c9eab8cd..9095f46f2b1ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs @@ -73,10 +73,6 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) { - Debug.Assert(handle != null); - Debug.Assert(!handle.IsClosed); - Debug.Assert(!handle.IsInvalid); - throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } From 71ea96f6de4b98ee7509bd330b0df2c24e1131b9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 14:06:21 -0700 Subject: [PATCH 129/168] Enable Windows thread pool timer --- .../src/System.Private.CoreLib.csproj | 4 ---- .../System.Private.CoreLib.Shared.projitems | 10 +++++++-- .../Threading/{Timer.cs => Timer.Portable.cs} | 0 .../src/System/Threading/Timer.Unix.cs | 21 +++++++++++++++++++ .../src/System/Threading/Timer.Windows.cs | 18 ++++++++++++++++ .../Threading/Timer.WindowsThreadPool.cs} | 8 ++++--- .../System/Threading/TimerQueue.Portable.cs | 8 +------ 7 files changed, 53 insertions(+), 16 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/Threading/{Timer.cs => Timer.Portable.cs} (100%) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs rename src/{coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs => libraries/System.Private.CoreLib/src/System/Threading/Timer.WindowsThreadPool.cs} (80%) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 4e382a5e7a683..a294b7016ea6e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -280,10 +280,6 @@ Interop\Windows\Kernel32\Interop.GetTickCount64.cs - - - Interop\Windows\Kernel32\Interop.Timer.cs - Interop\Windows\Kernel32\Interop.DynamicLoad.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index c1fb2d646a152..8ecb8c2cfea37 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1209,8 +1209,11 @@ - - + + + + + @@ -1594,6 +1597,9 @@ Common\Interop\Windows\Kernel32\Interop.CreateFile.cs + + Interop\Windows\Kernel32\Interop.Timer.cs + Interop\Windows\Kernel32\Interop.ThreadPoolIO.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Portable.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Portable.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs new file mode 100644 index 0000000000000..27fa794562dc8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Threading +{ + internal partial class TimerQueue + { +#pragma warning disable IDE0060 + private TimerQueue(int id) + { + } +#pragma warning restore IDE0060 + + private bool SetTimer(uint actualDuration) => SetTimerPortable(actualDuration); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs new file mode 100644 index 0000000000000..ed7c1ddbf38c5 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Threading +{ + internal partial class TimerQueue + { + private bool SetTimer(uint actualDuration) => + ThreadPool.UseWindowsThreadPool ? + SetTimerWindowsThreadPool(actualDuration) : + SetTimerPortable(actualDuration); + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.WindowsThreadPool.cs similarity index 80% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Timer.WindowsThreadPool.cs index 1f11b66ac14a5..a4eb62cb81696 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.WindowsThreadPool.cs @@ -21,8 +21,9 @@ private TimerQueue(int id) _id = id; } +#pragma warning disable IDE0060 // Remove unused parameter [UnmanagedCallersOnly] - private static unsafe void TimerCallback(void* instance, void* context, void* timer) + private static unsafe void TimerCallbackWindowsThreadPool(void* instance, void* context, void* timer) { int id = (int)context; var wrapper = ThreadPoolCallbackWrapper.Enter(); @@ -30,12 +31,13 @@ private static unsafe void TimerCallback(void* instance, void* context, void* ti ThreadPool.IncrementCompletedWorkItemCount(); wrapper.Exit(); } +#pragma warning restore IDE0060 - private unsafe bool SetTimer(uint actualDuration) + private unsafe bool SetTimerWindowsThreadPool(uint actualDuration) { if (_nativeTimer == IntPtr.Zero) { - _nativeTimer = Interop.Kernel32.CreateThreadpoolTimer(&TimerCallback, (IntPtr)_id, IntPtr.Zero); + _nativeTimer = Interop.Kernel32.CreateThreadpoolTimer(&TimerCallbackWindowsThreadPool, (IntPtr)_id, IntPtr.Zero); if (_nativeTimer == IntPtr.Zero) throw new OutOfMemoryException(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs index 40983768c823f..4d1e29d75a8b1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs @@ -23,12 +23,6 @@ internal sealed partial class TimerQueue : IThreadPoolWorkItem private bool _isScheduled; private long _scheduledDueTimeMs; -#pragma warning disable IDE0060 - private TimerQueue(int id) - { - } -#pragma warning restore IDE0060 - private static List InitializeScheduledTimerManager_Locked() { Debug.Assert(s_scheduledTimers == null); @@ -50,7 +44,7 @@ private static List InitializeScheduledTimerManager_Locked() return timers; } - private bool SetTimer(uint actualDuration) + private bool SetTimerPortable(uint actualDuration) { Debug.Assert((int)actualDuration >= 0); long dueTimeMs = TickCount64 + (int)actualDuration; From cbdbe583d706b9a499f46b0a864c7b8a3e00d30d Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 24 May 2023 14:18:57 -0700 Subject: [PATCH 130/168] Add Timer.WindowsThreadPool.cs only on Windows --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 8ecb8c2cfea37..1c7ab4e820cd2 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1212,7 +1212,7 @@ - + From ec6eebca815dd4ac34f281335a671ef14cb22796 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 24 May 2023 17:01:24 -0700 Subject: [PATCH 131/168] Fix TimerQueue.Browser.Mono.cs --- .../src/System.Private.CoreLib.Shared.projitems | 2 +- .../src/System/Threading/TimerQueue.Portable.cs | 1 + .../src/System/Threading/TimerQueue.Browser.Mono.cs | 6 ------ 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 1c7ab4e820cd2..38cca4ed25287 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1210,7 +1210,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs index 4d1e29d75a8b1..346259aa412c3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs @@ -20,6 +20,7 @@ internal sealed partial class TimerQueue : IThreadPoolWorkItem /// private static readonly AutoResetEvent s_timerEvent = new AutoResetEvent(false); + // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback private bool _isScheduled; private long _scheduledDueTimeMs; diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs index ba5e678e87427..d051b8f275f36 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs @@ -15,14 +15,8 @@ namespace System.Threading // internal partial class TimerQueue { - private static List? s_scheduledTimers; - private static List? s_scheduledTimersToFire; private static long s_shortestDueTimeMs = long.MaxValue; - // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback - private bool _isScheduled; - private long _scheduledDueTimeMs; - private TimerQueue(int _) { } From d9d5802b889e5b54dbfd92a9132a7a63dbfde877 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 24 May 2023 17:38:46 -0700 Subject: [PATCH 132/168] Fix browser buid --- .../src/System.Private.CoreLib.Shared.projitems | 4 ++-- .../src/System/Threading/TimerQueue.Browser.Mono.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 38cca4ed25287..a2c3618ee944b 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1210,10 +1210,10 @@ - + - + diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs index d051b8f275f36..24acdf695b975 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs @@ -15,8 +15,13 @@ namespace System.Threading // internal partial class TimerQueue { + private static List? s_scheduledTimers; + private static List? s_scheduledTimersToFire; private static long s_shortestDueTimeMs = long.MaxValue; + // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback + private bool _isScheduled; + private long _scheduledDueTimeMs; private TimerQueue(int _) { } From b519b17f80858019168b63fa59dc3a469f499bfb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 18:04:36 -0700 Subject: [PATCH 133/168] Enable FeaturePortableTimer on NativeAot --- .../System.Private.CoreLib/src/System.Private.CoreLib.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index a294b7016ea6e..1db8fb01760b9 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -29,8 +29,7 @@ true - false - true + true true From 6da6201872475832ea28b30b0a5f9de99ece7148 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 24 May 2023 19:06:00 -0700 Subject: [PATCH 134/168] Supress Finalizer on RegisteredWaitHandle --- .../src/System/Threading/RegisteredWaitHandle.Portable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index d1b63130f2e66..8e064c8ea333f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -54,8 +54,8 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb { #if WINDOWS Debug.Assert(!ThreadPool.UseWindowsThreadPool); - GC.SuppressFinalize(this); #endif + GC.SuppressFinalize(this); Thread.ThrowIfNoThreadStart(); _waitHandle = waitHandle.SafeWaitHandle; From 2acc143f863e07e3aa99e78fb6c030498a9262bc Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 May 2023 08:17:29 -0700 Subject: [PATCH 135/168] Throw argument exception in ThreadPoolBoundHandle.Unix.cs --- .../src/System/Threading/ThreadPoolBoundHandle.Unix.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs index 9095f46f2b1ac..9643edf7f2d8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Unix.cs @@ -73,6 +73,11 @@ private ThreadPoolBoundHandle(SafeHandle handle) /// public static ThreadPoolBoundHandle BindHandle(SafeHandle handle) { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsClosed || handle.IsInvalid) + throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); } From 7a5e7b7344aa93cd5b38a0f823609107e8a55595 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 May 2023 10:20:46 -0700 Subject: [PATCH 136/168] Set UseWindowsThreadPool=true for testing --- .../src/System/Threading/ThreadPool.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 8b34844948f92..1ebf7f84a36b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -11,7 +11,7 @@ namespace System.Threading public static partial class ThreadPool { internal static bool UseWindowsThreadPool { get; } = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool", true); #if NATIVEAOT private const bool IsWorkerTrackingEnabledInConfig = false; From f8bf8521a760831292d63e9181e13ae6b1389c07 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 May 2023 15:54:15 -0700 Subject: [PATCH 137/168] Revert "Set UseWindowsThreadPool=true for testing" This reverts commit 7a5e7b7344aa93cd5b38a0f823609107e8a55595. --- .../src/System/Threading/ThreadPool.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 1ebf7f84a36b9..8b34844948f92 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -11,7 +11,7 @@ namespace System.Threading public static partial class ThreadPool { internal static bool UseWindowsThreadPool { get; } = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool", true); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.UseWindowsThreadPool", "DOTNET_ThreadPool_UseWindowsThreadPool"); #if NATIVEAOT private const bool IsWorkerTrackingEnabledInConfig = false; From 53521b8e182cc9985d8a69d2574e36f9f23dba23 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 May 2023 16:09:26 -0700 Subject: [PATCH 138/168] Enable trimming --- .../src/ILLink/ILLink.Substitutions.Windows.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml index f832f7f2899d6..29c26957ff6ee 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Windows.xml @@ -1,8 +1,8 @@ - + + From 00fe833b86625fc08e5d59a3192b631ef58544b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 26 May 2023 13:47:41 -0700 Subject: [PATCH 139/168] Remove redundant assertions --- .../src/System/Threading/ThreadPoolBoundHandle.Windows.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs index 69ee529c0aa6d..97d77dd4f206f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Windows.cs @@ -50,10 +50,6 @@ private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) if (handle.IsClosed || handle.IsInvalid) throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle)); - Debug.Assert(handle != null); - Debug.Assert(!handle.IsClosed); - Debug.Assert(!handle.IsInvalid); - try { Debug.Assert(OperatingSystem.IsWindows()); From de39881f86a603330401001c4a0d5b7e73acdb16 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 26 May 2023 14:16:09 -0700 Subject: [PATCH 140/168] Fix IsWorkerTrackingEnabledInConfig in ThreadPool.Windows.cs --- .../System/Threading/ThreadPool.CoreCLR.cs | 3 -- src/coreclr/vm/comthreadpool.cpp | 31 +++++++++---------- .../System/Threading/ThreadPool.Windows.cs | 5 +-- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 4d0f5c4d6e747..5e3cb9030a11b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -70,8 +70,5 @@ private static extern unsafe int GetNextConfigUInt32Value( out bool isBoolean, out char* appContextConfigName); - private static bool GetEnableWorkerTracking() => - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); - } } diff --git a/src/coreclr/vm/comthreadpool.cpp b/src/coreclr/vm/comthreadpool.cpp index 2d941b130991d..daa8d48d3a307 100644 --- a/src/coreclr/vm/comthreadpool.cpp +++ b/src/coreclr/vm/comthreadpool.cpp @@ -43,23 +43,22 @@ FCIMPL4(INT32, ThreadPoolNative::GetNextConfigUInt32Value, case 2: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_ForceMaxWorkerThreads, false, W("System.Threading.ThreadPool.MaxThreads"))) { return 3; } FALLTHROUGH; case 3: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_DisableStarvationDetection, true, W("System.Threading.ThreadPool.DisableStarvationDetection"))) { return 4; } FALLTHROUGH; case 4: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_DebugBreakOnWorkerStarvation, true, W("System.Threading.ThreadPool.DebugBreakOnWorkerStarvation"))) { return 5; } FALLTHROUGH; - case 5: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking, true, W("System.Threading.ThreadPool.EnableWorkerTracking"))) { return 6; } FALLTHROUGH; - case 6: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_UnfairSemaphoreSpinLimit, false, W("System.Threading.ThreadPool.UnfairSemaphoreSpinLimit"))) { return 7; } FALLTHROUGH; + case 5: if (TryGetConfig(CLRConfig::INTERNAL_ThreadPool_UnfairSemaphoreSpinLimit, false, W("System.Threading.ThreadPool.UnfairSemaphoreSpinLimit"))) { return 6; } FALLTHROUGH; - case 7: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_Disable, true, W("System.Threading.ThreadPool.HillClimbing.Disable"))) { return 8; } FALLTHROUGH; - case 8: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_WavePeriod, false, W("System.Threading.ThreadPool.HillClimbing.WavePeriod"))) { return 9; } FALLTHROUGH; - case 9: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_TargetSignalToNoiseRatio, false, W("System.Threading.ThreadPool.HillClimbing.TargetSignalToNoiseRatio"))) { return 10; } FALLTHROUGH; - case 10: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_ErrorSmoothingFactor, false, W("System.Threading.ThreadPool.HillClimbing.ErrorSmoothingFactor"))) { return 11; } FALLTHROUGH; - case 11: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_WaveMagnitudeMultiplier, false, W("System.Threading.ThreadPool.HillClimbing.WaveMagnitudeMultiplier"))) { return 12; } FALLTHROUGH; - case 12: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxWaveMagnitude, false, W("System.Threading.ThreadPool.HillClimbing.MaxWaveMagnitude"))) { return 13; } FALLTHROUGH; - case 13: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_WaveHistorySize, false, W("System.Threading.ThreadPool.HillClimbing.WaveHistorySize"))) { return 14; } FALLTHROUGH; - case 14: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_Bias, false, W("System.Threading.ThreadPool.HillClimbing.Bias"))) { return 15; } FALLTHROUGH; - case 15: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxChangePerSecond, false, W("System.Threading.ThreadPool.HillClimbing.MaxChangePerSecond"))) { return 16; } FALLTHROUGH; - case 16: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxChangePerSample, false, W("System.Threading.ThreadPool.HillClimbing.MaxChangePerSample"))) { return 17; } FALLTHROUGH; - case 17: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxSampleErrorPercent, false, W("System.Threading.ThreadPool.HillClimbing.MaxSampleErrorPercent"))) { return 18; } FALLTHROUGH; - case 18: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_SampleIntervalLow, false, W("System.Threading.ThreadPool.HillClimbing.SampleIntervalLow"))) { return 19; } FALLTHROUGH; - case 19: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_SampleIntervalHigh, false, W("System.Threading.ThreadPool.HillClimbing.SampleIntervalHigh"))) { return 20; } FALLTHROUGH; - case 20: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_GainExponent, false, W("System.Threading.ThreadPool.HillClimbing.GainExponent"))) { return 21; } FALLTHROUGH; + case 6: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_Disable, true, W("System.Threading.ThreadPool.HillClimbing.Disable"))) { return 7; } FALLTHROUGH; + case 7: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_WavePeriod, false, W("System.Threading.ThreadPool.HillClimbing.WavePeriod"))) { return 8; } FALLTHROUGH; + case 8: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_TargetSignalToNoiseRatio, false, W("System.Threading.ThreadPool.HillClimbing.TargetSignalToNoiseRatio"))) { return 9; } FALLTHROUGH; + case 9: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_ErrorSmoothingFactor, false, W("System.Threading.ThreadPool.HillClimbing.ErrorSmoothingFactor"))) { return 10; } FALLTHROUGH; + case 10: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_WaveMagnitudeMultiplier, false, W("System.Threading.ThreadPool.HillClimbing.WaveMagnitudeMultiplier"))) { return 11; } FALLTHROUGH; + case 11: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxWaveMagnitude, false, W("System.Threading.ThreadPool.HillClimbing.MaxWaveMagnitude"))) { return 12; } FALLTHROUGH; + case 12: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_WaveHistorySize, false, W("System.Threading.ThreadPool.HillClimbing.WaveHistorySize"))) { return 13; } FALLTHROUGH; + case 13: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_Bias, false, W("System.Threading.ThreadPool.HillClimbing.Bias"))) { return 14; } FALLTHROUGH; + case 14: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxChangePerSecond, false, W("System.Threading.ThreadPool.HillClimbing.MaxChangePerSecond"))) { return 15; } FALLTHROUGH; + case 15: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxChangePerSample, false, W("System.Threading.ThreadPool.HillClimbing.MaxChangePerSample"))) { return 16; } FALLTHROUGH; + case 16: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_MaxSampleErrorPercent, false, W("System.Threading.ThreadPool.HillClimbing.MaxSampleErrorPercent"))) { return 17; } FALLTHROUGH; + case 17: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_SampleIntervalLow, false, W("System.Threading.ThreadPool.HillClimbing.SampleIntervalLow"))) { return 18; } FALLTHROUGH; + case 18: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_SampleIntervalHigh, false, W("System.Threading.ThreadPool.HillClimbing.SampleIntervalHigh"))) { return 19; } FALLTHROUGH; + case 19: if (TryGetConfig(CLRConfig::INTERNAL_HillClimbing_GainExponent, false, W("System.Threading.ThreadPool.HillClimbing.GainExponent"))) { return 20; } FALLTHROUGH; default: *configValueRef = 0; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 8b34844948f92..dd11053b2c40a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -15,12 +15,9 @@ public static partial class ThreadPool #if NATIVEAOT private const bool IsWorkerTrackingEnabledInConfig = false; -#elif CORECLR - private static readonly bool IsWorkerTrackingEnabledInConfig = - UseWindowsThreadPool ? false : GetEnableWorkerTracking(); #else private static readonly bool IsWorkerTrackingEnabledInConfig = - UseWindowsThreadPool ? false : AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); + UseWindowsThreadPool ? false : AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", "DOTNET_ThreadPool_EnableWorkerTracking", false); #endif From 883d96672a571417bd9a8f07f8018c587cea85ca Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Fri, 26 May 2023 14:17:35 -0700 Subject: [PATCH 141/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs Co-authored-by: Koundinya Veluri --- .../src/System/Threading/ThreadPool.Windows.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index dd11053b2c40a..f10ea9dbfb2c2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -21,16 +21,14 @@ public static partial class ThreadPool #endif -#if CORECLR +#if !(TARGET_BROWSER && FEATURE_WASM_THREADS) // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work. // // Windows thread pool threads need to yield back to the thread pool periodically, otherwise those threads may be // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread // injection. - internal static bool YieldFromDispatchLoop => UseWindowsThreadPool ? true : false; -#elif !(TARGET_BROWSER && FEATURE_WASM_THREADS) - internal static bool YieldFromDispatchLoop => false; + internal static bool YieldFromDispatchLoop => UseWindowsThreadPool; #endif [CLSCompliant(false)] From d70d86e99501ebbd869fa716e98389b18e993498 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 May 2023 11:48:36 -0700 Subject: [PATCH 142/168] Renamed BindHandleCore to BindHandlePortableCore --- .../src/System/Threading/ThreadPool.Portable.Windows.cs | 4 ++-- .../src/System/Threading/ThreadPool.Windows.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs index d6579c374df24..d1e959ce89c31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.Windows.cs @@ -27,14 +27,14 @@ private static unsafe bool UnsafeQueueNativeOverlappedPortableCore(NativeOverlap [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] [SupportedOSPlatform("windows")] - private static bool BindHandleCore(IntPtr osHandle) + private static bool BindHandlePortableCore(IntPtr osHandle) { PortableThreadPool.ThreadPoolInstance.RegisterForIOCompletionNotifications(osHandle); return true; } [SupportedOSPlatform("windows")] - private static bool BindHandleCore(SafeHandle osHandle) + private static bool BindHandlePortableCore(SafeHandle osHandle) { ArgumentNullException.ThrowIfNull(osHandle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index f10ea9dbfb2c2..3fa7e50b022a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -43,13 +43,13 @@ public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapp public static bool BindHandle(IntPtr osHandle) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.BindHandle(osHandle) : - BindHandleCore(osHandle); + BindHandlePortableCore(osHandle); [SupportedOSPlatform("windows")] public static bool BindHandle(SafeHandle osHandle) => ThreadPool.UseWindowsThreadPool ? WindowsThreadPool.BindHandle(osHandle) : - BindHandleCore(osHandle); + BindHandlePortableCore(osHandle); internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); From 09e932e171561662fa1db26ecc1a8ac325df0876 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 May 2023 12:30:02 -0700 Subject: [PATCH 143/168] Fix Timer* files --- .../System.Private.CoreLib.Shared.projitems | 8 +++---- .../src/System/Threading/Timer.Unix.cs | 21 ------------------- .../src/System/Threading/Timer.Windows.cs | 18 ---------------- .../Threading/{Timer.Portable.cs => Timer.cs} | 0 .../src/System/Threading/TimerQueue.Unix.cs | 8 +++++++ .../System/Threading/TimerQueue.Windows.cs | 5 +++++ ...ool.cs => TimerQueue.WindowsThreadPool.cs} | 0 7 files changed, 16 insertions(+), 44 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs rename src/libraries/System.Private.CoreLib/src/System/Threading/{Timer.Portable.cs => Timer.cs} (100%) rename src/libraries/System.Private.CoreLib/src/System/Threading/{Timer.WindowsThreadPool.cs => TimerQueue.WindowsThreadPool.cs} (100%) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a2c3618ee944b..efac778402982 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1209,10 +1209,7 @@ - - - - + @@ -2071,6 +2068,7 @@ + @@ -2389,7 +2387,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs deleted file mode 100644 index 27fa794562dc8..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Unix.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace System.Threading -{ - internal partial class TimerQueue - { -#pragma warning disable IDE0060 - private TimerQueue(int id) - { - } -#pragma warning restore IDE0060 - - private bool SetTimer(uint actualDuration) => SetTimerPortable(actualDuration); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs deleted file mode 100644 index ed7c1ddbf38c5..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Windows.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace System.Threading -{ - internal partial class TimerQueue - { - private bool SetTimer(uint actualDuration) => - ThreadPool.UseWindowsThreadPool ? - SetTimerWindowsThreadPool(actualDuration) : - SetTimerPortable(actualDuration); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/Timer.Portable.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs index 005fa12ca6f23..140d136a12cb1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs @@ -6,5 +6,13 @@ namespace System.Threading internal sealed partial class TimerQueue { private static long TickCount64 => Environment.TickCount64; + +#pragma warning disable IDE0060 + private TimerQueue(int id) + { + } +#pragma warning restore IDE0060 + + private bool SetTimer(uint actualDuration) => SetTimerPortable(actualDuration); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs index 228cae763e262..5dfa89d9c5e9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs @@ -33,5 +33,10 @@ private static long TickCount64 } } } + + private bool SetTimer(uint actualDuration) => + ThreadPool.UseWindowsThreadPool ? + SetTimerWindowsThreadPool(actualDuration) : + SetTimerPortable(actualDuration); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.WindowsThreadPool.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Threading/Timer.WindowsThreadPool.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.WindowsThreadPool.cs From 4e56e7b4d0d98665cb498c14f859159eda34d064 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 May 2023 12:32:53 -0700 Subject: [PATCH 144/168] Move constructor to TimerQueue.Windows.cs --- .../src/System/Threading/TimerQueue.Windows.cs | 5 +++++ .../src/System/Threading/TimerQueue.WindowsThreadPool.cs | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs index 5dfa89d9c5e9f..48babb49981ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs @@ -7,6 +7,11 @@ namespace System.Threading { internal sealed partial class TimerQueue { + private TimerQueue(int id) + { + _id = id; + } + private static long TickCount64 { get diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.WindowsThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.WindowsThreadPool.cs index a4eb62cb81696..438e53db4eeae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.WindowsThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.WindowsThreadPool.cs @@ -16,11 +16,6 @@ internal partial class TimerQueue private IntPtr _nativeTimer; private readonly int _id; - private TimerQueue(int id) - { - _id = id; - } - #pragma warning disable IDE0060 // Remove unused parameter [UnmanagedCallersOnly] private static unsafe void TimerCallbackWindowsThreadPool(void* instance, void* context, void* timer) From b59b9804fc22635928d56b2e93986f8f502b6204 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 29 May 2023 12:58:42 -0700 Subject: [PATCH 145/168] Removed GetEnableWorkerTracking() --- .../src/System/Threading/ThreadPool.Unix.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index e62a33ba5506a..fb7e8e20cd8fb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -9,9 +9,7 @@ namespace System.Threading { public static partial class ThreadPool { -#if CORECLR - private static readonly bool IsWorkerTrackingEnabledInConfig = GetEnableWorkerTracking(); -#elif NATIVEAOT +#if NATIVEAOT private const bool IsWorkerTrackingEnabledInConfig = false; #else private static readonly bool IsWorkerTrackingEnabledInConfig = From 71c83d041811b5bd4517d3c0e60a6fee9b910ea8 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 29 May 2023 13:26:00 -0700 Subject: [PATCH 146/168] Added TickCount64 to TimerQueue.Browser.Mono.cs --- .../src/System/Threading/TimerQueue.Browser.Mono.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs index 24acdf695b975..c007bae667395 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Browser.Mono.cs @@ -15,6 +15,7 @@ namespace System.Threading // internal partial class TimerQueue { + private static long TickCount64 => Environment.TickCount64; private static List? s_scheduledTimers; private static List? s_scheduledTimersToFire; private static long s_shortestDueTimeMs = long.MaxValue; From cfc1fa1638618b555c643ebcd087ebf8101ef330 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2023 19:28:23 -0700 Subject: [PATCH 147/168] Enable Windows Threadpool tests --- .../System.Threading.ThreadPool.sln | 114 +++++++++++------- ....ThreadPool.WindowsThreadPool.Tests.csproj | 21 ++++ 2 files changed, 92 insertions(+), 43 deletions(-) create mode 100644 src/libraries/System.Threading.ThreadPool/tests/WindowsThreadPool/System.Threading.ThreadPool.WindowsThreadPool.Tests.csproj diff --git a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln index 037f900dcc57b..7dd75710c3bdc 100644 --- a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln +++ b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33712.159 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{BEACD8A1-3C09-450E-931F-561D44986BFB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{CD477FC8-AF27-46A2-A451-4A2D4C11600D}" @@ -29,19 +33,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{1445280F-1F0 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{6A32653B-8FAC-4B01-A0E0-E6379DE90A1A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Threading.ThreadPool.WindowsThreadPool.Tests", "tests\WindowsThreadPool\System.Threading.ThreadPool.WindowsThreadPool.Tests.csproj", "{2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Checked|Any CPU = Checked|Any CPU + Checked|x64 = Checked|x64 + Checked|x86 = Checked|x86 Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 - Checked|Any CPU = Checked|Any CPU - Checked|x64 = Checked|x64 - Checked|x86 = Checked|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|Any CPU.ActiveCfg = Checked|x64 + {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|Any CPU.Build.0 = Checked|x64 + {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x64.ActiveCfg = Checked|x64 + {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x64.Build.0 = Checked|x64 + {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x86.ActiveCfg = Checked|x86 + {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x86.Build.0 = Checked|x86 {BEACD8A1-3C09-450E-931F-561D44986BFB}.Debug|Any CPU.ActiveCfg = Debug|x64 {BEACD8A1-3C09-450E-931F-561D44986BFB}.Debug|Any CPU.Build.0 = Debug|x64 {BEACD8A1-3C09-450E-931F-561D44986BFB}.Debug|x64.ActiveCfg = Debug|x64 @@ -54,12 +66,9 @@ Global {BEACD8A1-3C09-450E-931F-561D44986BFB}.Release|x64.Build.0 = Release|x64 {BEACD8A1-3C09-450E-931F-561D44986BFB}.Release|x86.ActiveCfg = Release|x86 {BEACD8A1-3C09-450E-931F-561D44986BFB}.Release|x86.Build.0 = Release|x86 - {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|Any CPU.ActiveCfg = Checked|x64 - {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|Any CPU.Build.0 = Checked|x64 - {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x64.ActiveCfg = Checked|x64 - {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x64.Build.0 = Checked|x64 - {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x86.ActiveCfg = Checked|x86 - {BEACD8A1-3C09-450E-931F-561D44986BFB}.Checked|x86.Build.0 = Checked|x86 + {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Checked|x64.ActiveCfg = Debug|Any CPU + {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Checked|x86.ActiveCfg = Debug|Any CPU {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -72,9 +81,9 @@ Global {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Release|x64.Build.0 = Release|Any CPU {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Release|x86.ActiveCfg = Release|Any CPU {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Release|x86.Build.0 = Release|Any CPU - {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Checked|x64.ActiveCfg = Debug|Any CPU - {CD477FC8-AF27-46A2-A451-4A2D4C11600D}.Checked|x86.ActiveCfg = Debug|Any CPU + {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Checked|x64.ActiveCfg = Debug|Any CPU + {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Checked|x86.ActiveCfg = Debug|Any CPU {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Debug|Any CPU.Build.0 = Debug|Any CPU {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -87,9 +96,9 @@ Global {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Release|x64.Build.0 = Release|Any CPU {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Release|x86.ActiveCfg = Release|Any CPU {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Release|x86.Build.0 = Release|Any CPU - {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Checked|x64.ActiveCfg = Debug|Any CPU - {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373}.Checked|x86.ActiveCfg = Debug|Any CPU + {3156246A-939D-4232-90B3-FDE03A14BA90}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {3156246A-939D-4232-90B3-FDE03A14BA90}.Checked|x64.ActiveCfg = Debug|Any CPU + {3156246A-939D-4232-90B3-FDE03A14BA90}.Checked|x86.ActiveCfg = Debug|Any CPU {3156246A-939D-4232-90B3-FDE03A14BA90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3156246A-939D-4232-90B3-FDE03A14BA90}.Debug|Any CPU.Build.0 = Debug|Any CPU {3156246A-939D-4232-90B3-FDE03A14BA90}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -102,9 +111,9 @@ Global {3156246A-939D-4232-90B3-FDE03A14BA90}.Release|x64.Build.0 = Release|Any CPU {3156246A-939D-4232-90B3-FDE03A14BA90}.Release|x86.ActiveCfg = Release|Any CPU {3156246A-939D-4232-90B3-FDE03A14BA90}.Release|x86.Build.0 = Release|Any CPU - {3156246A-939D-4232-90B3-FDE03A14BA90}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {3156246A-939D-4232-90B3-FDE03A14BA90}.Checked|x64.ActiveCfg = Debug|Any CPU - {3156246A-939D-4232-90B3-FDE03A14BA90}.Checked|x86.ActiveCfg = Debug|Any CPU + {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Checked|x64.ActiveCfg = Debug|Any CPU + {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Checked|x86.ActiveCfg = Debug|Any CPU {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -117,9 +126,9 @@ Global {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Release|x64.Build.0 = Release|Any CPU {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Release|x86.ActiveCfg = Release|Any CPU {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Release|x86.Build.0 = Release|Any CPU - {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Checked|x64.ActiveCfg = Debug|Any CPU - {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0}.Checked|x86.ActiveCfg = Debug|Any CPU + {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Checked|x64.ActiveCfg = Debug|Any CPU + {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Checked|x86.ActiveCfg = Debug|Any CPU {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -132,9 +141,9 @@ Global {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Release|x64.Build.0 = Release|Any CPU {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Release|x86.ActiveCfg = Release|Any CPU {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Release|x86.Build.0 = Release|Any CPU - {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Checked|x64.ActiveCfg = Debug|Any CPU - {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3}.Checked|x86.ActiveCfg = Debug|Any CPU + {E8E31BA1-DC94-40F1-9606-6922941582DC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {E8E31BA1-DC94-40F1-9606-6922941582DC}.Checked|x64.ActiveCfg = Debug|Any CPU + {E8E31BA1-DC94-40F1-9606-6922941582DC}.Checked|x86.ActiveCfg = Debug|Any CPU {E8E31BA1-DC94-40F1-9606-6922941582DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E8E31BA1-DC94-40F1-9606-6922941582DC}.Debug|Any CPU.Build.0 = Debug|Any CPU {E8E31BA1-DC94-40F1-9606-6922941582DC}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -147,9 +156,9 @@ Global {E8E31BA1-DC94-40F1-9606-6922941582DC}.Release|x64.Build.0 = Release|Any CPU {E8E31BA1-DC94-40F1-9606-6922941582DC}.Release|x86.ActiveCfg = Release|Any CPU {E8E31BA1-DC94-40F1-9606-6922941582DC}.Release|x86.Build.0 = Release|Any CPU - {E8E31BA1-DC94-40F1-9606-6922941582DC}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {E8E31BA1-DC94-40F1-9606-6922941582DC}.Checked|x64.ActiveCfg = Debug|Any CPU - {E8E31BA1-DC94-40F1-9606-6922941582DC}.Checked|x86.ActiveCfg = Debug|Any CPU + {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Checked|x64.ActiveCfg = Debug|Any CPU + {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Checked|x86.ActiveCfg = Debug|Any CPU {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Debug|Any CPU.Build.0 = Debug|Any CPU {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -162,9 +171,9 @@ Global {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Release|x64.Build.0 = Release|Any CPU {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Release|x86.ActiveCfg = Release|Any CPU {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Release|x86.Build.0 = Release|Any CPU - {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Checked|x64.ActiveCfg = Debug|Any CPU - {08FEC21B-875E-499A-B5CA-7D921B7A484B}.Checked|x86.ActiveCfg = Debug|Any CPU + {54134937-585D-48D5-AB60-79493BE2C9E7}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {54134937-585D-48D5-AB60-79493BE2C9E7}.Checked|x64.ActiveCfg = Debug|Any CPU + {54134937-585D-48D5-AB60-79493BE2C9E7}.Checked|x86.ActiveCfg = Debug|Any CPU {54134937-585D-48D5-AB60-79493BE2C9E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54134937-585D-48D5-AB60-79493BE2C9E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {54134937-585D-48D5-AB60-79493BE2C9E7}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -177,9 +186,9 @@ Global {54134937-585D-48D5-AB60-79493BE2C9E7}.Release|x64.Build.0 = Release|Any CPU {54134937-585D-48D5-AB60-79493BE2C9E7}.Release|x86.ActiveCfg = Release|Any CPU {54134937-585D-48D5-AB60-79493BE2C9E7}.Release|x86.Build.0 = Release|Any CPU - {54134937-585D-48D5-AB60-79493BE2C9E7}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {54134937-585D-48D5-AB60-79493BE2C9E7}.Checked|x64.ActiveCfg = Debug|Any CPU - {54134937-585D-48D5-AB60-79493BE2C9E7}.Checked|x86.ActiveCfg = Debug|Any CPU + {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Checked|x64.ActiveCfg = Debug|Any CPU + {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Checked|x86.ActiveCfg = Debug|Any CPU {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Debug|Any CPU.Build.0 = Debug|Any CPU {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -192,9 +201,9 @@ Global {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Release|x64.Build.0 = Release|Any CPU {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Release|x86.ActiveCfg = Release|Any CPU {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Release|x86.Build.0 = Release|Any CPU - {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Checked|x64.ActiveCfg = Debug|Any CPU - {53A0D29E-8633-4FF8-AB0E-86EF331DD379}.Checked|x86.ActiveCfg = Debug|Any CPU + {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Checked|x64.ActiveCfg = Debug|Any CPU + {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Checked|x86.ActiveCfg = Debug|Any CPU {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Debug|Any CPU.Build.0 = Debug|Any CPU {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -207,27 +216,46 @@ Global {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Release|x64.Build.0 = Release|Any CPU {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Release|x86.ActiveCfg = Release|Any CPU {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Release|x86.Build.0 = Release|Any CPU - {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Checked|Any CPU.ActiveCfg = Debug|Any CPU - {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Checked|x64.ActiveCfg = Debug|Any CPU - {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75}.Checked|x86.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Checked|Any CPU.Build.0 = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Checked|x64.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Checked|x64.Build.0 = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Checked|x86.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Checked|x86.Build.0 = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Debug|x64.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Debug|x64.Build.0 = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Debug|x86.ActiveCfg = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Debug|x86.Build.0 = Debug|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Release|Any CPU.Build.0 = Release|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Release|x64.ActiveCfg = Release|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Release|x64.Build.0 = Release|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Release|x86.ActiveCfg = Release|Any CPU + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {BEACD8A1-3C09-450E-931F-561D44986BFB} = {3358539E-11F4-4294-8D94-A9A31E9F47F5} - {53A0D29E-8633-4FF8-AB0E-86EF331DD379} = {3358539E-11F4-4294-8D94-A9A31E9F47F5} {CD477FC8-AF27-46A2-A451-4A2D4C11600D} = {DB513386-C3B1-4891-92E1-1BE5D38562DC} - {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75} = {DB513386-C3B1-4891-92E1-1BE5D38562DC} {54E0AB0F-EB35-49AB-9A38-7C09FD7EC373} = {1445280F-1F07-45C3-93BC-D4025720B5FE} + {3156246A-939D-4232-90B3-FDE03A14BA90} = {6A32653B-8FAC-4B01-A0E0-E6379DE90A1A} {B5732A3E-15DB-4BAA-A44C-41A7C29C68F0} = {1445280F-1F07-45C3-93BC-D4025720B5FE} {3A5BA59D-C1B6-492F-BF2C-FDE2C274E9B3} = {1445280F-1F07-45C3-93BC-D4025720B5FE} - {3156246A-939D-4232-90B3-FDE03A14BA90} = {6A32653B-8FAC-4B01-A0E0-E6379DE90A1A} {E8E31BA1-DC94-40F1-9606-6922941582DC} = {6A32653B-8FAC-4B01-A0E0-E6379DE90A1A} {08FEC21B-875E-499A-B5CA-7D921B7A484B} = {6A32653B-8FAC-4B01-A0E0-E6379DE90A1A} {54134937-585D-48D5-AB60-79493BE2C9E7} = {6A32653B-8FAC-4B01-A0E0-E6379DE90A1A} + {53A0D29E-8633-4FF8-AB0E-86EF331DD379} = {3358539E-11F4-4294-8D94-A9A31E9F47F5} + {71ACA28C-A4F1-4A07-A1B3-39DB86C11A75} = {DB513386-C3B1-4891-92E1-1BE5D38562DC} + {2EBEBCB5-9AC0-49E7-BCE3-C8A73E068CAE} = {DB513386-C3B1-4891-92E1-1BE5D38562DC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6124532E-4272-44A6-954B-A7B0FF9D694A} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{beacd8a1-3c09-450e-931f-561d44986bfb}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Threading.ThreadPool/tests/WindowsThreadPool/System.Threading.ThreadPool.WindowsThreadPool.Tests.csproj b/src/libraries/System.Threading.ThreadPool/tests/WindowsThreadPool/System.Threading.ThreadPool.WindowsThreadPool.Tests.csproj new file mode 100644 index 0000000000000..f39bea77d0fbc --- /dev/null +++ b/src/libraries/System.Threading.ThreadPool/tests/WindowsThreadPool/System.Threading.ThreadPool.WindowsThreadPool.Tests.csproj @@ -0,0 +1,21 @@ + + + true + + $(NetCoreAppCurrent)-windows + true + + + + + + + + + + + + + From 51df61eea6d375c8ee37b2d76d1a8924a1f6753f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2023 19:42:05 -0700 Subject: [PATCH 148/168] Updated System.Threading.ThreadPool.sln --- .../System.Threading.ThreadPool.sln | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln index 7dd75710c3bdc..ab6f29d1f0008 100644 --- a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln +++ b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln @@ -1,7 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33712.159 +Microsoft Visual Studio Solution File, Format Version 12.00 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{BEACD8A1-3C09-450E-931F-561D44986BFB}" EndProject From 8c93a1446f94ce2f9c01ce76a662d3ad9cb7e16a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 May 2023 20:20:41 -0700 Subject: [PATCH 149/168] Remove MinimumVisualStudioVersion --- .../System.Threading.ThreadPool/System.Threading.ThreadPool.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln index ab6f29d1f0008..7d63896b0ec06 100644 --- a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln +++ b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln @@ -1,5 +1,4 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{BEACD8A1-3C09-450E-931F-561D44986BFB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{CD477FC8-AF27-46A2-A451-4A2D4C11600D}" From 059f1a15da619406dbcaf234a071acc84071ebb9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 May 2023 12:12:30 -0700 Subject: [PATCH 150/168] Set System.Threading.ThreadPool.UseWindowsThreadPool to false in System.Threading.ThreadPool.Tests.csproj --- .../tests/System.Threading.ThreadPool.Tests.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj b/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj index d3359ebc0229e..0cb21c9d38492 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj +++ b/src/libraries/System.Threading.ThreadPool/tests/System.Threading.ThreadPool.Tests.csproj @@ -14,4 +14,7 @@ + + + From 553f226e494ae6eeb1d6e4f8224845479d6135fa Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 May 2023 14:29:03 -0700 Subject: [PATCH 151/168] Disable tests that call SetMinThreads/SetMaxThreads for WindowsThreadPool --- .../tests/ThreadPoolTests.cs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index dfef12aa200bd..68c8dec542eb9 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -42,7 +42,7 @@ public static IEnumerable TwoBools() => // Tests concurrent calls to ThreadPool.SetMinThreads. Invoked from the static constructor. private static void ConcurrentInitializeTest() { - RemoteExecutor.Invoke(() => + RemoteExecutor.Invoke((UsePortableThreadPool) => { int processorCount = Environment.ProcessorCount; var countdownEvent = new CountdownEvent(processorCount); @@ -51,7 +51,10 @@ private static void ConcurrentInitializeTest() { countdownEvent.Signal(); countdownEvent.Wait(ThreadTestHelpers.UnexpectedTimeoutMilliseconds); - Assert.True(ThreadPool.SetMinThreads(processorCount, processorCount)); + if (Boolean.Parse(UsePortableThreadPool)) + { + Assert.True(ThreadPool.SetMinThreads(processorCount, processorCount)); + } }; var waitForThreadArray = new Action[processorCount]; @@ -66,7 +69,7 @@ private static void ConcurrentInitializeTest() { waitForThread(); } - }).Dispose(); + }, UsePortableThreadPool.ToString()).Dispose(); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] @@ -97,7 +100,7 @@ public static void GetAvailableThreadsTest() Assert.True(c <= maxc); } - [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported), nameof(UsePortableThreadPool))] [ActiveIssue("https://github.com/mono/mono/issues/15164", TestRuntimes.Mono)] public static void SetMinMaxThreadsTest() { @@ -161,7 +164,7 @@ public static void SetMinMaxThreadsTest() }).Dispose(); } - [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported), nameof(UsePortableThreadPool))] public static void SetMinMaxThreadsTest_ChangedInDotNetCore() { RemoteExecutor.Invoke(() => @@ -222,7 +225,7 @@ private static void VerifyMaxThreads(int expectedMaxw, int expectedMaxc) Assert.Equal(expectedMaxc, maxc); } - [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported), nameof(UsePortableThreadPool))] public static void SetMinThreadsTo0Test() { RemoteExecutor.Invoke(() => @@ -701,7 +704,7 @@ public static void WorkQueueDepletionTest() done.CheckedWait(); } - [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported), nameof(UsePortableThreadPool))] public static void WorkerThreadStateResetTest() { RemoteExecutor.Invoke(() => @@ -785,7 +788,7 @@ public static void WorkerThreadStateResetTest() }).Dispose(); } - [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported), nameof(UsePortableThreadPool))] public static void SettingMinWorkerThreadsWillCreateThreadsUpToMinimum() { RemoteExecutor.Invoke(() => @@ -1105,7 +1108,7 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) } } - [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported), nameof(UsePortableThreadPool))] public void ThreadPoolMinMaxThreadsEventTest() { // The ThreadPoolMinMaxThreads event is fired when the ThreadPool is created @@ -1158,5 +1161,13 @@ public void ThreadPoolMinMaxThreadsEventTest() public static bool IsThreadingAndRemoteExecutorSupported => PlatformDetection.IsThreadingSupported && RemoteExecutor.IsSupported; + + private static bool getUseWindowsThreadPool() + { + AppContext.TryGetSwitch("System.Threading.ThreadPool.UseWindowsThreadPool", out bool UseWindowsThreadPool); + return UseWindowsThreadPool; + } + + private static bool UsePortableThreadPool { get; } = !getUseWindowsThreadPool(); } } From 156f7e5d8d1267749b468117ccdc0eeb5e2aea98 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 May 2023 14:43:03 -0700 Subject: [PATCH 152/168] Disabling MetricTest() for Windows Threadpool --- .../System.Threading.ThreadPool/tests/ThreadPoolTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index 68c8dec542eb9..d3fe4c5e2a7bc 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -436,7 +436,8 @@ public void Execute() { } public static bool IsMetricsTestSupported => Environment.ProcessorCount >= 3 && IsThreadingAndRemoteExecutorSupported; - [ConditionalFact(nameof(IsMetricsTestSupported))] + // Temporarily disabling for Windows Threadpool, it fails but still unsure why + [ConditionalFact(nameof(IsMetricsTestSupported), nameof(UsePortableThreadPool))] public void MetricsTest() { RemoteExecutor.Invoke(() => From 37c096a3aa07aabf1ac75e9485abfa50abf2ad2a Mon Sep 17 00:00:00 2001 From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com> Date: Wed, 31 May 2023 16:36:45 -0700 Subject: [PATCH 153/168] Update src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs Co-authored-by: Jan Kotas --- .../src/System/Threading/ThreadPool.NonCoreCLR.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs index bad11c3cb9eaf..89fbceef32bfc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs @@ -13,15 +13,5 @@ namespace System.Threading public static partial class ThreadPool { private static bool EnsureConfigInitializedCore() => true; - -#pragma warning disable IDE0060 - internal static bool CanSetMinIOCompletionThreads(int ioCompletionThreads) => false; - internal static bool CanSetMaxIOCompletionThreads(int ioCompletionThreads) => false; -#pragma warning restore IDE0060 - - [Conditional("unnecessary")] - internal static void SetMinIOCompletionThreads(int ioCompletionThreads) { } - [Conditional("unnecessary")] - internal static void SetMaxIOCompletionThreads(int ioCompletionThreads) { } } } From 6064280413e82108d201e9723954ac3ded712d8e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 May 2023 16:41:28 -0700 Subject: [PATCH 154/168] Remove ThreadPool.NonCoreCLR.cs --- .../src/System.Private.CoreLib.Shared.projitems | 1 - .../System/Threading/ThreadPool.NonCoreCLR.cs | 17 ----------------- .../src/System/Threading/ThreadPool.Unix.cs | 4 ++++ .../src/System/Threading/ThreadPool.Windows.cs | 4 ++++ 4 files changed, 8 insertions(+), 18 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index efac778402982..7b2358ca8e337 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2541,7 +2541,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs deleted file mode 100644 index 89fbceef32bfc..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.NonCoreCLR.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Threading -{ - // - // Portable implementation of ThreadPool - // - - public static partial class ThreadPool - { - private static bool EnsureConfigInitializedCore() => true; - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index fb7e8e20cd8fb..09281226e559e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -22,6 +22,10 @@ public static partial class ThreadPool internal static bool YieldFromDispatchLoop => false; #endif +#if !CORECLR + private static bool EnsureConfigInitializedCore() => true; +#endif + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); internal static object GetOrCreateThreadLocalCompletionCountObject() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 3fa7e50b022a0..675a5e5786723 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -51,6 +51,10 @@ public static bool BindHandle(SafeHandle osHandle) => WindowsThreadPool.BindHandle(osHandle) : BindHandlePortableCore(osHandle); +#if !CORECLR + private static bool EnsureConfigInitializedCore() => true; +#endif + internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); internal static void InitializeForThreadPoolThread() From 14b3ff02154cc9a2da56ef044a47e1ad3560a3d1 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 09:10:53 -0700 Subject: [PATCH 155/168] Remove conditions for YieldFromDispatchLoop in ThreadPool.Windows.cs --- .../src/System/Threading/ThreadPool.Windows.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 675a5e5786723..89595a1bcfc13 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -20,8 +20,6 @@ public static partial class ThreadPool UseWindowsThreadPool ? false : AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", "DOTNET_ThreadPool_EnableWorkerTracking", false); #endif - -#if !(TARGET_BROWSER && FEATURE_WASM_THREADS) // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work. // @@ -29,7 +27,6 @@ public static partial class ThreadPool // considered to be doing long-running work and change thread pool heuristics, such as slowing or halting thread // injection. internal static bool YieldFromDispatchLoop => UseWindowsThreadPool; -#endif [CLSCompliant(false)] [SupportedOSPlatform("windows")] From 01d7da3cbe6a633d038433f33a39e4994b36bf80 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 09:12:52 -0700 Subject: [PATCH 156/168] Removed EnsureConfigInitializedCore() in ThreadPool.Unix.cs --- .../src/System/Threading/ThreadPool.Unix.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 09281226e559e..127b073928f52 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -23,11 +23,9 @@ public static partial class ThreadPool #endif #if !CORECLR - private static bool EnsureConfigInitializedCore() => true; + private static bool EnsureConfigInitialized() => true; #endif - internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); - internal static object GetOrCreateThreadLocalCompletionCountObject() => PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject(); From 72c84b3e1fa4c8ed4de316c2738a06ce72dc7f3a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 09:38:31 -0700 Subject: [PATCH 157/168] Fix EnsureConfigInitialized() --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 2 +- .../src/System/Threading/ThreadPool.Windows.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 5e3cb9030a11b..3978383d866d8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -21,7 +21,7 @@ namespace System.Threading public static partial class ThreadPool { - private static bool EnsureConfigInitializedCore() + private static bool EnsureConfigInitialized() { return s_initialized; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 89595a1bcfc13..8ad8c16a441ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -49,11 +49,9 @@ public static bool BindHandle(SafeHandle osHandle) => BindHandlePortableCore(osHandle); #if !CORECLR - private static bool EnsureConfigInitializedCore() => true; + private static bool EnsureConfigInitialized() => true; #endif - internal static bool EnsureConfigInitialized() => EnsureConfigInitializedCore(); - internal static void InitializeForThreadPoolThread() { if (ThreadPool.UseWindowsThreadPool) From 4471f520fca45b9c0bb1ea6b7c8e33e0c5964dd9 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 09:57:32 -0700 Subject: [PATCH 158/168] Updated EnsureConfigInitialized() to internal --- .../src/System/Threading/ThreadPool.CoreCLR.cs | 2 +- .../src/System/Threading/ThreadPool.Unix.cs | 2 +- .../src/System/Threading/ThreadPool.Windows.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs index 3978383d866d8..e13fdeef31119 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @@ -21,7 +21,7 @@ namespace System.Threading public static partial class ThreadPool { - private static bool EnsureConfigInitialized() + internal static bool EnsureConfigInitialized() { return s_initialized; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 127b073928f52..1fe8585c4ebe0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -23,7 +23,7 @@ public static partial class ThreadPool #endif #if !CORECLR - private static bool EnsureConfigInitialized() => true; + internal static bool EnsureConfigInitialized() => true; #endif internal static object GetOrCreateThreadLocalCompletionCountObject() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 8ad8c16a441ef..c496cfb7c489f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -49,7 +49,7 @@ public static bool BindHandle(SafeHandle osHandle) => BindHandlePortableCore(osHandle); #if !CORECLR - private static bool EnsureConfigInitialized() => true; + internal static bool EnsureConfigInitialized() => true; #endif internal static void InitializeForThreadPoolThread() From e8872775afe59db9cf83269d30e138065a2f34c4 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 11:23:46 -0700 Subject: [PATCH 159/168] Enabled Windows ThreadPool on Native AOT by default --- .../nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 607028701561f..2a0293aebc93b 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -43,6 +43,7 @@ The .NET Foundation licenses this file to you under the MIT license. true false false + true @@ -255,6 +256,7 @@ The .NET Foundation licenses this file to you under the MIT license. + From 21375a81f6ff9f985b4e4a624d0f7ac48abaf45d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 12:11:24 -0700 Subject: [PATCH 160/168] Added windows condition to UseWindowsThreadPool in Microsoft.NETCore.Native.targets --- .../BuildIntegration/Microsoft.NETCore.Native.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 2a0293aebc93b..97b0a7407f955 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -43,7 +43,7 @@ The .NET Foundation licenses this file to you under the MIT license. true false false - true + true @@ -256,7 +256,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + From fd3e2f9aba7c829382fd93f4e9a3c4c04d04f24e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 12:28:06 -0700 Subject: [PATCH 161/168] Fixed bug --- .../nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 97b0a7407f955..c5bbf7dc451f8 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -256,7 +256,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + From 23ed1f22e6a6484c21805059a39f36513ca07cf5 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Jun 2023 14:33:23 -0700 Subject: [PATCH 162/168] Fix bug in Microsoft.NETCore.Native.targets --- .../nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index c5bbf7dc451f8..b23a4aded81bc 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -256,7 +256,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + From 36461765a829eaec8c14fb85e488c1a787e5e409 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Jun 2023 12:25:43 -0700 Subject: [PATCH 163/168] Fixed bug in Microsoft.NETCore.Native.targets + Enabled MetricsTest for WindowsThreadPool --- .../BuildIntegration/Microsoft.NETCore.Native.targets | 2 +- .../System.Threading.ThreadPool/tests/ThreadPoolTests.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index b23a4aded81bc..10665d62954d5 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -256,7 +256,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index d3fe4c5e2a7bc..17fdc8b077826 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -436,8 +436,7 @@ public void Execute() { } public static bool IsMetricsTestSupported => Environment.ProcessorCount >= 3 && IsThreadingAndRemoteExecutorSupported; - // Temporarily disabling for Windows Threadpool, it fails but still unsure why - [ConditionalFact(nameof(IsMetricsTestSupported), nameof(UsePortableThreadPool))] + [ConditionalFact(nameof(IsMetricsTestSupported))] public void MetricsTest() { RemoteExecutor.Invoke(() => @@ -541,7 +540,8 @@ public void MetricsTest() Assert.True(totalWorkCountToQueue >= 1); waitForWorkStart = true; scheduleWork(); - Assert.True(ThreadPool.ThreadCount >= totalWorkCountToQueue); + int threadCountLowerBound = UsePortableThreadPool ? totalWorkCountToQueue : 0; + Assert.True(ThreadPool.ThreadCount >= threadCountLowerBound); int runningWorkItemCount = queuedWorkCount; From 0651298f3a606c572b09f47bc6d4a706a7c63457 Mon Sep 17 00:00:00 2001 From: Eduardo Velarde Date: Fri, 2 Jun 2023 16:53:45 -0700 Subject: [PATCH 164/168] Fixed ThreadCount for WindowsThreadPool on Clr+Mono --- .../src/System/Threading/ThreadPool.Windows.cs | 6 ------ .../src/System/Threading/ThreadPoolCallbackWrapper.cs | 2 ++ .../System.Threading.ThreadPool/tests/ThreadPoolTests.cs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index c496cfb7c489f..1b4bbbc9aa10e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -58,10 +58,6 @@ internal static void InitializeForThreadPoolThread() { WindowsThreadPool.InitializeForThreadPoolThread(); } - else - { - InitializeForThreadPoolThreadPortableCore(); - } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -203,8 +199,6 @@ public static long CompletedWorkItemCount } } - private static void InitializeForThreadPoolThreadPortableCore() { } - private static RegisteredWaitHandle RegisterWaitForSingleObject( WaitHandle waitObject, WaitOrTimerCallback callBack, diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs index d6829bf3615a9..885db3793d524 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs @@ -13,6 +13,8 @@ public static ThreadPoolCallbackWrapper Enter() if (!currentThread.IsThreadPoolThread) { currentThread.IsThreadPoolThread = true; + // when using the Windows Threadpool, this is needed to increment the ThreadCount + ThreadPool.InitializeForThreadPoolThread(); } return new ThreadPoolCallbackWrapper { diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index 17fdc8b077826..3dc91878cdcce 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -540,7 +540,7 @@ public void MetricsTest() Assert.True(totalWorkCountToQueue >= 1); waitForWorkStart = true; scheduleWork(); - int threadCountLowerBound = UsePortableThreadPool ? totalWorkCountToQueue : 0; + int threadCountLowerBound = UsePortableThreadPool ? totalWorkCountToQueue : 1; Assert.True(ThreadPool.ThreadCount >= threadCountLowerBound); int runningWorkItemCount = queuedWorkCount; From 56be9f11ca037ca4a7a6be7340a615fbdff990b1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Jun 2023 08:24:37 -0700 Subject: [PATCH 165/168] Fixed InitializeForThreadPoolThread --- .../src/System/Threading/ThreadPoolCallbackWrapper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs index 885db3793d524..664049c61ed4e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolCallbackWrapper.cs @@ -13,8 +13,10 @@ public static ThreadPoolCallbackWrapper Enter() if (!currentThread.IsThreadPoolThread) { currentThread.IsThreadPoolThread = true; +#if TARGET_WINDOWS // when using the Windows Threadpool, this is needed to increment the ThreadCount ThreadPool.InitializeForThreadPoolThread(); +#endif } return new ThreadPoolCallbackWrapper { From 2d2a640227db31e5e7b3bc8da935f1d35bad73bb Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Jun 2023 23:26:13 -0700 Subject: [PATCH 166/168] Fixed review comments --- .../System/Threading/RegisteredWaitHandle.Unix.cs | 2 +- .../System/Threading/RegisteredWaitHandle.Windows.cs | 3 --- .../src/System/Threading/ThreadPool.Unix.cs | 4 ++-- .../src/System/Threading/ThreadPool.Windows.cs | 2 +- .../tests/ThreadPoolTests.cs | 12 ++++++------ 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs index f9401f1ac520b..cd42640816240 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs @@ -11,7 +11,7 @@ namespace System.Threading /// An object representing the registration of a via . /// #if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + [UnsupportedOSPlatform("browser")] #endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs index 964391ee06b98..7f1d1309f1056 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Windows.cs @@ -10,9 +10,6 @@ namespace System.Threading { -#if !FEATURE_WASM_THREADS - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] -#endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { private SafeWaitHandle? _waitHandle; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 1fe8585c4ebe0..3c5888a6f80db 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -13,10 +13,10 @@ public static partial class ThreadPool private const bool IsWorkerTrackingEnabledInConfig = false; #else private static readonly bool IsWorkerTrackingEnabledInConfig = - AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false); + AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", "DOTNET_ThreadPool_EnableWorkerTracking"); #endif -#if CORECLR || !(TARGET_BROWSER && FEATURE_WASM_THREADS) +#if !(TARGET_BROWSER && FEATURE_WASM_THREADS) // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work. internal static bool YieldFromDispatchLoop => false; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs index 1b4bbbc9aa10e..6882b0482c017 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Windows.cs @@ -17,7 +17,7 @@ public static partial class ThreadPool private const bool IsWorkerTrackingEnabledInConfig = false; #else private static readonly bool IsWorkerTrackingEnabledInConfig = - UseWindowsThreadPool ? false : AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", "DOTNET_ThreadPool_EnableWorkerTracking", false); + UseWindowsThreadPool ? false : AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", "DOTNET_ThreadPool_EnableWorkerTracking"); #endif // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index 3dc91878cdcce..a9b09f53895db 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -42,7 +42,7 @@ public static IEnumerable TwoBools() => // Tests concurrent calls to ThreadPool.SetMinThreads. Invoked from the static constructor. private static void ConcurrentInitializeTest() { - RemoteExecutor.Invoke((UsePortableThreadPool) => + RemoteExecutor.Invoke((usePortableThreadPool) => { int processorCount = Environment.ProcessorCount; var countdownEvent = new CountdownEvent(processorCount); @@ -51,7 +51,7 @@ private static void ConcurrentInitializeTest() { countdownEvent.Signal(); countdownEvent.Wait(ThreadTestHelpers.UnexpectedTimeoutMilliseconds); - if (Boolean.Parse(UsePortableThreadPool)) + if (Boolean.Parse(usePortableThreadPool)) { Assert.True(ThreadPool.SetMinThreads(processorCount, processorCount)); } @@ -1163,12 +1163,12 @@ public void ThreadPoolMinMaxThreadsEventTest() public static bool IsThreadingAndRemoteExecutorSupported => PlatformDetection.IsThreadingSupported && RemoteExecutor.IsSupported; - private static bool getUseWindowsThreadPool() + private static bool GetUseWindowsThreadPool() { - AppContext.TryGetSwitch("System.Threading.ThreadPool.UseWindowsThreadPool", out bool UseWindowsThreadPool); - return UseWindowsThreadPool; + AppContext.TryGetSwitch("System.Threading.ThreadPool.UseWindowsThreadPool", out bool useWindowsThreadPool); + return useWindowsThreadPool; } - private static bool UsePortableThreadPool { get; } = !getUseWindowsThreadPool(); + private static bool UsePortableThreadPool { get; } = !GetUseWindowsThreadPool(); } } From eb70d5376fd1e8d48ccb42320a132213b4f57e7d Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 5 Jun 2023 23:53:19 -0700 Subject: [PATCH 167/168] Removed [UnsupportedOSPlatform(browser)] from RegisteredWaitHandle.Unix.cs --- .../src/System/Threading/RegisteredWaitHandle.Unix.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs index cd42640816240..57adb42986635 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Unix.cs @@ -10,9 +10,6 @@ namespace System.Threading /// /// An object representing the registration of a via . /// -#if !FEATURE_WASM_THREADS - [UnsupportedOSPlatform("browser")] -#endif public sealed partial class RegisteredWaitHandle : MarshalByRefObject { private SafeWaitHandle? _waitHandle; From efa64201a0a561003cb43ed3cc4ed63ee6d57d8a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Jun 2023 00:15:12 -0700 Subject: [PATCH 168/168] Removed CoreLib's reference in System.Threading.ThreadPool.sln --- .../System.Threading.ThreadPool.sln | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln index 7d63896b0ec06..0a1f1ce1febd2 100644 --- a/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln +++ b/src/libraries/System.Threading.ThreadPool/System.Threading.ThreadPool.sln @@ -251,7 +251,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6124532E-4272-44A6-954B-A7B0FF9D694A} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{beacd8a1-3c09-450e-931f-561d44986bfb}*SharedItemsImports = 5 - EndGlobalSection EndGlobal