CountdownEvent.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// 
// CountdownEvent.cs 
//
// [....] 
//
// A simple coordination data structure that we use for fork/join style parallelism.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 

using System; 
using System.Diagnostics; 
using System.Security.Permissions;
using System.Runtime.InteropServices; 
using System.Threading;
using System.Diagnostics.Contracts;

namespace System.Threading 
{
 
    ///  
    /// Represents a synchronization primitive that is signaled when its count reaches zero.
    ///  
    /// 
    /// 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, and Reset, which should only be used when no other threads are
    /// accessing the event. 
    ///  
    [ComVisible(false)]
    [DebuggerDisplay("Initial Count={InitialCount}, Current Count={CurrentCount}")] 
    [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
    public class CountdownEvent : IDisposable
    {
        // CountdownEvent is a simple synchronization primitive used for fork/join parallelism. We create a 
        // latch with a count of N; threads then signal the latch, which decrements N by 1; other threads can
        // wait on the latch at any point; when the latch count reaches 0, all threads are woken and 
        // subsequent waiters return without waiting. The implementation internally lazily creates a true 
        // Win32 event as needed. We also use some amount of spinning on MP machines before falling back to a
        // wait. 

        private int m_initialCount; // The original # of signals the latch was instantiated with.
        private volatile int m_currentCount;  // The # of outstanding signals before the latch transitions to a signaled state.
        private ManualResetEventSlim m_event;   // An event used to manage blocking and signaling. 
        private volatile bool m_disposed; // Whether the latch has been disposed.
 
        ///  
        /// Initializes a new instance of  class with the
        /// specified count. 
        /// 
        /// The number of signals required to set the .
        ///  is less 
        /// than 0.
        public CountdownEvent(int initialCount) 
        { 
            if (initialCount < 0)
            { 
                throw new ArgumentOutOfRangeException("initialCount");
            }

            m_initialCount = initialCount; 
            m_currentCount = initialCount;
 
            // Allocate a thin event, which internally defers creation of an actual Win32 event. 
            m_event = new ManualResetEventSlim();
 
            // If the latch was created with a count of 0, then it's already in the signaled state.
            if (initialCount == 0)
            {
                m_event.Set(); 
            }
        } 
 
        /// 
        /// Gets the number of remaining signals required to set the event. 
        /// 
        /// 
        /// The number of remaining signals required to set the event.
        ///  
        public int CurrentCount
        { 
            get { return m_currentCount; } 
        }
 
        /// 
        /// Gets the numbers of signals initially required to set the event.
        /// 
        ///  
        /// The number of signals initially required to set the event.
        ///  
        public int InitialCount 
        {
            get { return m_initialCount; } 
        }

        /// 
        /// Determines whether the event is set. 
        /// 
        /// true if the event is set; otherwise, false. 
        public bool IsSet 
        {
            get 
            {
                // The latch is "completed" if its current count has reached 0. Note that this is NOT
                // the same thing is checking the event's IsCompleted property. There is a tiny window
                // of time, after the final decrement of the current count to 0 and before setting the 
                // event, where the two values are out of [....].
                return (m_currentCount == 0); 
            } 
        }
 
        /// 
        /// Gets a  that is used to wait for the event to be set.
        /// 
        /// A  that is used to wait for the event to be set. 
        /// The current instance has already been disposed.
        ///  
        ///  should only be used if it's needed for integration with code bases 
        /// that rely on having a WaitHandle.  If all that's needed is to wait for the 
        /// to be set, the  method should be preferred. 
        /// 
        public WaitHandle WaitHandle
        {
            get 
            {
                ThrowIfDisposed(); 
                return m_event.WaitHandle; 
            }
        } 

        /// 
        /// 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() 
        {
            // Gets rid of this latch's associated resources. This can consist of a Win32 event
            // which is (lazily) allocated by the underlying thin event. This method is not safe to
            // call concurrently -- i.e. a caller must coordinate to ensure only one thread is using 
            // the latch at the time of the call to 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)
            { 
                m_event.Dispose(); 
                m_disposed = true;
            } 
        }

        /// 
        /// Registers a signal with the , decrementing its 
        /// count.
        ///  
        /// true if the signal caused the count to reach zero and the event was set; otherwise, 
        /// false.
        /// The current instance is already set. 
        /// 
        /// The current instance has already been
        /// disposed.
        public bool Signal() 
        {
            return Signal(1); 
        } 

        ///  
        /// Registers multiple signals with the ,
        /// decrementing its count by the specified amount.
        /// 
        /// The number of signals to register. 
        /// true if the signals caused the count to reach zero and the event was set; otherwise,
        /// false. 
        ///  
        /// The current instance is already set. -or- Or  is greater than . 
        /// 
        ///  is less
        /// than 1.
        /// The current instance has already been 
        /// disposed.
        public bool Signal(int signalCount) 
        { 
            if (signalCount <= 0)
            { 
                throw new ArgumentOutOfRangeException("signalCount");
            }

            ThrowIfDisposed(); 
            Contract.Assert(m_event != null);
 
            int observedCount; 
            SpinWait spin = new SpinWait();
            while (true) 
            {
                observedCount = m_currentCount;

                // If the latch is already signaled, we will fail. 
                if (observedCount < signalCount)
                { 
                    throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); 
                }
 
                // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning
                // for this statement.  This warning is clearly senseless for Interlocked operations.
#pragma warning disable 0420
                if (Interlocked.CompareExchange(ref m_currentCount, observedCount - signalCount, observedCount) == observedCount) 
#pragma warning restore 0420
                { 
                    break; 
                }
 
                // The CAS failed.  Spin briefly and try again.
                spin.SpinOnce();
            }
 
            // If we were the last to signal, set the event.
            if (observedCount == signalCount) 
            { 
                m_event.Set();
                return true; 
            }

            Contract.Assert(m_currentCount >= 0, "latch was decremented below zero");
            return false; 
        }
 
        ///  
        /// Increments the 's current count by one.
        ///  
        /// The current instance is already
        /// set.
        ///  is equal to . 
        /// 
        /// The current instance has already been disposed. 
        ///  
        public void AddCount()
        { 
            AddCount(1);
        }

        ///  
        /// Attempts to increment the 's current count by one.
        ///  
        /// true if the increment succeeded; otherwise, false. If  is 
        /// already at zero. this will return false.
        ///  is equal to .
        /// The current instance has already been
        /// disposed.
        public bool TryAddCount() 
        {
            return TryAddCount(1); 
        } 

        ///  
        /// Increments the 's current count by a specified
        /// value.
        /// 
        /// The value by which to increase . 
        ///  is less than
        /// 0. 
        /// The current instance is already 
        /// set.
        ///  is equal to .
        /// The current instance has already been
        /// disposed.
        public void AddCount(int signalCount) 
        {
            if (!TryAddCount(signalCount)) 
            { 
                throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyZero"));
            } 
        }

        /// 
        /// Attempts to increment the 's current count by a 
        /// specified value.
        ///  
        /// The value by which to increase . 
        /// true if the increment succeeded; otherwise, false. If  is
        /// already at zero this will return false. 
        ///  is less
        /// than 0.
        /// The current instance is already
        /// set. 
        ///  is equal to . 
        /// The current instance has already been 
        /// disposed.
        public bool TryAddCount(int signalCount) 
        {
            if (signalCount <= 0)
            {
                throw new ArgumentOutOfRangeException("signalCount"); 
            }
 
            ThrowIfDisposed(); 

            // Loop around until we successfully increment the count. 
            int observedCount;
            SpinWait spin = new SpinWait();
            while (true)
            { 
                observedCount = m_currentCount;
 
                if (observedCount == 0) 
                {
                    return false; 
                }
                else if (observedCount > (Int32.MaxValue - signalCount))
                {
                    throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyMax")); 
                }
 
                // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning 
                // for this statement.  This warning is clearly senseless for Interlocked operations.
#pragma warning disable 0420 
                if (Interlocked.CompareExchange(ref m_currentCount, observedCount + signalCount, observedCount) == observedCount)
#pragma warning restore 0420
                {
                    break; 
                }
 
                // The CAS failed.  Spin briefly and try again. 
                spin.SpinOnce();
            } 

            return true;
        }
 
        /// 
        /// Resets the  to the value of . 
        ///  
        /// 
        /// Unlike most of the members of , Reset is not 
        /// thread-safe and may not be used concurrently with other members of this instance.
        /// 
        /// The current instance has already been
        /// disposed.. 
        public void Reset()
        { 
            Reset(m_initialCount); 
        }
 
        /// 
        /// Resets the  to a specified value.
        /// 
        /// The number of signals required to set the .
        ///  
        /// Unlike most of the members of , Reset is not 
        /// thread-safe and may not be used concurrently with other members of this instance.
        ///  
        ///  is
        /// less than 0.
        /// The current instance has alread been disposed.
        public void Reset(int count) 
        {
            ThrowIfDisposed(); 
 
            if (count < 0)
            { 
                throw new ArgumentOutOfRangeException("count");
            }

            m_currentCount = count; 
            m_initialCount = count;
 
            if (count == 0) 
            {
                m_event.Set(); 
            }
            else
            {
                m_event.Reset(); 
            }
        } 
 
        /// 
        /// Blocks the current thread until the  is set. 
        /// 
        /// 
        /// The caller of this method blocks indefinitely until the current instance is set. The caller will
        /// return immediately if the event is currently in a set state. 
        /// 
        /// The current instance has already been 
        /// disposed. 
        public void Wait()
        { 
            Wait(Timeout.Infinite, new CancellationToken());
        }

 
        /// 
        /// Blocks the current thread until the  is set, while 
        /// observing a . 
        /// 
        /// The  to 
        /// observe.
        /// 
        /// The caller of this method blocks indefinitely until the current instance is set. The caller will
        /// return immediately if the event is currently in a set state.  If the 
        /// CancellationToken being observed
        /// is canceled during the wait operation, an  
        /// will be thrown. 
        /// 
        ///  has been 
        /// canceled.
        /// The current instance has already been
        /// disposed.
        public void Wait(CancellationToken cancellationToken) 
        {
            Wait(Timeout.Infinite, cancellationToken); 
        } 

        ///  
        /// Blocks the current thread until the  is set, 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  was set; otherwise, 
        /// false.
        ///  is a negative 
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
        /// than .
        /// The current instance has already been
        /// disposed. 
        public bool Wait(TimeSpan timeout)
        { 
            long totalMilliseconds = (long)timeout.TotalMilliseconds; 
            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
            { 
                throw new ArgumentOutOfRangeException("timeout");
            }

            return Wait((int)totalMilliseconds, new CancellationToken()); 
        }
 
        ///  
        /// Blocks the current thread until the  is set, 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  was set; otherwise,
        /// false. 
        ///  is a negative
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
        /// than .
        /// The current instance has already been 
        /// disposed.
        ///  has 
        /// been canceled. 
        public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
        { 
            long totalMilliseconds = (long)timeout.TotalMilliseconds;
            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
            {
                throw new ArgumentOutOfRangeException("timeout"); 
            }
 
            return Wait((int)totalMilliseconds, cancellationToken); 
        }
 
        /// 
        /// Blocks the current thread until the  is set, 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  was set; otherwise, 
        /// false.
        ///  is a 
        /// negative number other than -1, which represents an infinite time-out.
        /// The current instance has already been
        /// disposed.
        public bool Wait(int millisecondsTimeout) 
        {
            return Wait(millisecondsTimeout, new CancellationToken()); 
        } 

        ///  
        /// Blocks the current thread until the  is set, 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  was set; otherwise, 
        /// false.
        ///  is a
        /// negative number other than -1, which represents an infinite time-out.
        /// The current instance has already been 
        /// disposed.
        ///  has 
        /// been canceled. 
        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
        { 
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException("millisecondsTimeout");
            } 

            ThrowIfDisposed(); 
            cancellationToken.ThrowIfCancellationRequested(); 

            bool returnValue = IsSet; 

            // If not completed yet, wait on the event.
            if (!returnValue)
            { 
                // ** the actual wait
                returnValue = m_event.Wait(millisecondsTimeout, cancellationToken); 
                //the Wait will throw OCE itself if the token is canceled. 
            }
 
            return returnValue;
        }

        // -------------------------------------- 
        // Private methods
 
 
        /// 
        /// Throws an exception if the latch has been disposed. 
        /// 
        private void ThrowIfDisposed()
        {
            if (m_disposed) 
            {
                throw new ObjectDisposedException("CountdownEvent"); 
            } 
        }
    } 
}

// 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