SemaphoreSlim.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Threading / SemaphoreSlim.cs / 1305376 / SemaphoreSlim.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 
// SemaphoreSlim.cs 
//
// [....] 
//
// 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.Permissions;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts; 

// The class will be part of the current System.Threading namespace 
namespace System.Threading 
{
    ///  
    /// Limits the number of threads that can access a resource or pool of resources concurrently.
    /// 
    /// 
    ///  
    /// The  provides a lightweight semaphore class that doesn't
    /// use Windows kernel semaphores. 
    ///  
    /// 
    /// All public and protected members of  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  have
    /// completed.
    ///  
    /// 
    [ComVisible(false)] 
    [HostProtection(Synchronization = true, ExternalThreading = true)] 
    [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 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 volatile int m_waitCount; 

        // 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 ManualResetEvent m_waitHandle;
 
        // No maximum constant 
        private const int NO_MAXIMUM = Int32.MaxValue;
 
        #endregion

        #region Public properties
 
        /// 
        /// Gets the current count of the . 
        ///  
        /// The current count of the .
        public int CurrentCount 
        {
            get { return m_currentCount; }
        }
 
        /// 
        /// Returns a  that can be used to wait on the semaphore. 
        ///  
        /// A  that can be used to wait on the
        /// semaphore. 
        /// 
        /// A successful wait on the  does not imply a successful wait on
        /// the  itself, nor does it decrement the semaphore's
        /// count.  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.
        ///  
        /// The  has been disposed.
        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
 
        /// 
        /// Initializes a new instance of the  class, specifying
        /// the initial number of requests that can be granted concurrently.
        ///  
        /// The initial number of requests for the semaphore that can be granted
        /// concurrently. 
        ///  
        /// is less than 0.
        public SemaphoreSlim(int initialCount) 
            : this(initialCount, NO_MAXIMUM)
        {
        }
 
        /// 
        /// Initializes a new instance of the  class, specifying 
        /// the initial and maximum number of requests that can be granted concurrently. 
        /// 
        /// The initial number of requests for the semaphore that can be granted 
        /// concurrently.
        /// The maximum number of requests for the semaphore that can be granted
        /// concurrently.
        ///   
        /// is less than 0. -or-
        ///  is greater than . -or- 
        ///  is less than 0. 
        public SemaphoreSlim(int initialCount, int maxCount)
        { 
            if (initialCount < 0 || initialCount > maxCount)
            {
                throw new ArgumentOutOfRangeException(
                    "initialCount", initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong")); 
            }
 
            //validate input 
            if (maxCount <= 0)
            { 
                throw new ArgumentOutOfRangeException("maxCount", maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong"));
            }

            m_maxCount = maxCount; 
            m_lockObj = new object();
            m_currentCount = initialCount; 
        } 

        #endregion 

        #region  Methods
        /// 
        /// Blocks the current thread until it can enter the . 
        /// 
        /// The current instance has already been 
        /// disposed. 
        public void Wait()
        { 
            // Call wait with infinite timeout
            Wait(Timeout.Infinite, new CancellationToken());
        }
 
        /// 
        /// Blocks the current thread until it can enter the , while observing a 
        /// . 
        /// 
        /// The  token to 
        /// observe.
        ///  was
        /// canceled.
        /// The current instance has already been 
        /// disposed.
        public void Wait(CancellationToken cancellationToken) 
        { 
            // Call wait with infinite timeout
            Wait(Timeout.Infinite, cancellationToken); 
        }

        /// 
        /// Blocks the current thread until it can enter the , using a  to measure the time interval.
        ///  
        /// A  that represents the number of milliseconds 
        /// to wait, or a  that represents -1 milliseconds to wait indefinitely.
        ///  
        /// true if the current thread successfully entered the ;
        /// otherwise, false.
        ///  is a negative
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater 
        /// than .
        public bool Wait(TimeSpan timeout) 
        { 
            // Validate the timeout
            Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; 
            if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
            {
                throw new System.ArgumentOutOfRangeException(
                    "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); 
            }
 
            // Call wait with the timeout milliseconds 
            return Wait((int)timeout.TotalMilliseconds, new CancellationToken());
        } 

        /// 
        /// Blocks the current thread until it can enter the , using a  to measure the time interval, while observing a .
        ///  
        /// A  that represents the number of milliseconds 
        /// to wait, or a  that represents -1 milliseconds to wait indefinitely.
        ///  
        /// The  to
        /// observe.
        /// true if the current thread successfully entered the ;
        /// otherwise, false. 
        ///  is a negative
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater 
        /// than . 
        ///  was canceled.
        public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) 
        {
            // Validate the timeout
            Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
            if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) 
            {
                throw new System.ArgumentOutOfRangeException( 
                    "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); 
            }
 
            // Call wait with the timeout milliseconds
            return Wait((int)timeout.TotalMilliseconds, cancellationToken);
        }
 
        /// 
        /// Blocks the current thread until it can enter the , using a 32-bit 
        /// signed integer to measure the time interval. 
        /// 
        /// The number of milliseconds to wait, or (-1) to wait indefinitely.
        /// true if the current thread successfully entered the ;
        /// otherwise, false.
        ///  is a 
        /// negative number other than -1, which represents an infinite time-out.
        public bool Wait(int millisecondsTimeout) 
        { 
            return Wait(millisecondsTimeout, new CancellationToken());
        } 


        /// 
        /// Blocks the current thread until it can enter the , 
        /// using a 32-bit signed integer to measure the time interval,
        /// while observing a . 
        ///  
        /// The number of milliseconds to wait, or (-1) to
        /// wait indefinitely. 
        /// The  to observe.
        /// true if the current thread successfully entered the ; otherwise, false.
        ///  is a negative number other than -1,
        /// which represents an infinite time-out. 
        ///  was canceled.
        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) 
        { 
            CheckDispose();
 
            // Validate input
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException( 
                    "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
            } 
 
            cancellationToken.ThrowIfCancellationRequested();
 
            long startTimeTicks = 0;
            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0)
            {
                startTimeTicks = DateTime.UtcNow.Ticks; 
            }
 
            bool lockTaken = false; 

            //Register for cancellation outside of the main lock. 
            //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could
            //      occur for (1)this.m_lockObj and (2)cts.internalLock
            CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(s_cancellationTokenCanceledEventHandler, this);
            try 
            {
                // Perf: first spin wait for the count to be positive, but only up to the first planned yield. 
                //       This additional amount of spinwaiting in addition 
                //       to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios.
                // 
                SpinWait spin = new SpinWait();
                while (m_currentCount == 0 && !spin.NextSpinWillYield)
                {
                    spin.SpinOnce(); 
                }
                // 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 the count > 0 we are good to move on. 
                // If not, then wait if we were given allowed some wait duration
                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
                    if (!WaitUntilCountOrTimeout(millisecondsTimeout, startTimeTicks, cancellationToken))
                    {
                        return false; 
                    }
                } 
 
                // At this point the count should be greater than zero
                Contract.Assert(m_currentCount > 0); 
                m_currentCount--;

                // 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();
            }

            return true; 
        }
 
 

