Moved SafeWaitHandle and cancellationToken to shared (dotnet/coreclr#18662)
authorAnirudh Agnihotry <anirudhagnihotry098@gmail.com>
Wed, 27 Jun 2018 18:36:42 +0000 (11:36 -0700)
committerJan Kotas <jkotas@microsoft.com>
Wed, 27 Jun 2018 18:36:42 +0000 (11:36 -0700)
* Moving SafeWaitHandle to shared

* Moved CancellationToken to shared

* _ remove from variable names

* Default change to Default(Token) and minor changes

* Fixing coreclr unix build

* moving semaphoreSlim to shared

* splitting safeWaitHandle to .windows and .unix

Commit migrated from https://github.com/dotnet/coreclr/commit/11c9d8590dc97b999e83c17dc992e886a6e9861b

12 files changed:
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs [deleted file]
src/coreclr/src/System.Private.CoreLib/src/Microsoft/Win32/Win32Native.cs
src/coreclr/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs [deleted file]
src/coreclr/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs
src/coreclr/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs [deleted file]
src/libraries/System.Private.CoreLib/src/Interop/Windows/Kernel32/Interop.CloseHandle.cs
src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.Windows.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Threading/CancellationToken.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs [new file with mode: 0644]

index b6917cd680ae3fcc6c4e67ce2eafa39df5c409e4..dbf45450a7ec5e2ed87ac2b1054e1c10a160cb63 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Threading\WaitHandle.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\SpinLock.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ThreadLocal.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Threading\SemaphoreSlim.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ManualResetEventSlim.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\CancellationTokenRegistration.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\CancellationTokenSource.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Threading\CancellationToken.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Tasks\future.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Tasks\FutureFactory.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\Tasks\Task.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Collections\ObjectModel\ReadOnlyDictionary.cs" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeWaitHandle.cs" />
     <Compile Condition="'$(FeatureWin32Registry)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeRegistryHandle.cs" />
   </ItemGroup>
   <ItemGroup>
diff --git a/src/coreclr/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs b/src/coreclr/src/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs
deleted file mode 100644 (file)
index 1141e6d..0000000
+++ /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);
-        }
-    }
-}
index a53ec792295a835e154fe0a5bd22fabe95ccd9d6..d1a92a3e0f797f920b6251a31b1391d3923d009b 100644 (file)
@@ -276,8 +276,6 @@ namespace Microsoft.Win32
         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/coreclr/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/CancellationToken.cs
deleted file mode 100644 (file)
index cffaf22..0000000
+++ /dev/null
@@ -1,317 +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.
-
-using System.Diagnostics;
-
-namespace System.Threading
-{
-    /// <summary>
-    /// Propagates notification that operations should be canceled.
-    /// </summary>
-    /// <remarks>
-    /// <para>
-    /// A <see cref="CancellationToken"/> may be created directly in an unchangeable canceled or non-canceled state
-    /// using the CancellationToken's constructors. However, to have a CancellationToken that can change 
-    /// from a non-canceled to a canceled state, 
-    /// <see cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see> must be used.
-    /// CancellationTokenSource exposes the associated CancellationToken that may be canceled by the source through its 
-    /// <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property. 
-    /// </para>
-    /// <para>
-    /// Once canceled, a token may not transition to a non-canceled state, and a token whose 
-    /// <see cref="CanBeCanceled"/> is false will never change to one that can be canceled.
-    /// </para>
-    /// <para>
-    /// All members of this struct are thread-safe and may be used concurrently from multiple threads.
-    /// </para>
-    /// </remarks>
-    [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
-    public readonly struct CancellationToken
-    {
-        private readonly static Action<object> 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
-
-        /// <summary>
-        /// Returns an empty CancellationToken value.
-        /// </summary>
-        /// <remarks>
-        /// The <see cref="CancellationToken"/> value returned by this property will be non-cancelable by default.
-        /// </remarks>
-        public static CancellationToken None => default;
-
-        /// <summary>
-        /// Gets whether cancellation has been requested for this token.
-        /// </summary>
-        /// <value>Whether cancellation has been requested for this token.</value>
-        /// <remarks>
-        /// <para>
-        /// This property indicates whether cancellation has been requested for this token, 
-        /// either through the token initially being constructed in a canceled state, or through
-        /// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see> 
-        /// on the token's associated <see cref="CancellationTokenSource"/>.
-        /// </para>
-        /// <para>
-        /// If this property is true, it only guarantees that cancellation has been requested.  
-        /// It does not guarantee that every registered handler
-        /// has finished executing, nor that cancellation requests have finished propagating
-        /// to all registered handlers.  Additional synchronization may be required,
-        /// particularly in situations where related objects are being canceled concurrently.
-        /// </para>
-        /// </remarks>
-        public bool IsCancellationRequested => _source != null && _source.IsCancellationRequested;
-
-        /// <summary>
-        /// Gets whether this token is capable of being in the canceled state.
-        /// </summary>
-        /// <remarks>
-        /// If CanBeCanceled returns false, it is guaranteed that the token will never transition
-        /// into a canceled state, meaning that <see cref="IsCancellationRequested"/> will never
-        /// return true.
-        /// </remarks>
-        public bool CanBeCanceled => _source != null;
-
-        /// <summary>
-        /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
-        /// <remarks>
-        /// Accessing this property causes a <see cref="T:System.Threading.WaitHandle">WaitHandle</see>
-        /// to be instantiated.  It is preferable to only use this property when necessary, and to then
-        /// dispose the associated <see cref="CancellationTokenSource"/> instance at the earliest opportunity (disposing
-        /// the source will dispose of this allocated handle).  The handle should not be closed or disposed directly.
-        /// </remarks>
-        /// <exception cref="T:System.ObjectDisposedException">The associated <see
-        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
-        public WaitHandle WaitHandle => (_source ?? CancellationTokenSource.s_neverCanceledSource).WaitHandle;
-
-        // public CancellationToken()
-        // this constructor is implicit for structs
-        //   -> this should behaves exactly as for new CancellationToken(false)
-
-        /// <summary>
-        /// Internal constructor only a CancellationTokenSource should create a CancellationToken
-        /// </summary>
-        internal CancellationToken(CancellationTokenSource source) => _source = source;
-
-        /// <summary>
-        /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
-        /// </summary>
-        /// <param name="canceled">
-        /// The canceled state for the token.
-        /// </param>
-        /// <remarks>
-        /// Tokens created with this constructor will remain in the canceled state specified
-        /// by the <paramref name="canceled"/> parameter.  If <paramref name="canceled"/> is false,
-        /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be false.
-        /// If <paramref name="canceled"/> is true,
-        /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be true. 
-        /// </remarks>
-        public CancellationToken(bool canceled) : this(canceled ? CancellationTokenSource.s_canceledSource : null)
-        {
-        }
-
-        /// <summary>
-        /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// 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.
-        /// </para>
-        /// <para>
-        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
-        /// along with the delegate and will be used when executing it.
-        /// </para>
-        /// </remarks>
-        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
-        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
-        /// be used to unregister the callback.</returns>
-        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
-        public CancellationTokenRegistration Register(Action callback) =>
-            Register(
-                s_actionToActionObjShunt,
-                callback ?? throw new ArgumentNullException(nameof(callback)),
-                useSyncContext: false,
-                useExecutionContext: true);
-
-        /// <summary>
-        /// Registers a delegate that will be called when this 
-        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// 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.
-        /// </para>
-        /// <para>
-        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
-        /// along with the delegate and will be used when executing it.
-        /// </para>
-        /// </remarks>
-        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
-        /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
-        /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
-        /// when invoking the <paramref name="callback"/>.</param>
-        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
-        /// be used to unregister the callback.</returns>
-        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
-        public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext) =>
-            Register(
-                s_actionToActionObjShunt,
-                callback ?? throw new ArgumentNullException(nameof(callback)),
-                useSynchronizationContext,
-                useExecutionContext: true);
-
-        /// <summary>
-        /// Registers a delegate that will be called when this 
-        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// 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.
-        /// </para>
-        /// <para>
-        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
-        /// along with the delegate and will be used when executing it.
-        /// </para>
-        /// </remarks>
-        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
-        /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked.  This may be null.</param>
-        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
-        /// be used to unregister the callback.</returns>
-        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
-        public CancellationTokenRegistration Register(Action<object> callback, object state) =>
-            Register(callback, state, useSyncContext: false, useExecutionContext: true);
-
-        /// <summary>
-        /// Registers a delegate that will be called when this 
-        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// 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.
-        /// </para>
-        /// <para>
-        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, 
-        /// will be captured along with the delegate and will be used when executing it.
-        /// </para>
-        /// </remarks>
-        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
-        /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked.  This may be null.</param>
-        /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
-        /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
-        /// when invoking the <paramref name="callback"/>.</param>
-        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
-        /// be used to unregister the callback.</returns>
-        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
-        /// <exception cref="T:System.ObjectDisposedException">The associated <see
-        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
-        public CancellationTokenRegistration Register(Action<object> callback, object state, bool useSynchronizationContext) =>
-            Register(callback, state, useSynchronizationContext, useExecutionContext: true);
-
-        // 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<object> callback, object state) =>
-            Register(callback, state, useSyncContext: false, useExecutionContext: false);
-
-        // the real work..
-        private CancellationTokenRegistration Register(Action<object> callback, object state, bool useSyncContext, 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) :
-                default; // Nothing to do for tokens than can never reach the canceled state. Give back a dummy registration.
-        }
-
-        /// <summary>
-        /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the 
-        /// specified token.
-        /// </summary>
-        /// <param name="other">The other <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to which to compare this
-        /// instance.</param>
-        /// <returns>True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
-        /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed 
-        /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
-        public bool Equals(CancellationToken other) => _source == other._source;
-
-        /// <summary>
-        /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the 
-        /// specified <see cref="T:System.Object"/>.
-        /// </summary>
-        /// <param name="other">The other object to which to compare this instance.</param>
-        /// <returns>True if <paramref name="other"/> is a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
-        /// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
-        /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed 
-        /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
-        /// <exception cref="T:System.ObjectDisposedException">An associated <see
-        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
-        public override bool Equals(object other) => other is CancellationToken && Equals((CancellationToken)other);
-
-        /// <summary>
-        /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
-        /// </summary>
-        /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance.</returns>
-        public override int GetHashCode() => (_source ?? CancellationTokenSource.s_neverCanceledSource).GetHashCode();
-
-        /// <summary>
-        /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
-        /// </summary>
-        /// <param name="left">The first instance.</param>
-        /// <param name="right">The second instance.</param>
-        /// <returns>True if the instances are equal; otherwise, false.</returns>
-        /// <exception cref="T:System.ObjectDisposedException">An associated <see
-        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
-        public static bool operator ==(CancellationToken left, CancellationToken right) => left.Equals(right);
-
-        /// <summary>
-        /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
-        /// </summary>
-        /// <param name="left">The first instance.</param>
-        /// <param name="right">The second instance.</param>
-        /// <returns>True if the instances are not equal; otherwise, false.</returns>
-        /// <exception cref="T:System.ObjectDisposedException">An associated <see
-        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
-        public static bool operator !=(CancellationToken left, CancellationToken right) => !left.Equals(right);
-
-        /// <summary>
-        /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
-        /// this token has had cancellation requested.
-        /// </summary>
-        /// <remarks>
-        /// This method provides functionality equivalent to:
-        /// <code>
-        /// if (token.IsCancellationRequested) 
-        ///    throw new OperationCanceledException(token);
-        /// </code>
-        /// </remarks>
-        /// <exception cref="System.OperationCanceledException">The token has had cancellation requested.</exception>
-        /// <exception cref="T:System.ObjectDisposedException">The associated <see
-        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
-        public void ThrowIfCancellationRequested()
-        {
-            if (IsCancellationRequested)
-            {
-                ThrowOperationCanceledException();
-            }
-        }
-
-        // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
-        private void ThrowOperationCanceledException() =>
-            throw new OperationCanceledException(SR.OperationCanceled, this);
-    }
-}
index d3c811348064261185b6bda1116d9ee6e2260c05..7f41d250b668b92b5112f5c5b67144b95301bc25 100644 (file)
@@ -725,8 +725,6 @@ namespace System.Threading
             }
         }
 
