diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 9e8d9f7fe732..986d273addc1 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -398,11 +398,9 @@ - - @@ -488,7 +486,6 @@ - diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CloseHandle.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CloseHandle.cs index 96ed922a846d..ff41f939f1c6 100644 --- a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CloseHandle.cs +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.CloseHandle.cs @@ -10,7 +10,6 @@ internal partial class Interop internal partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseHandle(IntPtr handle); } } diff --git a/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeWaitHandle.Windows.cs b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeWaitHandle.Windows.cs new file mode 100644 index 000000000000..66f17f0af709 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeWaitHandle.Windows.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Win32.SafeHandles +{ + public sealed partial class SafeWaitHandle : SafeHandleZeroOrMinusOneIsInvalid + { + protected override bool ReleaseHandle() => Interop.Kernel32.CloseHandle(handle); + } +} diff --git a/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs new file mode 100644 index 000000000000..edb0cdfcafe7 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Microsoft/Win32/SafeHandles/SafeWaitHandle.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. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.Win32.SafeHandles +{ + public sealed partial class SafeWaitHandle : SafeHandleZeroOrMinusOneIsInvalid + { + // Called by P/Invoke marshaler + private SafeWaitHandle() : base(true) + { + } + + public SafeWaitHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) + { + SetHandle(existingHandle); + } + } +} diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index da36623eaff7..59a3074ab8b7 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -26,6 +26,7 @@ + @@ -580,6 +581,7 @@ + @@ -594,6 +596,7 @@ + @@ -724,7 +727,6 @@ - @@ -810,6 +812,8 @@ + + diff --git a/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs b/src/System.Private.CoreLib/shared/System/Threading/CancellationToken.cs similarity index 89% rename from src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs rename to src/System.Private.CoreLib/shared/System/Threading/CancellationToken.cs index cffaf22cd48d..68e6971a5426 100644 --- a/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/CancellationToken.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.CompilerServices; namespace System.Threading { @@ -29,14 +30,14 @@ namespace System.Threading [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")] public readonly struct CancellationToken { - private readonly static Action s_actionToActionObjShunt = obj => ((Action)obj)(); - // The backing TokenSource. // if null, it implicitly represents the same thing as new CancellationToken(false). // When required, it will be instantiated to reflect this. private readonly CancellationTokenSource _source; //!! warning. If more fields are added, the assumptions in CreateLinkedToken may no longer be valid + private readonly static Action s_actionToActionObjShunt = obj => ((Action)obj)(); + /// /// Returns an empty CancellationToken value. /// @@ -136,7 +137,7 @@ public CancellationTokenRegistration Register(Action callback) => Register( s_actionToActionObjShunt, callback ?? throw new ArgumentNullException(nameof(callback)), - useSyncContext: false, + useSynchronizationContext: false, useExecutionContext: true); /// @@ -189,7 +190,7 @@ public CancellationTokenRegistration Register(Action callback, bool useSynchroni /// be used to unregister the callback. /// is null. public CancellationTokenRegistration Register(Action callback, object state) => - Register(callback, state, useSyncContext: false, useExecutionContext: true); + Register(callback, state, useSynchronizationContext: false, useExecutionContext: true); /// /// Registers a delegate that will be called when this @@ -222,19 +223,43 @@ public CancellationTokenRegistration Register(Action callback, object st // helper for internal registration needs that don't require an EC capture (e.g. creating linked token sources, or registering unstarted TPL tasks) // has a handy signature, and skips capturing execution context. internal CancellationTokenRegistration InternalRegisterWithoutEC(Action callback, object state) => - Register(callback, state, useSyncContext: false, useExecutionContext: false); + Register(callback, state, useSynchronizationContext: false, useExecutionContext: false); - // the real work.. - private CancellationTokenRegistration Register(Action callback, object state, bool useSyncContext, bool useExecutionContext) + /// + /// Registers a delegate that will be called when this + /// CancellationToken is canceled. + /// + /// + /// + /// If this token is already in the canceled state, the + /// delegate will be run immediately and synchronously. Any exception the delegate generates will be + /// propagated out of this method call. + /// + /// + /// The delegate to be executed when the CancellationToken is canceled. + /// The state to pass to the when the delegate is invoked. This may be null. + /// A Boolean value that indicates whether to capture + /// the current SynchronizationContext and use it + /// when invoking the . + /// The instance that can + /// be used to unregister the callback. + /// is null. + /// The associated CancellationTokenSource has been disposed. +#if CORECLR + private +#else + [MethodImpl(MethodImplOptions.NoInlining)] + public +#endif + CancellationTokenRegistration Register(Action callback, object state, bool useSynchronizationContext, bool useExecutionContext) { if (callback == null) - { throw new ArgumentNullException(nameof(callback)); - } CancellationTokenSource source = _source; return source != null ? - source.InternalRegister(callback, state, useSyncContext ? SynchronizationContext.Current : null, useExecutionContext ? ExecutionContext.Capture() : null) : + source.InternalRegister(callback, state, useSynchronizationContext ? SynchronizationContext.Current : null, useExecutionContext ? ExecutionContext.Capture() : null) : default; // Nothing to do for tokens than can never reach the canceled state. Give back a dummy registration. } @@ -305,9 +330,7 @@ private CancellationTokenRegistration Register(Action callback, object s public void ThrowIfCancellationRequested() { if (IsCancellationRequested) - { ThrowOperationCanceledException(); - } } // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested diff --git a/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs b/src/System.Private.CoreLib/shared/System/Threading/SemaphoreSlim.cs similarity index 97% rename from src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs rename to src/System.Private.CoreLib/shared/System/Threading/SemaphoreSlim.cs index 9001fcd499d0..6dca573ef991 100644 --- a/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/SemaphoreSlim.cs @@ -2,25 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// A lightweight semahore class that contains the basic semaphore functions plus some useful functions like interrupt -// and wait handle exposing to allow waiting on multiple semaphores. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - using System; using System.Collections.Generic; using System.Diagnostics; -using System.Security; using System.Runtime.InteropServices; using System.Threading.Tasks; -// The class will be part of the current System.Threading namespace - namespace System.Threading { /// @@ -77,7 +65,7 @@ public class SemaphoreSlim : IDisposable private TaskNode m_asyncTail; // A pre-completed task with Result==true - private readonly static Task s_trueTask = + private static readonly Task s_trueTask = new Task(false, true, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default); // A pre-completed task with Result==false private readonly static Task s_falseTask = @@ -97,8 +85,9 @@ void IThreadPoolWorkItem.ExecuteWorkItem() bool setSuccessfully = TrySetResult(true); Debug.Assert(setSuccessfully, "Should have been able to complete task"); } - - void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ } +#if CORECLR + void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ } +#endif } #endregion @@ -238,7 +227,7 @@ public void Wait(CancellationToken cancellationToken) /// otherwise, false. /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than . + /// than . public bool Wait(TimeSpan timeout) { // Validate the timeout @@ -267,7 +256,7 @@ public bool Wait(TimeSpan timeout) /// otherwise, false. /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than . + /// than . /// was canceled. public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) { @@ -348,8 +337,8 @@ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) try { // Perf: first spin wait for the count to be positive. - // This additional amount of spinwaiting in addition - // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios. + // This additional amount of spinwaiting in addition + // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios. if (m_currentCount == 0) { // Monitor.Enter followed by Monitor.Wait is much more expensive than waiting on an event as it involves another @@ -369,7 +358,6 @@ public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) } } } - // entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot // clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters. try { } @@ -487,7 +475,6 @@ private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, Ca return false; } } - // ** the actual wait ** bool waitSuccessful = Monitor.Wait(m_lockObj, remainingWaitMilliseconds); @@ -578,7 +565,7 @@ public Task WaitAsync(int millisecondsTimeout) /// /// /// is a negative number other than -1 milliseconds, which represents - /// an infinite time-out -or- timeout is greater than . + /// an infinite time-out -or- timeout is greater than . /// public Task WaitAsync(TimeSpan timeout) { @@ -599,7 +586,7 @@ public Task WaitAsync(TimeSpan timeout) /// /// /// is a negative number other than -1 milliseconds, which represents - /// an infinite time-out -or- timeout is greater than . + /// an infinite time-out -or- timeout is greater than . /// public Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken) { @@ -920,7 +907,7 @@ protected virtual void Dispose(bool disposing) { if (m_waitHandle != null) { - m_waitHandle.Close(); + m_waitHandle.Dispose(); m_waitHandle = null; } m_lockObj = null; @@ -929,8 +916,6 @@ protected virtual void Dispose(bool disposing) } } - - /// /// Private helper method to wake up waiters when a cancellationToken gets canceled. /// @@ -956,7 +941,6 @@ private void CheckDispose() throw new ObjectDisposedException(null, SR.SemaphoreSlim_Disposed); } } - #endregion } } diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs b/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs deleted file mode 100644 index 1141e6d027ff..000000000000 --- a/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.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. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** A wrapper for Win32 events (mutexes, auto reset events, and -** manual reset events). Used by WaitHandle. -** -** -===========================================================*/ - -using System; -using System.Security; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.Versioning; -using Microsoft.Win32; -using System.Threading; - -namespace Microsoft.Win32.SafeHandles -{ - public sealed class SafeWaitHandle : SafeHandleZeroOrMinusOneIsInvalid - { - // Called by P/Invoke marshaler - private SafeWaitHandle() : base(true) - { - } - - public SafeWaitHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) - { - SetHandle(existingHandle); - } - - override protected bool ReleaseHandle() - { - return Win32Native.CloseHandle(handle); - } - } -} diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs b/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs index a53ec792295a..d1a92a3e0f79 100644 --- a/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs +++ b/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs @@ -276,8 +276,6 @@ internal static bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer) internal static extern uint SysStringByteLen(IntPtr bstr); #endif - [DllImport(Interop.Libraries.Kernel32, SetLastError = true)] - internal static extern bool CloseHandle(IntPtr handle); [DllImport(Interop.Libraries.Kernel32, SetLastError = true)] internal static extern unsafe int WriteFile(SafeFileHandle handle, byte* bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero); diff --git a/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index d3c811348064..7f41d250b668 100644 --- a/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -725,8 +725,6 @@ public static CancellationTokenSource CreateLinkedTokenSource(params Cancellatio } } - - /// /// Wait for a single callback to complete (or, more specifically, to not be running). /// It is ok to call this method if the callback has already finished. @@ -790,17 +788,17 @@ private sealed class LinkedNCancellationTokenSource : CancellationTokenSource { internal static readonly Action s_linkedTokenCancelDelegate = s => ((CancellationTokenSource)s).NotifyCancellation(throwOnFirstException: false); // skip ThrowIfDisposed() check in Cancel() - private CancellationTokenRegistration[] m_linkingRegistrations; + private CancellationTokenRegistration[] _linkingRegistrations; internal LinkedNCancellationTokenSource(params CancellationToken[] tokens) { - m_linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; + _linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; for (int i = 0; i < tokens.Length; i++) { if (tokens[i].CanBeCanceled) { - m_linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this); + _linkingRegistrations[i] = tokens[i].InternalRegisterWithoutEC(s_linkedTokenCancelDelegate, this); } // Empty slots in the array will be default(CancellationTokenRegistration), which are nops to Dispose. // Based on usage patterns, such occurrences should also be rare, such that it's not worth resizing @@ -815,10 +813,10 @@ protected override void Dispose(bool disposing) return; } - CancellationTokenRegistration[] linkingRegistrations = m_linkingRegistrations; + CancellationTokenRegistration[] linkingRegistrations = _linkingRegistrations; if (linkingRegistrations != null) { - m_linkingRegistrations = null; // release for GC once we're done enumerating + _linkingRegistrations = null; // release for GC once we're done enumerating for (int i = 0; i < linkingRegistrations.Length; i++) { linkingRegistrations[i].Dispose();