        ///  
        /// Local helper function, waits on the monitor until the monitor recieves signal or the
        /// timeout is expired
        /// 
        /// The maximum timeout 
        /// The start ticks to calculate the elapsed time
        /// The CancellationToken to observe. 
        /// true if the monitor recieved a signal, false if the timeout expired 
        private bool WaitUntilCountOrTimeout(int millisecondsTimeout, long startTimeTicks, 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 = UpdateTimeOut(startTimeTicks, millisecondsTimeout);
                    if (remainingWaitMilliseconds <= 0)
                    { 
                        // The thread has expires its timeout
                        return false; 
                    } 
                }
                // ** the actual wait ** 
                if (!Monitor.Wait(m_lockObj, remainingWaitMilliseconds))
                {
                    return false;
                } 
            }
 
            return true; 
        }
 
        /// 
        /// Exits the  once.
        /// 
        /// The previous count of the . 
        /// The current instance has already been
        /// disposed. 
        public int Release() 
        {
            return Release(1); 
        }

        /// 
        /// Exits the  a specified number of times. 
        /// 
        /// The number of times to exit the semaphore. 
        /// The previous count of the . 
        ///  is less
        /// than 1. 
        /// The  has
        /// already reached its maximum size.
        /// The current instance has already been
        /// disposed. 
        public int Release(int releaseCount)
        { 
            CheckDispose(); 

            // Validate input 
            if (releaseCount < 1)
            {
                throw new ArgumentOutOfRangeException(
                    "releaseCount", releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong")); 
            }
 
            lock (m_lockObj) 
            {
 
                // If thre release count would result exceeding the maximum count throw SemaphoreFullException
                if (m_maxCount - m_currentCount < releaseCount)
                {
                    throw new SemaphoreFullException(); 
                }
 
                // Increment the count by the actual release count 
                m_currentCount += releaseCount;
 
                if (m_currentCount == 1 || m_waitCount == 1)
                {
                    Monitor.Pulse(m_lockObj);
                } 
                else if (m_waitCount > 1)
                { 
                    Monitor.PulseAll(m_lockObj); 
                }
 

                // Exposing wait handle if it is not null
                if (m_waitHandle != null && m_currentCount - releaseCount == 0)
                { 
                    m_waitHandle.Set();
                } 
                return m_currentCount - releaseCount; 
            }
        } 

        /// 
        /// Releases all resources used by the current instance of . 
        /// 
        ///  
        /// Unlike most of the members of ,  is not 
        /// thread-safe and may not be used concurrently with other members of this instance.
        ///  
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); 
        }
 
        ///  
        /// When overridden in a derived class, releases the unmanaged resources used by the
        /// , and optionally releases the managed resources. 
        /// 
        /// true to release both managed and unmanaged resources;
        /// false to release only unmanaged resources.
        ///  
        /// Unlike most of the members of ,  is not
        /// thread-safe and may not be used concurrently with other members of this instance. 
        ///  
        protected virtual void Dispose(bool disposing)
        { 
            if (disposing)
            {
                if (m_waitHandle != null)
                { 
                    m_waitHandle.Close();
                    m_waitHandle = null; 
                } 
                m_lockObj = null;
            } 
        }

        /// 
        /// Helper function to measure and update the wait time 
        /// 
        ///  The first time (in Ticks) observed when the wait started 
        /// The orginal wait timeoutout in milliseconds 
        /// The new wait time in milliseconds, -1 if the time expired
        private static int UpdateTimeOut(long startTimeTicks, int originalWaitMillisecondsTimeout) 
        {
            // The function must be called in case the time out is not infinite
            Contract.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite);
 
            long elapsedMilliseconds = (DateTime.UtcNow.Ticks - startTimeTicks) / TimeSpan.TicksPerMillisecond;
 
            // Check the elapsed milliseconds is greater than max int because this property is long 
            if (elapsedMilliseconds > int.MaxValue)
            { 
                return 0;
            }

            // Subtract the elapsed time from the current wait time 
            int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ;
            if (currentWaitTimeout <= 0) 
            { 
                return 0;
            } 

            return currentWaitTimeout;
        }
 

        ///  
        /// Private helper method to wake up waiters when a cancellationToken gets canceled. 
        /// 
        private static Action s_cancellationTokenCanceledEventHandler = new Action(CancellationTokenCanceledEventHandler); 
        private static void CancellationTokenCanceledEventHandler(object obj)
        {
            SemaphoreSlim semaphore = obj as SemaphoreSlim;
            Contract.Assert(semaphore != null, "Expected a SemaphoreSlim"); 
            lock (semaphore.m_lockObj)
            { 
                Monitor.PulseAll(semaphore.m_lockObj); //wake up all waiters. 
            }
        } 

        /// 
        /// Checks the dispose status by checking the lock object, if it is null means that object
        /// has been disposed and throw ObjectDisposedException 
        /// 
        private void CheckDispose() 
        { 
            if (m_lockObj == null)
            { 
                throw new ObjectDisposedException(null, GetResourceString("SemaphoreSlim_Disposed"));
            }
        }
 
        /// 
        /// local helper function to retrieve the exception string message from the resource file 
        ///  
        /// The key string
        private static string GetResourceString(string str) 
        {
            return Environment.GetResourceString(str);
        }
        #endregion 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 