-
-
         /// <summary>
         /// 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 @@ namespace System.Threading
         {
             internal static readonly Action<object> 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 @@ namespace System.Threading
                     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();
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs
deleted file mode 100644 (file)
index 9001fcd..0000000
+++ /dev/null
@@ -1,962 +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 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
-{
-    /// <summary>
-    /// Limits the number of threads that can access a resource or pool of resources concurrently.
-    /// </summary>
-    /// <remarks>
-    /// <para>
-    /// The <see cref="SemaphoreSlim"/> provides a lightweight semaphore class that doesn't
-    /// use Windows kernel semaphores.
-    /// </para>
-    /// <para>
-    /// All public and protected members of <see cref="SemaphoreSlim"/> are thread-safe and may be used
-    /// concurrently from multiple threads, with the exception of Dispose, which
-    /// must only be used when all other operations on the <see cref="SemaphoreSlim"/> have
-    /// completed.
-    /// </para>
-    /// </remarks>
-    [DebuggerDisplay("Current Count = {m_currentCount}")]
-    public class SemaphoreSlim : IDisposable
-    {
-        #region Private Fields
-
-        // The semaphore count, initialized in the constructor to the initial value, every release call incremetns it
-        // and every wait call decrements it as long as its value is positive otherwise the wait will block.
-        // Its value must be between the maximum semaphore value and zero
-        private volatile int m_currentCount;
-
-        // The maximum semaphore value, it is initialized to Int.MaxValue if the client didn't specify it. it is used 
-        // to check if the count excceeded the maxi value or not.
-        private readonly int m_maxCount;
-
-        // The number of synchronously waiting threads, it is set to zero in the constructor and increments before blocking the
-        // threading and decrements it back after that. It is used as flag for the release call to know if there are
-        // waiting threads in the monitor or not.
-        private int m_waitCount;
-
-        /// <summary>
-        /// This is used to help prevent waking more waiters than necessary. It's not perfect and sometimes more waiters than
-        /// necessary may still be woken, see <see cref="WaitUntilCountOrTimeout"/>.
-        /// </summary>
-        private int m_countOfWaitersPulsedToWake;
-
-        // Dummy object used to in lock statements to protect the semaphore count, wait handle and cancelation
-        private object m_lockObj;
-
-        // Act as the semaphore wait handle, it's lazily initialized if needed, the first WaitHandle call initialize it
-        // and wait an release sets and resets it respectively as long as it is not null
-        private volatile ManualResetEvent m_waitHandle;
-
-        // Head of list representing asynchronous waits on the semaphore.
-        private TaskNode m_asyncHead;
-
-        // Tail of list representing asynchronous waits on the semaphore.
-        private TaskNode m_asyncTail;
-
-        // A pre-completed task with Result==true
-        private readonly static Task<bool> s_trueTask =
-            new Task<bool>(false, true, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default);
-        // A pre-completed task with Result==false
-        private readonly static Task<bool> s_falseTask =
-            new Task<bool>(false, false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default);
-
-        // No maximum constant
-        private const int NO_MAXIMUM = int.MaxValue;
-
-        // Task in a linked list of asynchronous waiters
-        private sealed class TaskNode : Task<bool>, IThreadPoolWorkItem
-        {
-            internal TaskNode Prev, Next;
-            internal TaskNode() : base() { }
-
-            void IThreadPoolWorkItem.ExecuteWorkItem()
-            {
-                bool setSuccessfully = TrySetResult(true);
-                Debug.Assert(setSuccessfully, "Should have been able to complete task");
-            }
-
-            void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
-        }
-        #endregion
-
-        #region Public properties
-
-        /// <summary>
-        /// Gets the current count of the <see cref="SemaphoreSlim"/>.
-        /// </summary>
-        /// <value>The current count of the <see cref="SemaphoreSlim"/>.</value>
-        public int CurrentCount
-        {
-            get { return m_currentCount; }
-        }
-
-        /// <summary>
-        /// Returns a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait on the semaphore.
-        /// </summary>
-        /// <value>A <see cref="T:System.Threading.WaitHandle"/> that can be used to wait on the
-        /// semaphore.</value>
-        /// <remarks>
-        /// A successful wait on the <see cref="AvailableWaitHandle"/> does not imply a successful wait on
-        /// the <see cref="SemaphoreSlim"/> itself, nor does it decrement the semaphore's
-        /// count. <see cref="AvailableWaitHandle"/> exists to allow a thread to block waiting on multiple
-        /// semaphores, but such a wait should be followed by a true wait on the target semaphore.
-        /// </remarks>
-        /// <exception cref="T:System.ObjectDisposedException">The <see
-        /// cref="SemaphoreSlim"/> has been disposed.</exception>
-        public WaitHandle AvailableWaitHandle
-        {
-            get
-            {
-                CheckDispose();
-
-                // Return it directly if it is not null
-                if (m_waitHandle != null)
-                    return m_waitHandle;
-
-                //lock the count to avoid multiple threads initializing the handle if it is null
-                lock (m_lockObj)
-                {
-                    if (m_waitHandle == null)
-                    {
-                        // The initial state for the wait handle is true if the count is greater than zero
-                        // false otherwise
-                        m_waitHandle = new ManualResetEvent(m_currentCount != 0);
-                    }
-                }
-                return m_waitHandle;
-            }
-        }
-
-        #endregion
-
-        #region Constructors
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SemaphoreSlim"/> class, specifying
-        /// the initial number of requests that can be granted concurrently.
-        /// </summary>
-        /// <param name="initialCount">The initial number of requests for the semaphore that can be granted
-        /// concurrently.</param>
-        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="initialCount"/>
-        /// is less than 0.</exception>
-        public SemaphoreSlim(int initialCount)
-            : this(initialCount, NO_MAXIMUM)
-        {
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SemaphoreSlim"/> class, specifying
-        /// the initial and maximum number of requests that can be granted concurrently.
-        /// </summary>
-        /// <param name="initialCount">The initial number of requests for the semaphore that can be granted
-        /// concurrently.</param>
-        /// <param name="maxCount">The maximum number of requests for the semaphore that can be granted
-        /// concurrently.</param>
-        /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="initialCount"/>
-        /// is less than 0. -or-
-        /// <paramref name="initialCount"/> is greater than <paramref name="maxCount"/>. -or-
-        /// <paramref name="maxCount"/> is less than 0.</exception>
-        public SemaphoreSlim(int initialCount, int maxCount)
-        {
-            if (initialCount < 0 || initialCount > maxCount)
-            {
-                throw new ArgumentOutOfRangeException(
-                    nameof(initialCount), initialCount, SR.SemaphoreSlim_ctor_InitialCountWrong);
-            }
-
-            //validate input
-            if (maxCount <= 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(maxCount), maxCount, SR.SemaphoreSlim_ctor_MaxCountWrong);
-            }
-
-            m_maxCount = maxCount;
-            m_lockObj = new object();
-            m_currentCount = initialCount;
-        }
-
-        #endregion
-
-        #region  Methods
-        /// <summary>
-        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>.
-        /// </summary>
-        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
-        /// disposed.</exception>
-        public void Wait()
-        {
-            // Call wait with infinite timeout
-            Wait(Timeout.Infinite, new CancellationToken());
-        }
-
-        /// <summary>
-        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, while observing a
-        /// <see cref="T:System.Threading.CancellationToken"/>.
-        /// </summary>
-        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> token to
-        /// observe.</param>
-        /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> was
-        /// canceled.</exception>
-        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
-        /// disposed.</exception>
-        public void Wait(CancellationToken cancellationToken)
-        {
-            // Call wait with infinite timeout
-            Wait(Timeout.Infinite, cancellationToken);
-        }
-
-        /// <summary>
-        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a <see
-        /// cref="T:System.TimeSpan"/> to measure the time interval.
-        /// </summary>
-        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
-        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
-        /// </param>
-        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
-        /// otherwise, false.</returns>
-        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
-        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
-        /// than <see cref="System.int.MaxValue"/>.</exception>
-        public bool Wait(TimeSpan timeout)
-        {
-            // Validate the timeout
-            long totalMilliseconds = (long)timeout.TotalMilliseconds;
-            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
-            {
-                throw new System.ArgumentOutOfRangeException(
-                    nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
-            }
-
-            // Call wait with the timeout milliseconds
-            return Wait((int)timeout.TotalMilliseconds, new CancellationToken());
-        }
-
-        /// <summary>
-        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a <see
-        /// cref="T:System.TimeSpan"/> to measure the time interval, while observing a <see
-        /// cref="T:System.Threading.CancellationToken"/>.
-        /// </summary>
-        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
-        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
-        /// </param>
-        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
-        /// observe.</param>
-        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
-        /// otherwise, false.</returns>
-        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
-        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
-        /// than <see cref="System.int.MaxValue"/>.</exception>
-        /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
-        public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
-        {
-            // Validate the timeout
-            long totalMilliseconds = (long)timeout.TotalMilliseconds;
-            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
-            {
-                throw new System.ArgumentOutOfRangeException(
-                    nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
-            }
-
-            // Call wait with the timeout milliseconds
-            return Wait((int)timeout.TotalMilliseconds, cancellationToken);
-        }
-
-        /// <summary>
-        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a 32-bit
-        /// signed integer to measure the time interval.
-        /// </summary>
-        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
-        /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
-        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
-        /// otherwise, false.</returns>
-        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
-        /// negative number other than -1, which represents an infinite time-out.</exception>
-        public bool Wait(int millisecondsTimeout)
-        {
-            return Wait(millisecondsTimeout, new CancellationToken());
-        }
-
-
-        /// <summary>
-        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>,
-        /// using a 32-bit signed integer to measure the time interval, 
-        /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
-        /// </summary>
-        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to
-        /// wait indefinitely.</param>
-        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
-        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>; otherwise, false.</returns>
-        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
-        /// which represents an infinite time-out.</exception>
-        /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
-        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
-        {
-            CheckDispose();
-
-            // Validate input
-            if (millisecondsTimeout < -1)
-            {
-                throw new ArgumentOutOfRangeException(
-                    nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
-            }
-
-            cancellationToken.ThrowIfCancellationRequested();
-
-            // Perf: Check the stack timeout parameter before checking the volatile count
-            if (millisecondsTimeout == 0 && m_currentCount == 0)
-            {
-                // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!)
-                return false;
-            }
-
-            uint startTime = 0;
-            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0)
-            {
-                startTime = TimeoutHelper.GetTime();
-            }
-
-            bool waitSuccessful = false;
-            Task<bool> asyncWaitTask = null;
-            bool lockTaken = false;
-
-            //Register for cancellation outside of the main lock.
-            //NOTE: Register/unregister inside the lock can deadlock as different lock acquisition orders could
-            //      occur for (1)this.m_lockObj and (2)cts.internalLock
-            CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this);
-            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.
-                if (m_currentCount == 0)
-                {
-                    // Monitor.Enter followed by Monitor.Wait is much more expensive than waiting on an event as it involves another
-                    // spin, contention, etc. The usual number of spin iterations that would otherwise be used here is increased to
-                    // lessen that extra expense of doing a proper wait.
-                    int spinCount = SpinWait.SpinCountforSpinBeforeWait * 4;
-                    const int Sleep1Threshold = SpinWait.Sleep1ThresholdForSpinBeforeWait * 4;
-
-                    var spinner = new SpinWait();
-                    while (spinner.Count < spinCount)
-                    {
-                        spinner.SpinOnce(Sleep1Threshold);
-
-                        if (m_currentCount != 0)
-                        {
-                            break;
-                        }
-                    }
-                }
-
-                // 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 { }
-                finally
-                {
-                    Monitor.Enter(m_lockObj, ref lockTaken);
-                    if (lockTaken)
-                    {
-                        m_waitCount++;
-                    }
-                }
-
-                // If there are any async waiters, for fairness we'll get in line behind
-                // then by translating our synchronous wait into an asynchronous one that we 
-                // then block on (once we've released the lock).
-                if (m_asyncHead != null)
-                {
-                    Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't");
-                    asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken);
-                }
-                // There are no async waiters, so we can proceed with normal synchronous waiting.
-                else
-                {
-                    // If the count > 0 we are good to move on.
-                    // If not, then wait if we were given allowed some wait duration
-
-                    OperationCanceledException oce = null;
-
-                    if (m_currentCount == 0)
-                    {
-                        if (millisecondsTimeout == 0)
-                        {
-                            return false;
-                        }
-
-                        // Prepare for the main wait...
-                        // wait until the count become greater than zero or the timeout is expired
-                        try
-                        {
-                            waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken);
-                        }
-                        catch (OperationCanceledException e) { oce = e; }
-                    }
-
-                    // Now try to acquire.  We prioritize acquisition over cancellation/timeout so that we don't
-                    // lose any counts when there are asynchronous waiters in the mix.  Asynchronous waiters
-                    // defer to synchronous waiters in priority, which means that if it's possible an asynchronous
-                    // waiter didn't get released because a synchronous waiter was present, we need to ensure
-                    // that synchronous waiter succeeds so that they have a chance to release.
-                    Debug.Assert(!waitSuccessful || m_currentCount > 0,
-                        "If the wait was successful, there should be count available.");
-                    if (m_currentCount > 0)
-                    {
-                        waitSuccessful = true;
-                        m_currentCount--;
-                    }
-                    else if (oce != null)
-                    {
-                        throw oce;
-                    }
-
-                    // Exposing wait handle which is lazily initialized if needed
-                    if (m_waitHandle != null && m_currentCount == 0)
-                    {
-                        m_waitHandle.Reset();
-                    }
-                }
-            }
-            finally
-            {
-                // Release the lock
-                if (lockTaken)
-                {
-                    m_waitCount--;
-                    Monitor.Exit(m_lockObj);
-                }
-
-                // Unregister the cancellation callback.
-                cancellationTokenRegistration.Dispose();
-            }
-
-            // If we had to fall back to asynchronous waiting, block on it
-            // here now that we've released the lock, and return its
-            // result when available.  Otherwise, this was a synchronous
-            // wait, and whether we successfully acquired the semaphore is
-            // stored in waitSuccessful.
-
-            return (asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful;
-        }
-
-        /// <summary>
-        /// Local helper function, waits on the monitor until the monitor receives signal or the
-        /// timeout is expired
-        /// </summary>
-        /// <param name="millisecondsTimeout">The maximum timeout</param>
-        /// <param name="startTime">The start ticks to calculate the elapsed time</param>
-        /// <param name="cancellationToken">The CancellationToken to observe.</param>
-        /// <returns>true if the monitor received a signal, false if the timeout expired</returns>
-        private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, CancellationToken cancellationToken)
-        {
-            int remainingWaitMilliseconds = Timeout.Infinite;
-
-            //Wait on the monitor as long as the count is zero
-            while (m_currentCount == 0)
-            {
-                // If cancelled, we throw. Trying to wait could lead to deadlock.
-                cancellationToken.ThrowIfCancellationRequested();
-
-                if (millisecondsTimeout != Timeout.Infinite)
-                {
-                    remainingWaitMilliseconds = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
-                    if (remainingWaitMilliseconds <= 0)
-                    {
-                        // The thread has expires its timeout
-                        return false;
-                    }
-                }
-
-                // ** the actual wait **
-                bool waitSuccessful = Monitor.Wait(m_lockObj, remainingWaitMilliseconds);
-
-                // This waiter has woken up and this needs to be reflected in the count of waiters pulsed to wake. Since we
-                // don't have thread-specific pulse state, there is not enough information to tell whether this thread woke up
-                // because it was pulsed. For instance, this thread may have timed out and may have been waiting to reacquire
-                // the lock before returning from Monitor.Wait, in which case we don't know whether this thread got pulsed. So
-                // in any woken case, decrement the count if possible. As such, timeouts could cause more waiters to wake than
-                // necessary.
-                if (m_countOfWaitersPulsedToWake != 0)
-                {
-                    --m_countOfWaitersPulsedToWake;
-                }
-
-                if (!waitSuccessful)
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        /// <summary>
-        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>.
-        /// </summary>
-        /// <returns>A task that will complete when the semaphore has been entered.</returns>
-        public Task WaitAsync()
-        {
-            return WaitAsync(Timeout.Infinite, default);
-        }
-
-        /// <summary>
-        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, while observing a
-        /// <see cref="T:System.Threading.CancellationToken"/>.
-        /// </summary>
-        /// <returns>A task that will complete when the semaphore has been entered.</returns>
-        /// <param name="cancellationToken">
-        /// The <see cref="T:System.Threading.CancellationToken"/> token to observe.
-        /// </param>
-        /// <exception cref="T:System.ObjectDisposedException">
-        /// The current instance has already been disposed.
-        /// </exception>
-        public Task WaitAsync(CancellationToken cancellationToken)
-        {
-            return WaitAsync(Timeout.Infinite, cancellationToken);
-        }
-
-        /// <summary>
-        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>,
-        /// using a 32-bit signed integer to measure the time interval.
-        /// </summary>
-        /// <param name="millisecondsTimeout">
-        /// The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to wait indefinitely.
-        /// </param>
-        /// <returns>
-        /// A task that will complete with a result of true if the current thread successfully entered 
-        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
-        /// </returns>
-        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
-        /// disposed.</exception>
-        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
-        /// which represents an infinite time-out.
-        /// </exception>
-        public Task<bool> WaitAsync(int millisecondsTimeout)
-        {
-            return WaitAsync(millisecondsTimeout, default);
-        }
-
-        /// <summary>
-        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, using a <see
-        /// cref="T:System.TimeSpan"/> to measure the time interval, while observing a
-        /// <see cref="T:System.Threading.CancellationToken"/>.
-        /// </summary>
-        /// <param name="timeout">
-        /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds
-        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
-        /// </param>
-        /// <param name="cancellationToken">
-        /// The <see cref="T:System.Threading.CancellationToken"/> token to observe.
-        /// </param>
-        /// <returns>
-        /// A task that will complete with a result of true if the current thread successfully entered 
-        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
-        /// </returns>
-        /// <exception cref="T:System.ObjectDisposedException">
-        /// The current instance has already been disposed.
-        /// </exception>
-        /// <exception cref="T:System.ArgumentOutOfRangeException">
-        /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents 
-        /// an infinite time-out -or- timeout is greater than <see cref="System.int.MaxValue"/>.
-        /// </exception>
-        public Task<bool> WaitAsync(TimeSpan timeout)
-        {
-            return WaitAsync(timeout, default);
-        }
-
-        /// <summary>
-        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, using a <see
-        /// cref="T:System.TimeSpan"/> to measure the time interval.
-        /// </summary>
-        /// <param name="timeout">
-        /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds
-        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
-        /// </param>
-        /// <returns>
-        /// A task that will complete with a result of true if the current thread successfully entered 
-        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
-        /// </returns>
-        /// <exception cref="T:System.ArgumentOutOfRangeException">
-        /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents 
-        /// an infinite time-out -or- timeout is greater than <see cref="System.int.MaxValue"/>.
-        /// </exception>
-        public Task<bool> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
-        {
-            // Validate the timeout
-            long totalMilliseconds = (long)timeout.TotalMilliseconds;
-            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
-            {
-                throw new System.ArgumentOutOfRangeException(
-                    nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
-            }
-
-            // Call wait with the timeout milliseconds
-            return WaitAsync((int)timeout.TotalMilliseconds, cancellationToken);
-        }
-
-        /// <summary>
-        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>,
-        /// using a 32-bit signed integer to measure the time interval, 
-        /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
-        /// </summary>
-        /// <param name="millisecondsTimeout">
-        /// The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to wait indefinitely.
-        /// </param>
-        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
-        /// <returns>
-        /// A task that will complete with a result of true if the current thread successfully entered 
-        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
-        /// </returns>
-        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
-        /// disposed.</exception>
-        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
-        /// which represents an infinite time-out.
-        /// </exception>
-        public Task<bool> WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
-        {
-            CheckDispose();
-
-            // Validate input
-            if (millisecondsTimeout < -1)
-            {
-                throw new ArgumentOutOfRangeException(
-                    nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
-            }
-
-            // Bail early for cancellation
-            if (cancellationToken.IsCancellationRequested)
-                return Task.FromCanceled<bool>(cancellationToken);
-
-            lock (m_lockObj)
-            {
-                // If there are counts available, allow this waiter to succeed.
-                if (m_currentCount > 0)
-                {
-                    --m_currentCount;
-                    if (m_waitHandle != null && m_currentCount == 0) m_waitHandle.Reset();
-                    return s_trueTask;
-                }
-                else if (millisecondsTimeout == 0)
-                {
-                    // No counts, if timeout is zero fail fast
-                    return s_falseTask;
-                }
-                // If there aren't, create and return a task to the caller.
-                // The task will be completed either when they've successfully acquired
-                // the semaphore or when the timeout expired or cancellation was requested.
-                else
-                {
-                    Debug.Assert(m_currentCount == 0, "m_currentCount should never be negative");
-                    var asyncWaiter = CreateAndAddAsyncWaiter();
-                    return (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled) ?
-                        asyncWaiter :
-                        WaitUntilCountOrTimeoutAsync(asyncWaiter, millisecondsTimeout, cancellationToken);
-                }
-            }
-        }
-
-        /// <summary>Creates a new task and stores it into the async waiters list.</summary>
-        /// <returns>The created task.</returns>
-        private TaskNode CreateAndAddAsyncWaiter()
-        {
-            Debug.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
-
-            // Create the task
-            var task = new TaskNode();
-
-            // Add it to the linked list
-            if (m_asyncHead == null)
-            {
-                Debug.Assert(m_asyncTail == null, "If head is null, so too should be tail");
-                m_asyncHead = task;
-                m_asyncTail = task;
-            }
-            else
-            {
-                Debug.Assert(m_asyncTail != null, "If head is not null, neither should be tail");
-                m_asyncTail.Next = task;
-                task.Prev = m_asyncTail;
-                m_asyncTail = task;
-            }
-
-            // Hand it back
-            return task;
-        }
-
-        /// <summary>Removes the waiter task from the linked list.</summary>
-        /// <param name="task">The task to remove.</param>
-        /// <returns>true if the waiter was in the list; otherwise, false.</returns>
-        private bool RemoveAsyncWaiter(TaskNode task)
-        {
-            Debug.Assert(task != null, "Expected non-null task");
-            Debug.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
-
-            // Is the task in the list?  To be in the list, either it's the head or it has a predecessor that's in the list.
-            bool wasInList = m_asyncHead == task || task.Prev != null;
-
-            // Remove it from the linked list
-            if (task.Next != null) task.Next.Prev = task.Prev;
-            if (task.Prev != null) task.Prev.Next = task.Next;
-            if (m_asyncHead == task) m_asyncHead = task.Next;
-            if (m_asyncTail == task) m_asyncTail = task.Prev;
-            Debug.Assert((m_asyncHead == null) == (m_asyncTail == null), "Head is null iff tail is null");
-
-            // Make sure not to leak
-            task.Next = task.Prev = null;
-
-            // Return whether the task was in the list
-            return wasInList;
-        }
-
-        /// <summary>Performs the asynchronous wait.</summary>
-        /// <param name="millisecondsTimeout">The timeout.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>The task to return to the caller.</returns>
-        private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken)
-        {
-            Debug.Assert(asyncWaiter != null, "Waiter should have been constructed");
-            Debug.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
-
-            // Wait until either the task is completed, timeout occurs, or cancellation is requested.
-            // We need to ensure that the Task.Delay task is appropriately cleaned up if the await
-            // completes due to the asyncWaiter completing, so we use our own token that we can explicitly
-            // cancel, and we chain the caller's supplied token into it.
-            using (var cts = cancellationToken.CanBeCanceled ?
-                CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default) :
-                new CancellationTokenSource())
-            {
-                var waitCompleted = Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token));
-                if (asyncWaiter == await waitCompleted.ConfigureAwait(false))
-                {
-                    cts.Cancel(); // ensure that the Task.Delay task is cleaned up
-                    return true; // successfully acquired
-                }
-            }
-
-            // If we get here, the wait has timed out or been canceled.
-
-            // If the await completed synchronously, we still hold the lock.  If it didn't,
-            // we no longer hold the lock.  As such, acquire it.
-            lock (m_lockObj)
-            {
-                // Remove the task from the list.  If we're successful in doing so,
-                // we know that no one else has tried to complete this waiter yet,
-                // so we can safely cancel or timeout.
-                if (RemoveAsyncWaiter(asyncWaiter))
-                {
-                    cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred
-                    return false; // timeout occurred
-                }
-            }
-
-            // The waiter had already been removed, which means it's already completed or is about to
-            // complete, so let it, and don't return until it does.
-            return await asyncWaiter.ConfigureAwait(false);
-        }
-
-        /// <summary>
-        /// Exits the <see cref="SemaphoreSlim"/> once.
-        /// </summary>
-        /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
-        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
-        /// disposed.</exception>
-        public int Release()
-        {
-            return Release(1);
-        }
-
-        /// <summary>
-        /// Exits the <see cref="SemaphoreSlim"/> a specified number of times.
-        /// </summary>
-        /// <param name="releaseCount">The number of times to exit the semaphore.</param>
-        /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
-        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="releaseCount"/> is less
-        /// than 1.</exception>
-        /// <exception cref="T:System.Threading.SemaphoreFullException">The <see cref="SemaphoreSlim"/> has
-        /// already reached its maximum size.</exception>
-        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
-        /// disposed.</exception>
-        public int Release(int releaseCount)
-        {
-            CheckDispose();
-
-            // Validate input
-            if (releaseCount < 1)
-            {
-                throw new ArgumentOutOfRangeException(
-                    nameof(releaseCount), releaseCount, SR.SemaphoreSlim_Release_CountWrong);
-            }
-            int returnCount;
-
-            lock (m_lockObj)
-            {
-                // Read the m_currentCount into a local variable to avoid unnecessary volatile accesses inside the lock.
-                int currentCount = m_currentCount;
-                returnCount = currentCount;
-
-                // If the release count would result exceeding the maximum count, throw SemaphoreFullException.
-                if (m_maxCount - currentCount < releaseCount)
-                {
-                    throw new SemaphoreFullException();
-                }
-
-                // Increment the count by the actual release count
-                currentCount += releaseCount;
-
-                // Signal to any synchronous waiters, taking into account how many waiters have previously been pulsed to wake
-                // but have not yet woken
-                int waitCount = m_waitCount;
-                Debug.Assert(m_countOfWaitersPulsedToWake <= waitCount);
-                int waitersToNotify = Math.Min(currentCount, waitCount) - m_countOfWaitersPulsedToWake;
-                if (waitersToNotify > 0)
-                {
-                    // Ideally, limiting to a maximum of releaseCount would not be necessary and could be an assert instead, but
-                    // since WaitUntilCountOrTimeout() does not have enough information to tell whether a woken thread was
-                    // pulsed, it's possible for m_countOfWaitersPulsedToWake to be less than the number of threads that have
-                    // actually been pulsed to wake.
-                    if (waitersToNotify > releaseCount)
-                    {
-                        waitersToNotify = releaseCount;
-                    }
-
-                    m_countOfWaitersPulsedToWake += waitersToNotify;
-                    for (int i = 0; i < waitersToNotify; i++)
-                    {
-                        Monitor.Pulse(m_lockObj);
-                    }
-                }
-
-                // Now signal to any asynchronous waiters, if there are any.  While we've already
-                // signaled the synchronous waiters, we still hold the lock, and thus
-                // they won't have had an opportunity to acquire this yet.  So, when releasing
-                // asynchronous waiters, we assume that all synchronous waiters will eventually
-                // acquire the semaphore.  That could be a faulty assumption if those synchronous
-                // waits are canceled, but the wait code path will handle that.
-                if (m_asyncHead != null)
-                {
-                    Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't null");
-                    int maxAsyncToRelease = currentCount - waitCount;
-                    while (maxAsyncToRelease > 0 && m_asyncHead != null)
-                    {
-                        --currentCount;
-                        --maxAsyncToRelease;
-
-                        // Get the next async waiter to release and queue it to be completed
-                        var waiterTask = m_asyncHead;
-                        RemoveAsyncWaiter(waiterTask); // ensures waiterTask.Next/Prev are null
-                        QueueWaiterTask(waiterTask);
-                    }
-                }
-                m_currentCount = currentCount;
-
-                // Exposing wait handle if it is not null
-                if (m_waitHandle != null && returnCount == 0 && currentCount > 0)
-                {
-                    m_waitHandle.Set();
-                }
-            }
-
-            // And return the count
-            return returnCount;
-        }
-
-        /// <summary>
-        /// Queues a waiter task to the ThreadPool. We use this small helper method so that
-        /// the larger Release(count) method does not need to be SecuritySafeCritical.
-        /// </summary>
-        private static void QueueWaiterTask(TaskNode waiterTask)
-        {
-            ThreadPool.UnsafeQueueCustomWorkItem(waiterTask, forceGlobal: false);
-        }
-
-        /// <summary>
-        /// Releases all resources used by the current instance of <see
-        /// cref="SemaphoreSlim"/>.
-        /// </summary>
-        /// <remarks>
-        /// Unlike most of the members of <see cref="SemaphoreSlim"/>, <see cref="Dispose()"/> is not
-        /// thread-safe and may not be used concurrently with other members of this instance.
-        /// </remarks>
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, releases the unmanaged resources used by the 
-        /// <see cref="T:System.Threading.ManualResetEventSlim"/>, and optionally releases the managed resources.
-        /// </summary>
-        /// <param name="disposing">true to release both managed and unmanaged resources;
-        /// false to release only unmanaged resources.</param>
-        /// <remarks>
-        /// Unlike most of the members of <see cref="SemaphoreSlim"/>, <see cref="Dispose(bool)"/> is not
-        /// thread-safe and may not be used concurrently with other members of this instance.
-        /// </remarks>
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing)
-            {
-                if (m_waitHandle != null)
-                {
-                    m_waitHandle.Close();
-                    m_waitHandle = null;
-                }
-                m_lockObj = null;
-                m_asyncHead = null;
-                m_asyncTail = null;
-            }
-        }
-
-
-
-        /// <summary>
-        /// Private helper method to wake up waiters when a cancellationToken gets canceled.
-        /// </summary>
-        private static Action<object> s_cancellationTokenCanceledEventHandler = new Action<object>(CancellationTokenCanceledEventHandler);
-        private static void CancellationTokenCanceledEventHandler(object obj)
-        {
-            SemaphoreSlim semaphore = obj as SemaphoreSlim;
-            Debug.Assert(semaphore != null, "Expected a SemaphoreSlim");
-            lock (semaphore.m_lockObj)
-            {
-                Monitor.PulseAll(semaphore.m_lockObj); //wake up all waiters.
-            }
-        }
-
-        /// <summary>
-        /// Checks the dispose status by checking the lock object, if it is null means that object
-        /// has been disposed and throw ObjectDisposedException
-        /// </summary>
-        private void CheckDispose()
-        {
-            if (m_lockObj == null)
-            {
-                throw new ObjectDisposedException(null, SR.SemaphoreSlim_Disposed);
-            }
-        }
-
-        #endregion
-    }
-}
index 96ed922a846df26ed7b6bddd3b7f06c196dbf430..ff41f939f1c6eac5314dd44aca47bfe8e861bf54 100644 (file)
@@ -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/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.Windows.cs
new file mode 100644 (file)
index 0000000..66f17f0
--- /dev/null
@@ -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/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeWaitHandle.cs
new file mode 100644 (file)
index 0000000..edb0cdf
--- /dev/null
@@ -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);
+        }
+    }
+}
index da36623eaff7f6e0329a5e727bafbb627d9d3cd9..59a3074ab8b77b993d25b256eb23330002c87dfb 100644 (file)
@@ -26,6 +26,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleZeroOrMinusOneIsInvalid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeHandleMinusOneIsInvalid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Action.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\AccessViolationException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ApplicationException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ApartmentState.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\AsyncLocal.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\AutoResetEvent.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\CancellationToken.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\DeferredDisposableLifetime.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventResetMode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventWaitHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ReaderWriterLockSlim.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreFullException.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreSlim.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SendOrPostCallback.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinWait.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SynchronizationLockException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.BOOL.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Libraries.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CancelIoEx.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileAttributes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FILE_INFO_BY_HANDLE_CLASS.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs" />
   </ItemGroup>
   <ItemGroup Condition="$(TargetsWindows) or '$(FeaturePal)'=='true'">
