Fixes to allow using OS-provided threadpool (#43726)
authorJan Kotas <jkotas@microsoft.com>
Fri, 23 Oct 2020 06:42:27 +0000 (23:42 -0700)
committerGitHub <noreply@github.com>
Fri, 23 Oct 2020 06:42:27 +0000 (23:42 -0700)
Backport from dotnet/runtimelab:NativeAOT

12 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs
src/coreclr/src/tools/Common/System/Collections/Generic/ArrayBuilder.cs
src/coreclr/src/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
src/coreclr/src/tools/Common/TypeSystem/Common/TypeSystemHelpers.cs
src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLock.cs
src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs
src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Portable.cs
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs
src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
src/tests/Interop/PInvoke/CustomMarshalers/CustomMarshalersTest.cs
src/tests/Interop/PInvoke/CustomMarshalers/CustomMarshalersTest.csproj

index d18e050..95b283e 100644 (file)
@@ -353,24 +353,46 @@ namespace System.Threading
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern long GetPendingUnmanagedWorkItemCount();
 
-        private static void RegisterWaitForSingleObjectCore(WaitHandle waitObject, RegisteredWaitHandle registeredWaitHandle)
+        private static RegisteredWaitHandle RegisterWaitForSingleObject(
+             WaitHandle? waitObject,
+             WaitOrTimerCallback? callBack,
+             object? state,
+             uint millisecondsTimeOutInterval,
+             bool executeOnlyOnce,
+             bool flowExecutionContext)
         {
+
+            if (waitObject == null)
+                throw new ArgumentNullException(nameof(waitObject));
+
+            if (callBack == null)
+                throw new ArgumentNullException(nameof(callBack));
+
+            RegisteredWaitHandle registeredWaitHandle = new RegisteredWaitHandle(
+                waitObject,
+                new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext),
+                (int)millisecondsTimeOutInterval,
+                !executeOnlyOnce);
+
             registeredWaitHandle.OnBeforeRegister();
 
             if (UsePortableThreadPool)
             {
                 PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle);
-                return;
+            }
+            else
+            {
+                IntPtr nativeRegisteredWaitHandle =
+                    RegisterWaitForSingleObjectNative(
+                        waitObject,
+                        registeredWaitHandle.Callback,
+                        (uint)registeredWaitHandle.TimeoutDurationMs,
+                        !registeredWaitHandle.Repeating,
+                        registeredWaitHandle);
+                registeredWaitHandle.SetNativeRegisteredWaitHandle(nativeRegisteredWaitHandle);
             }
 
-            IntPtr nativeRegisteredWaitHandle =
-                RegisterWaitForSingleObjectNative(
-                    waitObject,
-                    registeredWaitHandle.Callback,
-                    (uint)registeredWaitHandle.TimeoutDurationMs,
-                    !registeredWaitHandle.Repeating,
-                    registeredWaitHandle);
-            registeredWaitHandle.SetNativeRegisteredWaitHandle(nativeRegisteredWaitHandle);
+            return registeredWaitHandle;
         }
 
         internal static void UnsafeQueueWaitCompletion(CompleteWaitThreadPoolWorkItem completeWaitWorkItem)
index dc6b819..32023e2 100644 (file)
@@ -104,5 +104,18 @@ namespace System.Collections.Generic
 
             return false;
         }
+
+        public bool Any(Func<T, bool> func)
+        {
+            for (int i = 0; i < _count; i++)
+            {
+                if (func(_items[i]))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
     }
 }
index 7c8667a..2e23688 100644 (file)
@@ -421,9 +421,9 @@ namespace Internal.TypeSystem
 
                 largestAlignmentRequirement = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequirement);
 
-                cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, type.Context.Target
-                                                                        armAlignFromStartOfFields: true // In what appears to have been a bug in the design of the arm32 type layout code
-                                                                                                          // this portion of the layout algorithm does not layout from the start of the object
+                cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, type.Context.Target,
+                                                                        armAlignFromStartOfFields: true // In what appears to have been a bug in the design of the arm32 type layout code
+                                                                                                        // this portion of the layout algorithm does not layout from the start of the object
                                                                         );
                 offsets[fieldOrdinal] = new FieldAndOffset(field, cumulativeInstanceFieldPos);
                 cumulativeInstanceFieldPos = checked(cumulativeInstanceFieldPos + fieldSizeAndAlignment.Size);