// SemaphoreSlim.cs 
//
// [....] 
//
// 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.Permissions;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts; 

// The class will be part of the current System.Threading namespace 
namespace System.Threading 
{
    ///  
    /// Limits the number of threads that can access a resource or pool of resources concurrently.
    /// 
    /// 
    ///  
    /// The  provides a lightweight semaphore class that doesn't
    /// use Windows kernel semaphores. 
    ///  
    /// 
    /// All public and protected members of  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  have
    /// completed.
    ///  
    /// 
    [ComVisible(false)] 
    [HostProtection(Synchronization = true, ExternalThreading = true)] 
    [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 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 volatile int m_waitCount; 

        // 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 ManualResetEvent m_waitHandle;
 
        // No maximum constant 
        private const int NO_MAXIMUM = Int32.MaxValue;
 
        #endregion

        #region Public properties
 
        /// 
        /// Gets the current count of the . 
        ///  
        /// The current count of the .
        public int CurrentCount 
        {
            get { return m_currentCount; }
        }
 
        /// 
        /// Returns a  that can be used to wait on the semaphore. 
        ///  
        /// A  that can be used to wait on the
        /// semaphore. 
        /// 
        /// A successful wait on the  does not imply a successful wait on
        /// the  itself, nor does it decrement the semaphore's
        /// count.  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.
        ///  
        /// The  has been disposed.
        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
 
        /// 
        /// Initializes a new instance of the  class, specifying
        /// the initial number of requests that can be granted concurrently.
        ///  
        /// The initial number of requests for the semaphore that can be granted
        /// concurrently. 
        ///  
        /// is less than 0.
        public SemaphoreSlim(int initialCount) 
            : this(initialCount, NO_MAXIMUM)
        {
        }
 
        /// 
        /// Initializes a new instance of the  class, specifying 
        /// the initial and maximum number of requests that can be granted concurrently. 
        /// 
        /// The initial number of requests for the semaphore that can be granted 
        /// concurrently.
        /// The maximum number of requests for the semaphore that can be granted
        /// concurrently.
        ///   
        /// is less than 0. -or-
        ///  is greater than . -or- 
        ///  is less than 0. 
        public SemaphoreSlim(int initialCount, int maxCount)
        { 
            if (initialCount < 0 || initialCount > maxCount)
            {
                throw new ArgumentOutOfRangeException(
                    "initialCount", initialCount, GetResourceString("SemaphoreSlim_ctor_InitialCountWrong")); 
            }
 
            //validate input 
            if (maxCount <= 0)
            { 
                throw new ArgumentOutOfRangeException("maxCount", maxCount, GetResourceString("SemaphoreSlim_ctor_MaxCountWrong"));
            }

            m_maxCount = maxCount; 
            m_lockObj = new object();
            m_currentCount = initialCount; 
        } 

        #endregion 

        #region  Methods
        /// 
        /// Blocks the current thread until it can enter the . 
        /// 
        /// The current instance has already been 
        /// disposed. 
        public void Wait()
        { 
            // Call wait with infinite timeout
            Wait(Timeout.Infinite, new CancellationToken());
        }
 
        /// 
        /// Blocks the current thread until it can enter the , while observing a 
        /// . 
        /// 
        /// The  token to 
        /// observe.
        ///  was
        /// canceled.
        /// The current instance has already been 
        /// disposed.
        public void Wait(CancellationToken cancellationToken) 
        { 
            // Call wait with infinite timeout
            Wait(Timeout.Infinite, cancellationToken); 
        }

        /// 
        /// Blocks the current thread until it can enter the , using a  to measure the time interval.
        ///  
        /// A  that represents the number of milliseconds 
        /// to wait, or a  that represents -1 milliseconds to wait indefinitely.
        ///  
        /// true if the current thread successfully entered the ;
        /// otherwise, false.
        ///  is a negative
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater 
        /// than .
        public bool Wait(TimeSpan timeout) 
        { 
            // Validate the timeout
            Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; 
            if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
            {
                throw new System.ArgumentOutOfRangeException(
                    "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); 
            }
 
            // Call wait with the timeout milliseconds 
            return Wait((int)timeout.TotalMilliseconds, new CancellationToken());
        } 

        /// 
        /// Blocks the current thread until it can enter the , using a  to measure the time interval, while observing a .
        ///  
        /// A  that represents the number of milliseconds 
        /// to wait, or a  that represents -1 milliseconds to wait indefinitely.
        ///  
        /// The  to
        /// observe.
        /// true if the current thread successfully entered the ;
        /// otherwise, false. 
        ///  is a negative
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater 
        /// than . 
        ///  was canceled.
        public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) 
        {
            // Validate the timeout
            Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
            if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) 
            {
                throw new System.ArgumentOutOfRangeException( 
                    "timeout", timeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); 
            }
 
            // Call wait with the timeout milliseconds
            return Wait((int)timeout.TotalMilliseconds, cancellationToken);
        }
 