+    <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Windows.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.Constants.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationToken.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationToken.cs
new file mode 100644 (file)
index 0000000..68e6971
--- /dev/null
@@ -0,0 +1,340 @@
+// 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.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Threading
+{
+    /// <summary>
+    /// Propagates notification that operations should be canceled.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// A <see cref="CancellationToken"/> may be created directly in an unchangeable canceled or non-canceled state
+    /// using the CancellationToken's constructors. However, to have a CancellationToken that can change 
+    /// from a non-canceled to a canceled state, 
+    /// <see cref="System.Threading.CancellationTokenSource">CancellationTokenSource</see> must be used.
+    /// CancellationTokenSource exposes the associated CancellationToken that may be canceled by the source through its 
+    /// <see cref="System.Threading.CancellationTokenSource.Token">Token</see> property. 
+    /// </para>
+    /// <para>
+    /// Once canceled, a token may not transition to a non-canceled state, and a token whose 
+    /// <see cref="CanBeCanceled"/> is false will never change to one that can be canceled.
+    /// </para>
+    /// <para>
+    /// All members of this struct are thread-safe and may be used concurrently from multiple threads.
+    /// </para>
+    /// </remarks>
+    [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
+    public readonly struct CancellationToken
+    {
+        // 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<object> s_actionToActionObjShunt = obj => ((Action)obj)();
+
+        /// <summary>
+        /// Returns an empty CancellationToken value.
+        /// </summary>
+        /// <remarks>
+        /// The <see cref="CancellationToken"/> value returned by this property will be non-cancelable by default.
+        /// </remarks>
+        public static CancellationToken None => default;
+
+        /// <summary>
+        /// Gets whether cancellation has been requested for this token.
+        /// </summary>
+        /// <value>Whether cancellation has been requested for this token.</value>
+        /// <remarks>
+        /// <para>
+        /// This property indicates whether cancellation has been requested for this token, 
+        /// either through the token initially being constructed in a canceled state, or through
+        /// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see> 
+        /// on the token's associated <see cref="CancellationTokenSource"/>.
+        /// </para>
+        /// <para>
+        /// If this property is true, it only guarantees that cancellation has been requested.  
+        /// It does not guarantee that every registered handler
+        /// has finished executing, nor that cancellation requests have finished propagating
+        /// to all registered handlers.  Additional synchronization may be required,
+        /// particularly in situations where related objects are being canceled concurrently.
+        /// </para>
+        /// </remarks>
+        public bool IsCancellationRequested => _source != null && _source.IsCancellationRequested;
+
+        /// <summary>
+        /// Gets whether this token is capable of being in the canceled state.
+        /// </summary>
+        /// <remarks>
+        /// If CanBeCanceled returns false, it is guaranteed that the token will never transition
+        /// into a canceled state, meaning that <see cref="IsCancellationRequested"/> will never
+        /// return true.
+        /// </remarks>
+        public bool CanBeCanceled => _source != null;
+
+        /// <summary>
+        /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is signaled when the token is canceled.</summary>
+        /// <remarks>
+        /// Accessing this property causes a <see cref="T:System.Threading.WaitHandle">WaitHandle</see>
+        /// to be instantiated.  It is preferable to only use this property when necessary, and to then
+        /// dispose the associated <see cref="CancellationTokenSource"/> instance at the earliest opportunity (disposing
+        /// the source will dispose of this allocated handle).  The handle should not be closed or disposed directly.
+        /// </remarks>
+        /// <exception cref="T:System.ObjectDisposedException">The associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+        public WaitHandle WaitHandle => (_source ?? CancellationTokenSource.s_neverCanceledSource).WaitHandle;
+
+        // public CancellationToken()
+        // this constructor is implicit for structs
+        //   -> this should behaves exactly as for new CancellationToken(false)
+
+        /// <summary>
+        /// Internal constructor only a CancellationTokenSource should create a CancellationToken
+        /// </summary>
+        internal CancellationToken(CancellationTokenSource source) => _source = source;
+
+        /// <summary>
+        /// Initializes the <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+        /// </summary>
+        /// <param name="canceled">
+        /// The canceled state for the token.
+        /// </param>
+        /// <remarks>
+        /// Tokens created with this constructor will remain in the canceled state specified
+        /// by the <paramref name="canceled"/> parameter.  If <paramref name="canceled"/> is false,
+        /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be false.
+        /// If <paramref name="canceled"/> is true,
+        /// both <see cref="CanBeCanceled"/> and <see cref="IsCancellationRequested"/> will be true. 
+        /// </remarks>
+        public CancellationToken(bool canceled) : this(canceled ? CancellationTokenSource.s_canceledSource : null)
+        {
+        }
+
+        /// <summary>
+        /// Registers a delegate that will be called when this <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// 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.
+        /// </para>
+        /// <para>
+        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
+        /// along with the delegate and will be used when executing it.
+        /// </para>
+        /// </remarks>
+        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
+        /// be used to unregister the callback.</returns>
+        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+        public CancellationTokenRegistration Register(Action callback) =>
+            Register(
+                s_actionToActionObjShunt,
+                callback ?? throw new ArgumentNullException(nameof(callback)),
+                useSynchronizationContext: false,
+                useExecutionContext: true);
+
+        /// <summary>
+        /// Registers a delegate that will be called when this 
+        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// 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.
+        /// </para>
+        /// <para>
+        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
+        /// along with the delegate and will be used when executing it.
+        /// </para>
+        /// </remarks>
+        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+        /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
+        /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
+        /// when invoking the <paramref name="callback"/>.</param>
+        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
+        /// be used to unregister the callback.</returns>
+        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+        public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext) =>
+            Register(
+                s_actionToActionObjShunt,
+                callback ?? throw new ArgumentNullException(nameof(callback)),
+                useSynchronizationContext,
+                useExecutionContext: true);
+
+        /// <summary>
+        /// Registers a delegate that will be called when this 
+        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// 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.
+        /// </para>
+        /// <para>
+        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, will be captured
+        /// along with the delegate and will be used when executing it.
+        /// </para>
+        /// </remarks>
+        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+        /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked.  This may be null.</param>
+        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
+        /// be used to unregister the callback.</returns>
+        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+        public CancellationTokenRegistration Register(Action<object> callback, object state) =>
+            Register(callback, state, useSynchronizationContext: false, useExecutionContext: true);
+
+        /// <summary>
+        /// Registers a delegate that will be called when this 
+        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// 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.
+        /// </para>
+        /// <para>
+        /// The current <see cref="System.Threading.ExecutionContext">ExecutionContext</see>, if one exists, 
+        /// will be captured along with the delegate and will be used when executing it.
+        /// </para>
+        /// </remarks>
+        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+        /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked.  This may be null.</param>
+        /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
+        /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
+        /// when invoking the <paramref name="callback"/>.</param>
+        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
+        /// be used to unregister the callback.</returns>
+        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+        /// <exception cref="T:System.ObjectDisposedException">The associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+        public CancellationTokenRegistration Register(Action<object> callback, object state, bool useSynchronizationContext) =>
+            Register(callback, state, useSynchronizationContext, useExecutionContext: true);
+
+        // 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<object> callback, object state) =>
+            Register(callback, state, useSynchronizationContext: false, useExecutionContext: false);
+
+        /// <summary>
+        /// Registers a delegate that will be called when this 
+        /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// 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.
+        /// </para>
+        /// </remarks>
+        /// <param name="callback">The delegate to be executed when the <see cref="T:System.Threading.CancellationToken">CancellationToken</see> is canceled.</param>
+        /// <param name="state">The state to pass to the <paramref name="callback"/> when the delegate is invoked.  This may be null.</param>
+        /// <param name="useSynchronizationContext">A Boolean value that indicates whether to capture
+        /// the current <see cref="T:System.Threading.SynchronizationContext">SynchronizationContext</see> and use it
+        /// when invoking the <paramref name="callback"/>.</param>
+        /// <returns>The <see cref="T:System.Threading.CancellationTokenRegistration"/> instance that can 
+        /// be used to unregister the callback.</returns>
+        /// <exception cref="T:System.ArgumentNullException"><paramref name="callback"/> is null.</exception>
+        /// <exception cref="T:System.ObjectDisposedException">The associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+#if CORECLR
+        private
+#else
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public
+#endif
+        CancellationTokenRegistration Register(Action<object> 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, 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.
+        }
+
+        /// <summary>
+        /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the 
+        /// specified token.
+        /// </summary>
+        /// <param name="other">The other <see cref="T:System.Threading.CancellationToken">CancellationToken</see> to which to compare this
+        /// instance.</param>
+        /// <returns>True if the instances are equal; otherwise, false. Two tokens are equal if they are associated
+        /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed 
+        /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
+        public bool Equals(CancellationToken other) => _source == other._source;
+
+        /// <summary>
+        /// Determines whether the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance is equal to the 
+        /// specified <see cref="T:System.Object"/>.
+        /// </summary>
+        /// <param name="other">The other object to which to compare this instance.</param>
+        /// <returns>True if <paramref name="other"/> is a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>
+        /// and if the two instances are equal; otherwise, false. Two tokens are equal if they are associated
+        /// with the same <see cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> or if they were both constructed 
+        /// from public CancellationToken constructors and their <see cref="IsCancellationRequested"/> values are equal.</returns>
+        /// <exception cref="T:System.ObjectDisposedException">An associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+        public override bool Equals(object other) => other is CancellationToken && Equals((CancellationToken)other);
+
+        /// <summary>
+        /// Serves as a hash function for a <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+        /// </summary>
+        /// <returns>A hash code for the current <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instance.</returns>
+        public override int GetHashCode() => (_source ?? CancellationTokenSource.s_neverCanceledSource).GetHashCode();
+
+        /// <summary>
+        /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are equal.
+        /// </summary>
+        /// <param name="left">The first instance.</param>
+        /// <param name="right">The second instance.</param>
+        /// <returns>True if the instances are equal; otherwise, false.</returns>
+        /// <exception cref="T:System.ObjectDisposedException">An associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+        public static bool operator ==(CancellationToken left, CancellationToken right) => left.Equals(right);
+
+        /// <summary>
+        /// Determines whether two <see cref="T:System.Threading.CancellationToken">CancellationToken</see> instances are not equal.
+        /// </summary>
+        /// <param name="left">The first instance.</param>
+        /// <param name="right">The second instance.</param>
+        /// <returns>True if the instances are not equal; otherwise, false.</returns>
+        /// <exception cref="T:System.ObjectDisposedException">An associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+        public static bool operator !=(CancellationToken left, CancellationToken right) => !left.Equals(right);
+
+        /// <summary>
+        /// Throws a <see cref="T:System.OperationCanceledException">OperationCanceledException</see> if
+        /// this token has had cancellation requested.
+        /// </summary>
+        /// <remarks>
+        /// This method provides functionality equivalent to:
+        /// <code>
+        /// if (token.IsCancellationRequested) 
+        ///    throw new OperationCanceledException(token);
+        /// </code>
+        /// </remarks>
+        /// <exception cref="System.OperationCanceledException">The token has had cancellation requested.</exception>
+        /// <exception cref="T:System.ObjectDisposedException">The associated <see
+        /// cref="T:System.Threading.CancellationTokenSource">CancellationTokenSource</see> has been disposed.</exception>
+        public void ThrowIfCancellationRequested()
+        {
+            if (IsCancellationRequested)
+                ThrowOperationCanceledException();
+        }
+
+        // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
+        private void ThrowOperationCanceledException() =>
+            throw new OperationCanceledException(SR.OperationCanceled, this);
+    }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs
new file mode 100644 (file)
index 0000000..6dca573
--- /dev/null
@@ -0,0 +1,946 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace System.Threading
+{
+    /// <summary>
+    /// Limits the number of threads that can access a resource or pool of resources concurrently.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// The <see cref="SemaphoreSlim"/> provides a lightweight semaphore class that doesn't
+    /// use Windows kernel semaphores.
+    /// </para>
+    /// <para>
+    /// All public and protected members of <see cref="SemaphoreSlim"/> are thread-safe and may be used
+    /// concurrently from multiple threads, with the exception of Dispose, which
+    /// must only be used when all other operations on the <see cref="SemaphoreSlim"/> have
+    /// completed.
+    /// </para>
+    /// </remarks>
+    [DebuggerDisplay("Current Count = {m_currentCount}")]
+    public class SemaphoreSlim : IDisposable
+    {
+        #region Private Fields
+
+        // The semaphore count, initialized in the constructor to the initial value, every release call incremetns it
+        // and every wait call decrements it as long as its value is positive otherwise the wait will block.
+        // Its value must be between the maximum semaphore value and zero
+        private volatile int m_currentCount;
+
+        // The maximum semaphore value, it is initialized to Int.MaxValue if the client didn't specify it. it is used 
+        // to check if the count excceeded the maxi value or not.
+        private readonly int m_maxCount;
+
+        // The number of synchronously waiting threads, it is set to zero in the constructor and increments before blocking the
+        // threading and decrements it back after that. It is used as flag for the release call to know if there are
+        // waiting threads in the monitor or not.
+        private int m_waitCount;
+
+        /// <summary>
+        /// This is used to help prevent waking more waiters than necessary. It's not perfect and sometimes more waiters than
+        /// necessary may still be woken, see <see cref="WaitUntilCountOrTimeout"/>.
+        /// </summary>
+        private int m_countOfWaitersPulsedToWake;
+
+        // Dummy object used to in lock statements to protect the semaphore count, wait handle and cancelation
+        private object m_lockObj;
+
+        // Act as the semaphore wait handle, it's lazily initialized if needed, the first WaitHandle call initialize it
+        // and wait an release sets and resets it respectively as long as it is not null
+        private volatile ManualResetEvent m_waitHandle;
+
+        // Head of list representing asynchronous waits on the semaphore.
+        private TaskNode m_asyncHead;
+
+        // Tail of list representing asynchronous waits on the semaphore.
+        private TaskNode m_asyncTail;
+
+        // A pre-completed task with Result==true
+        private static readonly Task<bool> s_trueTask =
+            new Task<bool>(false, true, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default);
+        // A pre-completed task with Result==false
+        private readonly static Task<bool> s_falseTask =
+            new Task<bool>(false, false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default);
+
+        // No maximum constant
+        private const int NO_MAXIMUM = int.MaxValue;
+
+        // Task in a linked list of asynchronous waiters
+        private sealed class TaskNode : Task<bool>, IThreadPoolWorkItem
+        {
+            internal TaskNode Prev, Next;
+            internal TaskNode() : base() { }
+
+            void IThreadPoolWorkItem.ExecuteWorkItem()
+            {
+                bool setSuccessfully = TrySetResult(true);
+                Debug.Assert(setSuccessfully, "Should have been able to complete task");
+            }
+#if CORECLR
+            void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ } 
+#endif
+        }
+        #endregion
+
+        #region Public properties
+
+        /// <summary>
+        /// Gets the current count of the <see cref="SemaphoreSlim"/>.
+        /// </summary>
+        /// <value>The current count of the <see cref="SemaphoreSlim"/>.</value>
+        public int CurrentCount
+        {
+            get { return m_currentCount; }
+        }
+
+        /// <summary>
+        /// Returns a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait on the semaphore.
+        /// </summary>
+        /// <value>A <see cref="T:System.Threading.WaitHandle"/> that can be used to wait on the
+        /// semaphore.</value>
+        /// <remarks>
+        /// A successful wait on the <see cref="AvailableWaitHandle"/> does not imply a successful wait on
+        /// the <see cref="SemaphoreSlim"/> itself, nor does it decrement the semaphore's
+        /// count. <see cref="AvailableWaitHandle"/> exists to allow a thread to block waiting on multiple
+        /// semaphores, but such a wait should be followed by a true wait on the target semaphore.
+        /// </remarks>
+        /// <exception cref="T:System.ObjectDisposedException">The <see
+        /// cref="SemaphoreSlim"/> has been disposed.</exception>
+        public WaitHandle AvailableWaitHandle
+        {
+            get
+            {
+                CheckDispose();
+
+                // Return it directly if it is not null
+                if (m_waitHandle != null)
+                    return m_waitHandle;
+
+                //lock the count to avoid multiple threads initializing the handle if it is null
+                lock (m_lockObj)
+                {
+                    if (m_waitHandle == null)
+                    {
+                        // The initial state for the wait handle is true if the count is greater than zero
+                        // false otherwise
+                        m_waitHandle = new ManualResetEvent(m_currentCount != 0);
+                    }
+                }
+                return m_waitHandle;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SemaphoreSlim"/> class, specifying
+        /// the initial number of requests that can be granted concurrently.
+        /// </summary>
+        /// <param name="initialCount">The initial number of requests for the semaphore that can be granted
+        /// concurrently.</param>
+        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="initialCount"/>
+        /// is less than 0.</exception>
+        public SemaphoreSlim(int initialCount)
+            : this(initialCount, NO_MAXIMUM)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SemaphoreSlim"/> class, specifying
+        /// the initial and maximum number of requests that can be granted concurrently.
+        /// </summary>
+        /// <param name="initialCount">The initial number of requests for the semaphore that can be granted
+        /// concurrently.</param>
+        /// <param name="maxCount">The maximum number of requests for the semaphore that can be granted
+        /// concurrently.</param>
+        /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="initialCount"/>
+        /// is less than 0. -or-
+        /// <paramref name="initialCount"/> is greater than <paramref name="maxCount"/>. -or-
+        /// <paramref name="maxCount"/> is less than 0.</exception>
+        public SemaphoreSlim(int initialCount, int maxCount)
+        {
+            if (initialCount < 0 || initialCount > maxCount)
+            {
+                throw new ArgumentOutOfRangeException(
+                    nameof(initialCount), initialCount, SR.SemaphoreSlim_ctor_InitialCountWrong);
+            }
+
+            //validate input
+            if (maxCount <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(maxCount), maxCount, SR.SemaphoreSlim_ctor_MaxCountWrong);
+            }
+
+            m_maxCount = maxCount;
+            m_lockObj = new object();
+            m_currentCount = initialCount;
+        }
+
+        #endregion
+
+        #region  Methods
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>.
+        /// </summary>
+        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+        /// disposed.</exception>
+        public void Wait()
+        {
+            // Call wait with infinite timeout
+            Wait(Timeout.Infinite, new CancellationToken());
+        }
+
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, while observing a
+        /// <see cref="T:System.Threading.CancellationToken"/>.
+        /// </summary>
+        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> token to
+        /// observe.</param>
+        /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> was
+        /// canceled.</exception>
+        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+        /// disposed.</exception>
+        public void Wait(CancellationToken cancellationToken)
+        {
+            // Call wait with infinite timeout
+            Wait(Timeout.Infinite, cancellationToken);
+        }
+
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a <see
+        /// cref="T:System.TimeSpan"/> to measure the time interval.
+        /// </summary>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+        /// </param>
+        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
+        /// otherwise, false.</returns>
+        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+        /// than <see cref="System.Int32.MaxValue"/>.</exception>
+        public bool Wait(TimeSpan timeout)
+        {
+            // Validate the timeout
+            long totalMilliseconds = (long)timeout.TotalMilliseconds;
+            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+            {
+                throw new System.ArgumentOutOfRangeException(
+                    nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
+            }
+
+            // Call wait with the timeout milliseconds
+            return Wait((int)timeout.TotalMilliseconds, new CancellationToken());
+        }
+
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a <see
+        /// cref="T:System.TimeSpan"/> to measure the time interval, while observing a <see
+        /// cref="T:System.Threading.CancellationToken"/>.
+        /// </summary>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+        /// </param>
+        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to
+        /// observe.</param>
+        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
+        /// otherwise, false.</returns>
+        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
+        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
+        /// than <see cref="System.Int32.MaxValue"/>.</exception>
+        /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
+        public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
+        {
+            // Validate the timeout
+            long totalMilliseconds = (long)timeout.TotalMilliseconds;
+            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+            {
+                throw new System.ArgumentOutOfRangeException(
+                    nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
+            }
+
+            // Call wait with the timeout milliseconds
+            return Wait((int)timeout.TotalMilliseconds, cancellationToken);
+        }
+
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, using a 32-bit
+        /// signed integer to measure the time interval.
+        /// </summary>
+        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
+        /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param>
+        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>;
+        /// otherwise, false.</returns>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a
+        /// negative number other than -1, which represents an infinite time-out.</exception>
+        public bool Wait(int millisecondsTimeout)
+        {
+            return Wait(millisecondsTimeout, new CancellationToken());
+        }
+
+
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>,
+        /// using a 32-bit signed integer to measure the time interval, 
+        /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
+        /// </summary>
+        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to
+        /// wait indefinitely.</param>
+        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
+        /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>; otherwise, false.</returns>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
+        /// which represents an infinite time-out.</exception>
+        /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
+        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
+        {
+            CheckDispose();
+
+            // Validate input
+            if (millisecondsTimeout < -1)
+            {
+                throw new ArgumentOutOfRangeException(
+                    nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
+            }
+
+            cancellationToken.ThrowIfCancellationRequested();
+
+            // Perf: Check the stack timeout parameter before checking the volatile count
+            if (millisecondsTimeout == 0 && m_currentCount == 0)
+            {
+                // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!)
+                return false;
+            }
+
+            uint startTime = 0;
+            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0)
+            {
+                startTime = TimeoutHelper.GetTime();
+            }
+
+            bool waitSuccessful = false;
+            Task<bool> asyncWaitTask = null;
+            bool lockTaken = false;
+
+            //Register for cancellation outside of the main lock.
+            //NOTE: Register/unregister inside the lock can deadlock as different lock acquisition orders could
+            //      occur for (1)this.m_lockObj and (2)cts.internalLock
+            CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this);
+            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.
+                if (m_currentCount == 0)
+                {
+                    // Monitor.Enter followed by Monitor.Wait is much more expensive than waiting on an event as it involves another
+                    // spin, contention, etc. The usual number of spin iterations that would otherwise be used here is increased to
+                    // lessen that extra expense of doing a proper wait.
+                    int spinCount = SpinWait.SpinCountforSpinBeforeWait * 4;
+                    const int Sleep1Threshold = SpinWait.Sleep1ThresholdForSpinBeforeWait * 4;
+
+                    var spinner = new SpinWait();
+                    while (spinner.Count < spinCount)
+                    {
+                        spinner.SpinOnce(Sleep1Threshold);
+
+                        if (m_currentCount != 0)
+                        {
+                            break;
+                        }
+                    }
+                }
+                // 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 { }
+                finally
+                {
+                    Monitor.Enter(m_lockObj, ref lockTaken);
+                    if (lockTaken)
+                    {
+                        m_waitCount++;
+                    }
+                }
+
+                // If there are any async waiters, for fairness we'll get in line behind
+                // then by translating our synchronous wait into an asynchronous one that we 
+                // then block on (once we've released the lock).
+                if (m_asyncHead != null)
+                {
+                    Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't");
+                    asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken);
+                }
+                // There are no async waiters, so we can proceed with normal synchronous waiting.
+                else
+                {
+                    // If the count > 0 we are good to move on.
+                    // If not, then wait if we were given allowed some wait duration
+
+                    OperationCanceledException oce = null;
+
+                    if (m_currentCount == 0)
+                    {
+                        if (millisecondsTimeout == 0)
+                        {
+                            return false;
+                        }
+
+                        // Prepare for the main wait...
+                        // wait until the count become greater than zero or the timeout is expired
+                        try
+                        {
+                            waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken);
+                        }
+                        catch (OperationCanceledException e) { oce = e; }
+                    }
+
+                    // Now try to acquire.  We prioritize acquisition over cancellation/timeout so that we don't
+                    // lose any counts when there are asynchronous waiters in the mix.  Asynchronous waiters
+                    // defer to synchronous waiters in priority, which means that if it's possible an asynchronous
+                    // waiter didn't get released because a synchronous waiter was present, we need to ensure
+                    // that synchronous waiter succeeds so that they have a chance to release.
+                    Debug.Assert(!waitSuccessful || m_currentCount > 0,
+                        "If the wait was successful, there should be count available.");
+                    if (m_currentCount > 0)
+                    {
+                        waitSuccessful = true;
+                        m_currentCount--;
+                    }
+                    else if (oce != null)
+                    {
+                        throw oce;
+                    }
+
+                    // Exposing wait handle which is lazily initialized if needed
+                    if (m_waitHandle != null && m_currentCount == 0)
+                    {
+                        m_waitHandle.Reset();
+                    }
+                }
+            }
+            finally
+            {
+                // Release the lock
+                if (lockTaken)
+                {
+                    m_waitCount--;
+                    Monitor.Exit(m_lockObj);
+                }
+
+                // Unregister the cancellation callback.
+                cancellationTokenRegistration.Dispose();
+            }
+
+            // If we had to fall back to asynchronous waiting, block on it
+            // here now that we've released the lock, and return its
+            // result when available.  Otherwise, this was a synchronous
+            // wait, and whether we successfully acquired the semaphore is
+            // stored in waitSuccessful.
+
+            return (asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful;
+        }
+
+        /// <summary>
+        /// Local helper function, waits on the monitor until the monitor receives signal or the
+        /// timeout is expired
+        /// </summary>
+        /// <param name="millisecondsTimeout">The maximum timeout</param>
+        /// <param name="startTime">The start ticks to calculate the elapsed time</param>
+        /// <param name="cancellationToken">The CancellationToken to observe.</param>
+        /// <returns>true if the monitor received a signal, false if the timeout expired</returns>
+        private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, CancellationToken cancellationToken)
+        {
+            int remainingWaitMilliseconds = Timeout.Infinite;
+
+            //Wait on the monitor as long as the count is zero
+            while (m_currentCount == 0)
+            {
+                // If cancelled, we throw. Trying to wait could lead to deadlock.
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (millisecondsTimeout != Timeout.Infinite)
+                {
+                    remainingWaitMilliseconds = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
+                    if (remainingWaitMilliseconds <= 0)
+                    {
+                        // The thread has expires its timeout
+                        return false;
+                    }
+                }
+                // ** the actual wait **
+                bool waitSuccessful = Monitor.Wait(m_lockObj, remainingWaitMilliseconds);
+
+                // This waiter has woken up and this needs to be reflected in the count of waiters pulsed to wake. Since we
+                // don't have thread-specific pulse state, there is not enough information to tell whether this thread woke up
+                // because it was pulsed. For instance, this thread may have timed out and may have been waiting to reacquire
+                // the lock before returning from Monitor.Wait, in which case we don't know whether this thread got pulsed. So
+                // in any woken case, decrement the count if possible. As such, timeouts could cause more waiters to wake than
+                // necessary.
+                if (m_countOfWaitersPulsedToWake != 0)
+                {
+                    --m_countOfWaitersPulsedToWake;
+                }
+
+                if (!waitSuccessful)
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>.
+        /// </summary>
+        /// <returns>A task that will complete when the semaphore has been entered.</returns>
+        public Task WaitAsync()
+        {
+            return WaitAsync(Timeout.Infinite, default);
+        }
+
+        /// <summary>
+        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, while observing a
+        /// <see cref="T:System.Threading.CancellationToken"/>.
+        /// </summary>
+        /// <returns>A task that will complete when the semaphore has been entered.</returns>
+        /// <param name="cancellationToken">
+        /// The <see cref="T:System.Threading.CancellationToken"/> token to observe.
+        /// </param>
+        /// <exception cref="T:System.ObjectDisposedException">
+        /// The current instance has already been disposed.
+        /// </exception>
+        public Task WaitAsync(CancellationToken cancellationToken)
+        {
+            return WaitAsync(Timeout.Infinite, cancellationToken);
+        }
+
+        /// <summary>
+        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>,
+        /// using a 32-bit signed integer to measure the time interval.
+        /// </summary>
+        /// <param name="millisecondsTimeout">
+        /// The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to wait indefinitely.
+        /// </param>
+        /// <returns>
+        /// A task that will complete with a result of true if the current thread successfully entered 
+        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+        /// </returns>
+        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+        /// disposed.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
+        /// which represents an infinite time-out.
+        /// </exception>
+        public Task<bool> WaitAsync(int millisecondsTimeout)
+        {
+            return WaitAsync(millisecondsTimeout, default);
+        }
+
+        /// <summary>
+        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, using a <see
+        /// cref="T:System.TimeSpan"/> to measure the time interval, while observing a
+        /// <see cref="T:System.Threading.CancellationToken"/>.
+        /// </summary>
+        /// <param name="timeout">
+        /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+        /// </param>
+        /// <param name="cancellationToken">
+        /// The <see cref="T:System.Threading.CancellationToken"/> token to observe.
+        /// </param>
+        /// <returns>
+        /// A task that will complete with a result of true if the current thread successfully entered 
+        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+        /// </returns>
+        /// <exception cref="T:System.ObjectDisposedException">
+        /// The current instance has already been disposed.
+        /// </exception>
+        /// <exception cref="T:System.ArgumentOutOfRangeException">
+        /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents 
+        /// an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+        /// </exception>
+        public Task<bool> WaitAsync(TimeSpan timeout)
+        {
+            return WaitAsync(timeout, default);
+        }
+
+        /// <summary>
+        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>, using a <see
+        /// cref="T:System.TimeSpan"/> to measure the time interval.
+        /// </summary>
+        /// <param name="timeout">
+        /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds
+        /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
+        /// </param>
+        /// <returns>
+        /// A task that will complete with a result of true if the current thread successfully entered 
+        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+        /// </returns>
+        /// <exception cref="T:System.ArgumentOutOfRangeException">
+        /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents 
+        /// an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+        /// </exception>
+        public Task<bool> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
+        {
+            // Validate the timeout
+            long totalMilliseconds = (long)timeout.TotalMilliseconds;
+            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+            {
+                throw new System.ArgumentOutOfRangeException(
+                    nameof(timeout), timeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
+            }
+
+            // Call wait with the timeout milliseconds
+            return WaitAsync((int)timeout.TotalMilliseconds, cancellationToken);
+        }
+
+        /// <summary>
+        /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/>,
+        /// using a 32-bit signed integer to measure the time interval, 
+        /// while observing a <see cref="T:System.Threading.CancellationToken"/>.
+        /// </summary>
+        /// <param name="millisecondsTimeout">
+        /// The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to wait indefinitely.
+        /// </param>
+        /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
+        /// <returns>
+        /// A task that will complete with a result of true if the current thread successfully entered 
+        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
+        /// </returns>
+        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+        /// disposed.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
+        /// which represents an infinite time-out.
+        /// </exception>
+        public Task<bool> WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
+        {
+            CheckDispose();
+
+            // Validate input
+            if (millisecondsTimeout < -1)
+            {
+                throw new ArgumentOutOfRangeException(
+                    nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
+            }
+
+            // Bail early for cancellation
+            if (cancellationToken.IsCancellationRequested)
+                return Task.FromCanceled<bool>(cancellationToken);
+
+            lock (m_lockObj)
+            {
+                // If there are counts available, allow this waiter to succeed.
+                if (m_currentCount > 0)
+                {
+                    --m_currentCount;
+                    if (m_waitHandle != null && m_currentCount == 0) m_waitHandle.Reset();
+                    return s_trueTask;
+                }
+                else if (millisecondsTimeout == 0)
+                {
+                    // No counts, if timeout is zero fail fast
+                    return s_falseTask;
+                }
+                // If there aren't, create and return a task to the caller.
+                // The task will be completed either when they've successfully acquired
+                // the semaphore or when the timeout expired or cancellation was requested.
+                else
+                {
+                    Debug.Assert(m_currentCount == 0, "m_currentCount should never be negative");
+                    var asyncWaiter = CreateAndAddAsyncWaiter();
+                    return (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled) ?
+                        asyncWaiter :
+                        WaitUntilCountOrTimeoutAsync(asyncWaiter, millisecondsTimeout, cancellationToken);
+                }
+            }
+        }
+
+        /// <summary>Creates a new task and stores it into the async waiters list.</summary>
+        /// <returns>The created task.</returns>
+        private TaskNode CreateAndAddAsyncWaiter()
+        {
+            Debug.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
+
+            // Create the task
+            var task = new TaskNode();
+
+            // Add it to the linked list
+            if (m_asyncHead == null)
+            {
+                Debug.Assert(m_asyncTail == null, "If head is null, so too should be tail");
+                m_asyncHead = task;
+                m_asyncTail = task;
+            }
+            else
+            {
+                Debug.Assert(m_asyncTail != null, "If head is not null, neither should be tail");
+                m_asyncTail.Next = task;
+                task.Prev = m_asyncTail;
+                m_asyncTail = task;
+            }
+
+            // Hand it back
+            return task;
+        }
+
+        /// <summary>Removes the waiter task from the linked list.</summary>
+        /// <param name="task">The task to remove.</param>
+        /// <returns>true if the waiter was in the list; otherwise, false.</returns>
+        private bool RemoveAsyncWaiter(TaskNode task)
+        {
+            Debug.Assert(task != null, "Expected non-null task");
+            Debug.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
+
+            // Is the task in the list?  To be in the list, either it's the head or it has a predecessor that's in the list.
+            bool wasInList = m_asyncHead == task || task.Prev != null;
+
+            // Remove it from the linked list
+            if (task.Next != null) task.Next.Prev = task.Prev;
+            if (task.Prev != null) task.Prev.Next = task.Next;
+            if (m_asyncHead == task) m_asyncHead = task.Next;
+            if (m_asyncTail == task) m_asyncTail = task.Prev;
+            Debug.Assert((m_asyncHead == null) == (m_asyncTail == null), "Head is null iff tail is null");
+
+            // Make sure not to leak
+            task.Next = task.Prev = null;
+
+            // Return whether the task was in the list
+            return wasInList;
+        }
+
+        /// <summary>Performs the asynchronous wait.</summary>
+        /// <param name="millisecondsTimeout">The timeout.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>The task to return to the caller.</returns>
+        private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken)
+        {
+            Debug.Assert(asyncWaiter != null, "Waiter should have been constructed");
+            Debug.Assert(Monitor.IsEntered(m_lockObj), "Requires the lock be held");
+
+            // Wait until either the task is completed, timeout occurs, or cancellation is requested.
+            // We need to ensure that the Task.Delay task is appropriately cleaned up if the await
+            // completes due to the asyncWaiter completing, so we use our own token that we can explicitly
+            // cancel, and we chain the caller's supplied token into it.
+            using (var cts = cancellationToken.CanBeCanceled ?
+                CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default) :
+                new CancellationTokenSource())
+            {
+                var waitCompleted = Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token));
+                if (asyncWaiter == await waitCompleted.ConfigureAwait(false))
+                {
+                    cts.Cancel(); // ensure that the Task.Delay task is cleaned up
+                    return true; // successfully acquired
+                }
+            }
+
+            // If we get here, the wait has timed out or been canceled.
+
+            // If the await completed synchronously, we still hold the lock.  If it didn't,
+            // we no longer hold the lock.  As such, acquire it.
+            lock (m_lockObj)
+            {
+                // Remove the task from the list.  If we're successful in doing so,
+                // we know that no one else has tried to complete this waiter yet,
+                // so we can safely cancel or timeout.
+                if (RemoveAsyncWaiter(asyncWaiter))
+                {
+                    cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred
+                    return false; // timeout occurred
+                }
+            }
+
+            // The waiter had already been removed, which means it's already completed or is about to
+            // complete, so let it, and don't return until it does.
+            return await asyncWaiter.ConfigureAwait(false);
+        }
+
+        /// <summary>
+        /// Exits the <see cref="SemaphoreSlim"/> once.
+        /// </summary>
+        /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
+        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+        /// disposed.</exception>
+        public int Release()
+        {
+            return Release(1);
+        }
+
+        /// <summary>
+        /// Exits the <see cref="SemaphoreSlim"/> a specified number of times.
+        /// </summary>
+        /// <param name="releaseCount">The number of times to exit the semaphore.</param>
+        /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
+        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="releaseCount"/> is less
+        /// than 1.</exception>
+        /// <exception cref="T:System.Threading.SemaphoreFullException">The <see cref="SemaphoreSlim"/> has
+        /// already reached its maximum size.</exception>
+        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
+        /// disposed.</exception>
+        public int Release(int releaseCount)
+        {
+            CheckDispose();
+
+            // Validate input
+            if (releaseCount < 1)
+            {
+                throw new ArgumentOutOfRangeException(
+                    nameof(releaseCount), releaseCount, SR.SemaphoreSlim_Release_CountWrong);
+            }
+            int returnCount;
+
+            lock (m_lockObj)
+            {
+                // Read the m_currentCount into a local variable to avoid unnecessary volatile accesses inside the lock.
+                int currentCount = m_currentCount;
+                returnCount = currentCount;
+
+                // If the release count would result exceeding the maximum count, throw SemaphoreFullException.
+                if (m_maxCount - currentCount < releaseCount)
+                {
+                    throw new SemaphoreFullException();
+                }
+
+                // Increment the count by the actual release count
+                currentCount += releaseCount;
+
+                // Signal to any synchronous waiters, taking into account how many waiters have previously been pulsed to wake
+                // but have not yet woken
+                int waitCount = m_waitCount;
+                Debug.Assert(m_countOfWaitersPulsedToWake <= waitCount);
+                int waitersToNotify = Math.Min(currentCount, waitCount) - m_countOfWaitersPulsedToWake;
+                if (waitersToNotify > 0)
+                {
+                    // Ideally, limiting to a maximum of releaseCount would not be necessary and could be an assert instead, but
+                    // since WaitUntilCountOrTimeout() does not have enough information to tell whether a woken thread was
+                    // pulsed, it's possible for m_countOfWaitersPulsedToWake to be less than the number of threads that have
+                    // actually been pulsed to wake.
+                    if (waitersToNotify > releaseCount)
+                    {
+                        waitersToNotify = releaseCount;
+                    }
+
+                    m_countOfWaitersPulsedToWake += waitersToNotify;
+                    for (int i = 0; i < waitersToNotify; i++)
+                    {
+                        Monitor.Pulse(m_lockObj);
+                    }
+                }
+
+                // Now signal to any asynchronous waiters, if there are any.  While we've already
+                // signaled the synchronous waiters, we still hold the lock, and thus
+                // they won't have had an opportunity to acquire this yet.  So, when releasing
+                // asynchronous waiters, we assume that all synchronous waiters will eventually
+                // acquire the semaphore.  That could be a faulty assumption if those synchronous
+                // waits are canceled, but the wait code path will handle that.
+                if (m_asyncHead != null)
+                {
+                    Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't null");
+                    int maxAsyncToRelease = currentCount - waitCount;
+                    while (maxAsyncToRelease > 0 && m_asyncHead != null)
+                    {
+                        --currentCount;
+                        --maxAsyncToRelease;
+
+                        // Get the next async waiter to release and queue it to be completed
+                        var waiterTask = m_asyncHead;
+                        RemoveAsyncWaiter(waiterTask); // ensures waiterTask.Next/Prev are null
+                        QueueWaiterTask(waiterTask);
+                    }
+                }
+                m_currentCount = currentCount;
+
+                // Exposing wait handle if it is not null
+                if (m_waitHandle != null && returnCount == 0 && currentCount > 0)
+                {
+                    m_waitHandle.Set();
+                }
+            }
+
+            // And return the count
+            return returnCount;
+        }
+
+        /// <summary>
+        /// Queues a waiter task to the ThreadPool. We use this small helper method so that
+        /// the larger Release(count) method does not need to be SecuritySafeCritical.
+        /// </summary>
+        private static void QueueWaiterTask(TaskNode waiterTask)
+        {
+            ThreadPool.UnsafeQueueCustomWorkItem(waiterTask, forceGlobal: false);
+        }
+
+        /// <summary>
+        /// Releases all resources used by the current instance of <see
+        /// cref="SemaphoreSlim"/>.
+        /// </summary>
+        /// <remarks>
+        /// Unlike most of the members of <see cref="SemaphoreSlim"/>, <see cref="Dispose()"/> is not
+        /// thread-safe and may not be used concurrently with other members of this instance.
+        /// </remarks>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// When overridden in a derived class, releases the unmanaged resources used by the 
+        /// <see cref="T:System.Threading.ManualResetEventSlim"/>, and optionally releases the managed resources.
+        /// </summary>
+        /// <param name="disposing">true to release both managed and unmanaged resources;
+        /// false to release only unmanaged resources.</param>
+        /// <remarks>
+        /// Unlike most of the members of <see cref="SemaphoreSlim"/>, <see cref="Dispose(bool)"/> is not
+        /// thread-safe and may not be used concurrently with other members of this instance.
+        /// </remarks>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                if (m_waitHandle != null)
+                {
+                    m_waitHandle.Dispose();
+                    m_waitHandle = null;
+                }
+                m_lockObj = null;
+                m_asyncHead = null;
+                m_asyncTail = null;
+            }
+        }
+
+        /// <summary>
+        /// Private helper method to wake up waiters when a cancellationToken gets canceled.
+        /// </summary>
+        private static Action<object> s_cancellationTokenCanceledEventHandler = new Action<object>(CancellationTokenCanceledEventHandler);
+        private static void CancellationTokenCanceledEventHandler(object obj)
+        {
+            SemaphoreSlim semaphore = obj as SemaphoreSlim;
+            Debug.Assert(semaphore != null, "Expected a SemaphoreSlim");
+            lock (semaphore.m_lockObj)
+            {
+                Monitor.PulseAll(semaphore.m_lockObj); //wake up all waiters.
+            }
+        }
+
+        /// <summary>
+        /// Checks the dispose status by checking the lock object, if it is null means that object
+        /// has been disposed and throw ObjectDisposedException
+        /// </summary>
+        private void CheckDispose()
+        {
+            if (m_lockObj == null)
+            {
+                throw new ObjectDisposedException(null, SR.SemaphoreSlim_Disposed);
+            }
+        }
+        #endregion
+    }
+}