index 10d541d..a72d9a1 100644 (file)
@@ -440,7 +440,7 @@ namespace Internal.TypeSystem
         }
 
         /// <summary>
-        /// Determines whether an object of type '<paramref name="type"/>' requires 8-byte alignment on 
+        /// Determines whether an object of type '<paramref name="type"/>' requires 8-byte alignment on
         /// 32bit ARM or 32bit Wasm architectures.
         /// </summary>
         public static bool RequiresAlign8(this TypeDesc type)
index 1c31475..4fb1fd7 100644 (file)
@@ -53,6 +53,18 @@ namespace System.Threading
             GC.SuppressFinalize(this);
         }
 
+#if DEBUG
+        public bool IsLocked
+        {
+            get
+            {
+                bool isLocked = _ownerThread == Thread.CurrentThread;
+                Debug.Assert(!isLocked || (_state & LockedMask) != 0);
+                return isLocked;
+            }
+        }
+#endif
+
         [Conditional("DEBUG")]
         public void VerifyIsLocked()
         {
index 5e43ef3..ed091b9 100644 (file)
@@ -6,6 +6,21 @@ using Microsoft.Win32.SafeHandles;
 
 namespace System.Threading
 {
+    /// <summary>
+    /// The info for a completed wait on a specific <see cref="RegisteredWaitHandle"/>.
+    /// </summary>
+    internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem
+    {
+        private RegisteredWaitHandle _registeredWaitHandle;
+        private bool _timedOut;
+
+        public CompleteWaitThreadPoolWorkItem(RegisteredWaitHandle registeredWaitHandle, bool timedOut)
+        {
+            _registeredWaitHandle = registeredWaitHandle;
+            _timedOut = timedOut;
+        }
+    }
+
     internal partial class PortableThreadPool
     {
         /// <summary>
index 6f67c19..5fea9dd 100644 (file)
 // 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
 {
+    /// <summary>
+    /// An object representing the registration of a <see cref="WaitHandle"/> via <see cref="ThreadPool.RegisterWaitForSingleObject"/>.
+    /// </summary>
+    [UnsupportedOSPlatform("browser")]
     public sealed partial class RegisteredWaitHandle : MarshalByRefObject
     {
+        internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper,
+            int millisecondsTimeout, bool repeating)
+        {
+            Handle = waitHandle.SafeWaitHandle;
+            Callback = callbackHelper;
+            TimeoutDurationMs = millisecondsTimeout;
+            Repeating = repeating;
+            if (!IsInfiniteTimeout)
+            {
+                RestartTimeout();
+            }
+        }
+
+        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();
+
+        /// <summary>
+        /// The callback to execute when the wait on <see cref="Handle"/> either times out or completes.
+        /// </summary>
+        internal _ThreadPoolWaitOrTimerCallback Callback { get; }
+
+        /// <summary>
+        /// The <see cref="SafeWaitHandle"/> that was registered.
+        /// </summary>
+        internal SafeWaitHandle Handle { get; }
+
+        /// <summary>
+        /// The time this handle times out at in ms.
+        /// </summary>
+        internal int TimeoutTimeMs { get; private set; }
+
+        internal int TimeoutDurationMs { get; }
+
+        internal bool IsInfiniteTimeout => TimeoutDurationMs == -1;
+
+        internal void RestartTimeout()
+        {
+            Debug.Assert(!IsInfiniteTimeout);
+            TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs;
+        }
+
+        /// <summary>
+        /// Whether or not the wait is a repeating wait.
+        /// </summary>
+        internal bool Repeating { get; }
+
+        /// <summary>
+        /// The <see cref="WaitHandle"/> the user passed in via <see cref="Unregister(WaitHandle)"/>.
+        /// </summary>
+        private SafeWaitHandle? UserUnregisterWaitHandle { get; set; }
+
+        private IntPtr UserUnregisterWaitHandleValue { get; set; }
+
+        private static IntPtr InvalidHandleValue => new IntPtr(-1);
+
+        internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue;
+
+        /// <summary>
+        /// The number of callbacks that are currently queued on the Thread Pool or executing.
+        /// </summary>
+        private int _numRequestedCallbacks;
+
+        /// <summary>
+        /// Notes if we need to signal the user's unregister event after all callbacks complete.
+        /// </summary>
+        private bool _signalAfterCallbacksComplete;
+
+        private bool _unregisterCalled;
+
+        private bool _unregistered;
+
+        private AutoResetEvent? _callbacksComplete;
+
+        private AutoResetEvent? _removed;
+
         /// <summary>
         /// The <see cref="PortableThreadPool.WaitThread"/> this <see cref="RegisteredWaitHandle"/> was registered on.
         /// </summary>
         internal PortableThreadPool.WaitThread? WaitThread { get; set; }
 
+#if CORECLR
         private bool UnregisterPortable(WaitHandle waitObject)
+#else
+        public bool Unregister(WaitHandle waitObject)
+#endif
         {
             // 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
@@ -79,5 +177,135 @@ namespace System.Threading
             WaitThread!.UnregisterWait(this);
             return true;
         }
+
+        /// <summary>
+        /// Signal <see cref="UserUnregisterWaitHandle"/> if it has not been signaled yet and is a valid handle.
+        /// </summary>
+        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;
+            }
+        }
+
+        /// <summary>
+        /// Perform the registered callback if the <see cref="UserUnregisterWaitHandle"/> has not been signaled.
+        /// </summary>
+        /// <param name="timedOut">Whether or not the wait timed out.</param>
+        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();
+        }
+
+        /// <summary>
+        /// Tell this handle that there is a callback queued on the thread pool for this handle.
+        /// </summary>
+        internal void RequestCallback()
+        {
+            s_callbackLock.Acquire();
+            try
+            {
+                _numRequestedCallbacks++;
+            }
+            finally
+            {
+                s_callbackLock.Release();
+            }
+        }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        internal void OnRemoveWait()
+        {
+            s_callbackLock.Acquire();
+            try
+            {
+                _removed?.Set();
+                if (_numRequestedCallbacks == 0)
+                {
+                    SignalUserWaitHandle();
+                }
+                else
+                {
+                    _signalAfterCallbacksComplete = true;
+                }
+            }
+            finally
+            {
+                s_callbackLock.Release();
+            }
+        }
+
+        /// <summary>
+        /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it.
+        /// </summary>
+        private void CompleteCallbackRequest()
+        {
+            s_callbackLock.Acquire();
+            try
+            {
+                --_numRequestedCallbacks;
+                if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete)
+                {
+                    SignalUserWaitHandle();
+                }
+            }
+            finally
+            {
+                s_callbackLock.Release();
+            }
+        }
+
+        /// <summary>
+        /// Wait for all queued callbacks and the full unregistration to complete.
+        /// </summary>
+        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;
+        }
     }
 }
index 25ed127..0806711 100644 (file)
@@ -10,20 +10,6 @@ namespace System.Threading
     // Portable implementation of ThreadPool
     //
 
-    public sealed partial class RegisteredWaitHandle : MarshalByRefObject
-    {
-        /// <summary>
-        /// Unregisters this wait handle registration from the wait threads.
-        /// </summary>
-        /// <param name="waitObject">The event to signal when the handle is unregistered.</param>
-        /// <returns>If the handle was successfully marked to be removed and the provided wait handle was set as the user provided event.</returns>
-        /// <remarks>
-        /// This method will only return true on the first call.
-        /// Passing in a wait handle with a value of -1 will result in a blocking wait, where Unregister will not return until the full unregistration is completed.
-        /// </remarks>
-        public bool Unregister(WaitHandle waitObject) => UnregisterPortable(waitObject);
-    }
-
     internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem
     {
         void IThreadPoolWorkItem.Execute() => PortableThreadPool.CompleteWait(_registeredWaitHandle, _timedOut);
@@ -38,8 +24,15 @@ namespace System.Threading
         // does not yield the thread and instead processes time-sensitive work items queued by specific APIs periodically.
         internal const bool SupportsTimeSensitiveWorkItems = true;
 
+#if CORERT
+        internal const bool EnableWorkerTracking = false;
+#else
         internal static readonly bool EnableWorkerTracking =
             AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", false);
+#endif
+
+        // Threadpool specific initialization of a new thread. Used by OS-provided threadpools. No-op for portable threadpool.
+        internal static void InitializeForThreadPoolThread() { }
 
         internal static bool CanSetMinIOCompletionThreads(int ioCompletionThreads) => true;
         internal static void SetMinIOCompletionThreads(int ioCompletionThreads) { }
@@ -110,8 +103,28 @@ namespace System.Threading
         internal static object GetOrCreateThreadLocalCompletionCountObject() =>
             PortableThreadPool.ThreadPoolInstance.GetOrCreateThreadLocalCompletionCountObject();
 
-        private static void RegisterWaitForSingleObjectCore(WaitHandle? waitObject, RegisteredWaitHandle registeredWaitHandle) =>
-            PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredWaitHandle);
+        private static RegisteredWaitHandle RegisterWaitForSingleObject(
+             WaitHandle? waitObject,
+             WaitOrTimerCallback? callBack,
+             object? state,
+             uint millisecondsTimeOutInterval,
+             bool executeOnlyOnce,
+             bool flowExecutionContext)
+        {
+            if (waitObject == null)
+                throw new ArgumentNullException(nameof(waitObject));
+
+            if (callBack == null)
+                throw new ArgumentNullException(nameof(callBack));
+
+            RegisteredWaitHandle registeredHandle = new RegisteredWaitHandle(
+                waitObject,
+                new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext),
+                (int)millisecondsTimeOutInterval,
+                !executeOnlyOnce);
+            PortableThreadPool.ThreadPoolInstance.RegisterWaitHandle(registeredHandle);
+            return registeredHandle;
+        }
 
         internal static void UnsafeQueueWaitCompletion(CompleteWaitThreadPoolWorkItem completeWaitWorkItem) =>
             UnsafeQueueUserWorkItemInternal(completeWaitWorkItem, preferLocal: false);
index 3bbb27f..0d5f291 100644 (file)
@@ -1058,255 +1058,6 @@ namespace System.Threading
         }
     }
 
-    /// <summary>
-    /// An object representing the registration of a <see cref="WaitHandle"/> via <see cref="ThreadPool.RegisterWaitForSingleObject"/>.
-    /// </summary>
-    [UnsupportedOSPlatform("browser")]
-    public sealed partial class RegisteredWaitHandle : MarshalByRefObject
-    {
-        internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallback callbackHelper,
-            int millisecondsTimeout, bool repeating)
-        {
-            Handle = waitHandle.SafeWaitHandle;
-            Callback = callbackHelper;
-            TimeoutDurationMs = millisecondsTimeout;
-            Repeating = repeating;
-            if (!IsInfiniteTimeout)
-            {
-                RestartTimeout();
-            }
-        }
-
-        private static AutoResetEvent? s_cachedEvent;
-
-        private static AutoResetEvent RentEvent()
-        {
-            AutoResetEvent? resetEvent = Interlocked.Exchange(ref s_cachedEvent, (AutoResetEvent?)null);
-            if (resetEvent == null)
-            {
-                resetEvent = new AutoResetEvent(false);
-            }
-            return resetEvent;
-        }
-
-        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();
-
-        /// <summary>
-        /// The callback to execute when the wait on <see cref="Handle"/> either times out or completes.
-        /// </summary>
-        internal _ThreadPoolWaitOrTimerCallback Callback { get; }
-
-
-        /// <summary>
-        /// The <see cref="SafeWaitHandle"/> that was registered.
-        /// </summary>
-        internal SafeWaitHandle Handle { get; }
-
-        /// <summary>
-        /// The time this handle times out at in ms.
-        /// </summary>
-        internal int TimeoutTimeMs { get; private set; }
-
-        internal int TimeoutDurationMs { get; }
-
-        internal bool IsInfiniteTimeout => TimeoutDurationMs == -1;
-
-        internal void RestartTimeout()
-        {
-            Debug.Assert(!IsInfiniteTimeout);
-            TimeoutTimeMs = Environment.TickCount + TimeoutDurationMs;
-        }
-
-        /// <summary>
-        /// Whether or not the wait is a repeating wait.
-        /// </summary>
-        internal bool Repeating { get; }
-
-        /// <summary>
-        /// The <see cref="WaitHandle"/> the user passed in via <see cref="Unregister(WaitHandle)"/>.
-        /// </summary>
-        private SafeWaitHandle? UserUnregisterWaitHandle { get; set; }
-
-        private IntPtr UserUnregisterWaitHandleValue { get; set; }
-
-        private static IntPtr InvalidHandleValue => new IntPtr(-1);
-
-        internal bool IsBlocking => UserUnregisterWaitHandleValue == InvalidHandleValue;
-
-        /// <summary>
-        /// The number of callbacks that are currently queued on the Thread Pool or executing.
-        /// </summary>
-        private int _numRequestedCallbacks;
-
-        /// <summary>
-        /// Notes if we need to signal the user's unregister event after all callbacks complete.
-        /// </summary>
-        private bool _signalAfterCallbacksComplete;
-
-        private bool _unregisterCalled;
-
-#pragma warning disable CS0414 // The field is assigned but its value is never used. Some runtimes may not support registered wait handles.
-        private bool _unregistered;
-#pragma warning restore CS0414
-
-        private AutoResetEvent? _callbacksComplete;
-
-        private AutoResetEvent? _removed;
-
-        /// <summary>
-        /// Signal <see cref="UserUnregisterWaitHandle"/> if it has not been signaled yet and is a valid handle.
-        /// </summary>
-        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;
-            }
-        }
-
-        /// <summary>
-        /// Perform the registered callback if the <see cref="UserUnregisterWaitHandle"/> has not been signaled.
-        /// </summary>
-        /// <param name="timedOut">Whether or not the wait timed out.</param>
-        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();
-        }
-
-        /// <summary>
-        /// Tell this handle that there is a callback queued on the thread pool for this handle.
-        /// </summary>
-        internal void RequestCallback()
-        {
-            s_callbackLock.Acquire();
-            try
-            {
-                _numRequestedCallbacks++;
-            }
-            finally
-            {
-                s_callbackLock.Release();
-            }
-        }
-
-        /// <summary>
-        /// 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.
-        /// </summary>
-        internal void OnRemoveWait()
-        {
-            s_callbackLock.Acquire();
-            try
-            {
-                _removed?.Set();
-                if (_numRequestedCallbacks == 0)
-                {
-                    SignalUserWaitHandle();
-                }
-                else
-                {
-                    _signalAfterCallbacksComplete = true;
-                }
-            }
-            finally
-            {
-                s_callbackLock.Release();
-            }
-        }
-
-        /// <summary>
-        /// Reduces the number of callbacks requested. If there are no more callbacks and the user's handle is queued to be signaled, signal it.
-        /// </summary>
-        private void CompleteCallbackRequest()
-        {
-            s_callbackLock.Acquire();
-            try
-            {
-                --_numRequestedCallbacks;
-                if (_numRequestedCallbacks == 0 && _signalAfterCallbacksComplete)
-                {
-                    SignalUserWaitHandle();
-                }
-            }
-            finally
-            {
-                s_callbackLock.Release();
-            }
-        }
-
-        /// <summary>
-        /// Wait for all queued callbacks and the full unregistration to complete.
-        /// </summary>
-        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;
-        }
-    }
-
-    /// <summary>
-    /// The info for a completed wait on a specific <see cref="RegisteredWaitHandle"/>.
-    /// </summary>
-    internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem
-    {
-        private RegisteredWaitHandle _registeredWaitHandle;
-        private bool _timedOut;
-
-        public CompleteWaitThreadPoolWorkItem(RegisteredWaitHandle registeredWaitHandle, bool timedOut)
-        {
-            _registeredWaitHandle = registeredWaitHandle;
-            _timedOut = timedOut;
-        }
-    }
-
     public static partial class ThreadPool
     {
         internal const string WorkerThreadName = ".NET ThreadPool Worker";
@@ -1449,29 +1200,6 @@ namespace System.Threading
             return RegisterWaitForSingleObject(waitObject, callBack, state, (uint)tm, executeOnlyOnce, false);
         }
 
-        private static RegisteredWaitHandle RegisterWaitForSingleObject(
-             WaitHandle? waitObject,
-             WaitOrTimerCallback? callBack,
-             object? state,
-             uint millisecondsTimeOutInterval,
-             bool executeOnlyOnce,
-             bool flowExecutionContext)
-        {
-            if (waitObject == null)
-                throw new ArgumentNullException(nameof(waitObject));
-
-            if (callBack == null)
-                throw new ArgumentNullException(nameof(callBack));
-
-            RegisteredWaitHandle registeredHandle = new RegisteredWaitHandle(
-                waitObject,
-                new _ThreadPoolWaitOrTimerCallback(callBack, state, flowExecutionContext),
-                (int)millisecondsTimeOutInterval,
-                !executeOnlyOnce);
-            RegisterWaitForSingleObjectCore(waitObject, registeredHandle);
-            return registeredHandle;
-        }
-
         public static bool QueueUserWorkItem(WaitCallback callBack) =>
             QueueUserWorkItem(callBack, null);
 
index 500c694..99415e5 100644 (file)
@@ -4,30 +4,28 @@
 using System.Diagnostics;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
 using System.Diagnostics.CodeAnalysis;
 using Microsoft.Win32.SafeHandles;
 
 namespace System.Threading
 {
-    public sealed partial class RegisteredWaitHandle : MarshalByRefObject
+    [UnsupportedOSPlatform("browser")]
+    public sealed class RegisteredWaitHandle : MarshalByRefObject
     {
-        public bool Unregister(WaitHandle? waitObject)
+        internal RegisteredWaitHandle()
         {
-            throw new PlatformNotSupportedException();
         }
-    }
 
-    internal sealed partial class CompleteWaitThreadPoolWorkItem : IThreadPoolWorkItem
-    {
-        void IThreadPoolWorkItem.Execute()
+        public bool Unregister(WaitHandle? waitObject)
         {
-            Debug.Fail("Registered wait handles are currently not supported");
+            throw new PlatformNotSupportedException();
         }
     }
 
     public static partial class ThreadPool
     {
-        // Time-senstiive work items are those that may need to run ahead of normal work items at least periodically. For a
+        // Time-sensitive work items are those that may need to run ahead of normal work items at least periodically. For a
         // runtime that does not support time-sensitive work items on the managed side, the thread pool yields the thread to the
         // runtime periodically (by exiting the dispatch loop) so that the runtime may use that thread for processing
         // any time-sensitive work. For a runtime that supports time-sensitive work items on the managed side, the thread pool
@@ -94,8 +92,16 @@ namespace System.Threading
 
         internal static object? GetOrCreateThreadLocalCompletionCountObject() => null;
 
-        private static void RegisterWaitForSingleObjectCore(WaitHandle? waitObject, RegisteredWaitHandle registeredWaitHandle) =>
+        private static RegisteredWaitHandle RegisterWaitForSingleObject(
+             WaitHandle? waitObject,
+             WaitOrTimerCallback? callBack,
+             object? state,
+             uint millisecondsTimeOutInterval,
+             bool executeOnlyOnce,
+             bool flowExecutionContext)
+        {
             throw new PlatformNotSupportedException();
+        }
 
         [DynamicDependency("Callback")]
         [DynamicDependency("PumpThreadPool")]
index 39484c4..b3983b3 100644 (file)
@@ -5,11 +5,8 @@ using System;
 using System.Globalization;
 using System.Reflection;
 using System.Runtime.InteropServices;
-using System.Runtime.InteropServices.Expando;
 using TestLibrary;
 
-using Console = Internal.Console;
-
 namespace PInvokeTests
 {
     static class CustomMarshalersNative
index e436877..7ea49c1 100644 (file)
@@ -2,7 +2,6 @@
   <PropertyGroup>
     <OutputType>exe</OutputType>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
     <!-- CustomMarshalers unsupported outside of windows -->
     <CLRTestTargetUnsupported Condition="'$(TargetsWindows)' != 'true'">true</CLRTestTargetUnsupported>
   </PropertyGroup>