        /// 
        /// Blocks the current thread until it can enter the , using a 32-bit 
        /// signed integer to measure the time interval. 
        /// 
        /// The number of milliseconds to wait, or (-1) to wait indefinitely.
        /// true if the current thread successfully entered the ;
        /// otherwise, false.
        ///  is a 
        /// negative number other than -1, which represents an infinite time-out.
        public bool Wait(int millisecondsTimeout) 
        { 
            return Wait(millisecondsTimeout, new CancellationToken());
        } 


        /// 
        /// Blocks the current thread until it can enter the , 
        /// using a 32-bit signed integer to measure the time interval,
        /// while observing a . 
        ///  
        /// The number of milliseconds to wait, or (-1) to
        /// wait indefinitely. 
        /// The  to observe.
        /// true if the current thread successfully entered the ; otherwise, false.
        ///  is a negative number other than -1,
        /// which represents an infinite time-out. 
        ///  was canceled.
        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) 
        { 
            CheckDispose();
 
            // Validate input
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException( 
                    "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong"));
            } 
 
            cancellationToken.ThrowIfCancellationRequested();
 
            long startTimeTicks = 0;
            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0)
            {
                startTimeTicks = DateTime.UtcNow.Ticks; 
            }
 
            bool lockTaken = false; 

            //Register for cancellation outside of the main lock. 
            //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could
            //      occur for (1)this.m_lockObj and (2)cts.internalLock
            CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(s_cancellationTokenCanceledEventHandler, this);
            try 
            {
                // Perf: first spin wait for the count to be positive, but only up to the first planned yield. 
                //       This additional amount of spinwaiting in addition 
                //       to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios.
                // 
                SpinWait spin = new SpinWait();
                while (m_currentCount == 0 && !spin.NextSpinWillYield)
                {
                    spin.SpinOnce(); 
                }
                // 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 the count > 0 we are good to move on. 
                // If not, then wait if we were given allowed some wait duration
                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
                    if (!WaitUntilCountOrTimeout(millisecondsTimeout, startTimeTicks, cancellationToken))
                    {
                        return false; 
                    }
                } 
 
                // At this point the count should be greater than zero
                Contract.Assert(m_currentCount > 0); 
                m_currentCount--;

                // 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();
            }

            return true; 
        }
 
 

        ///  
        /// Local helper function, waits on the monitor until the monitor recieves signal or the
        /// timeout is expired
        /// 
        /// The maximum timeout 
        /// The start ticks to calculate the elapsed time
        /// The CancellationToken to observe. 
        /// true if the monitor recieved a signal, false if the timeout expired 
        private bool WaitUntilCountOrTimeout(int millisecondsTimeout, long startTimeTicks, 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 = UpdateTimeOut(startTimeTicks, millisecondsTimeout);
                    if (remainingWaitMilliseconds <= 0)
                    { 
                        // The thread has expires its timeout
                        return false; 
                    } 
                }
                // ** the actual wait ** 
                if (!Monitor.Wait(m_lockObj, remainingWaitMilliseconds))
                {
                    return false;
                } 
            }
 
            return true; 
        }
 
        /// 
        /// Exits the  once.
        /// 
        /// The previous count of the . 
        /// The current instance has already been
        /// disposed. 
        public int Release() 
        {
            return Release(1); 
        }

        /// 
        /// Exits the  a specified number of times. 
        /// 
        /// The number of times to exit the semaphore. 
        /// The previous count of the . 
        ///  is less
        /// than 1. 
        /// The  has
        /// already reached its maximum size.
        /// The current instance has already been
        /// disposed. 
        public int Release(int releaseCount)
        { 
            CheckDispose(); 

            // Validate input 
            if (releaseCount < 1)
            {
                throw new ArgumentOutOfRangeException(
                    "releaseCount", releaseCount, GetResourceString("SemaphoreSlim_Release_CountWrong")); 
            }
 
            lock (m_lockObj) 
            {
 
                // If thre release count would result exceeding the maximum count throw SemaphoreFullException
                if (m_maxCount - m_currentCount < releaseCount)
                {
                    throw new SemaphoreFullException(); 
                }
 
                // Increment the count by the actual release count 
                m_currentCount += releaseCount;
 
                if (m_currentCount == 1 || m_waitCount == 1)
                {
                    Monitor.Pulse(m_lockObj);
                } 
                else if (m_waitCount > 1)
                { 
                    Monitor.PulseAll(m_lockObj); 
                }
 

                // Exposing wait handle if it is not null
                if (m_waitHandle != null && m_currentCount - releaseCount == 0)
                { 
                    m_waitHandle.Set();
                } 
                return m_currentCount - releaseCount; 
            }
        } 

        /// 
        /// Releases all resources used by the current instance of . 
        /// 
        ///  
        /// Unlike most of the members of ,  is not 
        /// thread-safe and may not be used concurrently with other members of this instance.
        ///  
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); 
        }
 
        ///  
        /// When overridden in a derived class, releases the unmanaged resources used by the
        /// , and optionally releases the managed resources. 
        /// 
        /// true to release both managed and unmanaged resources;
        /// false to release only unmanaged resources.
        ///  
        /// Unlike most of the members of ,  is not
        /// thread-safe and may not be used concurrently with other members of this instance. 
        ///  
        protected virtual void Dispose(bool disposing)
        { 
            if (disposing)
            {
                if (m_waitHandle != null)
                { 
                    m_waitHandle.Close();
                    m_waitHandle = null; 
                } 
                m_lockObj = null;
            } 
        }

        /// 
        /// Helper function to measure and update the wait time 
        /// 
        ///  The first time (in Ticks) observed when the wait started 
        /// The orginal wait timeoutout in milliseconds 
        /// The new wait time in milliseconds, -1 if the time expired
        private static int UpdateTimeOut(long startTimeTicks, int originalWaitMillisecondsTimeout) 
        {
            // The function must be called in case the time out is not infinite
            Contract.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite);
 
            long elapsedMilliseconds = (DateTime.UtcNow.Ticks - startTimeTicks) / TimeSpan.TicksPerMillisecond;
 
            // Check the elapsed milliseconds is greater than max int because this property is long 
            if (elapsedMilliseconds > int.MaxValue)
            { 
                return 0;
            }

            // Subtract the elapsed time from the current wait time 
            int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ;
            if (currentWaitTimeout <= 0) 
            { 
                return 0;
            } 

            return currentWaitTimeout;
        }
 

        ///  
        /// Private helper method to wake up waiters when a cancellationToken gets canceled. 
        /// 
        private static Action s_cancellationTokenCanceledEventHandler = new Action(CancellationTokenCanceledEventHandler); 
        private static void CancellationTokenCanceledEventHandler(object obj)
        {
            SemaphoreSlim semaphore = obj as SemaphoreSlim;
            Contract.Assert(semaphore != null, "Expected a SemaphoreSlim"); 
            lock (semaphore.m_lockObj)
            { 
                Monitor.PulseAll(semaphore.m_lockObj); //wake up all waiters. 
            }
        } 

        /// 
        /// Checks the dispose status by checking the lock object, if it is null means that object
        /// has been disposed and throw ObjectDisposedException 
        /// 
        private void CheckDispose() 
        { 
            if (m_lockObj == null)
            { 
                throw new ObjectDisposedException(null, GetResourceString("SemaphoreSlim_Disposed"));
            }
        }
 
        /// 
        /// local helper function to retrieve the exception string message from the resource file 
        ///  
        /// The key string
        private static string GetResourceString(string str) 
        {
            return Environment.GetResourceString(str);
        }
        #endregion 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK