ManualResetEventSlim.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 / ManualResetEventSlim.cs / 1305376 / ManualResetEventSlim.cs

                            #pragma warning disable 0420 
// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 
// 
// SlimManualResetEvent.cs
// 
// [....]
//
// An manual-reset event that mixes a little spinning with a true Win32 event.
// 
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
using System; 
using System.Diagnostics;
using System.Security.Permissions; 
using System.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
 
namespace System.Threading
{ 
 
    // ManualResetEventSlim wraps a manual-reset event internally with a little bit of
    // spinning. When an event will be set imminently, it is often advantageous to avoid 
    // a 4k+ cycle context switch in favor of briefly spinning. Therefore we layer on to
    // a brief amount of spinning that should, on the average, make using the slim event
    // cheaper than using Win32 events directly. This can be reset manually, much like
    // a Win32 manual-reset would be. 
    //
    // Notes: 
    //     We lazily allocate the Win32 event internally. Therefore, the caller should 
    //     always call Dispose to clean it up, just in case. This API is a no-op of the
    //     event wasn't allocated, but if it was, ensures that the event goes away 
    //     eagerly, instead of waiting for finalization.

    /// 
    /// Provides a slimmed down version of . 
    /// 
    ///  
    /// 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("Set = {IsSet}")]
    [HostProtection(Synchronization = true, ExternalThreading = true)] 
    public class ManualResetEventSlim : IDisposable 
    {
        // These are the default spin counts we use on single-proc and MP machines. 
        private const int DEFAULT_SPIN_SP = 1;
        private const int DEFAULT_SPIN_MP = SpinWait.YIELD_THRESHOLD;

        private object m_lock; 
        // A lock used for waiting and pulsing. Lazily initialized via EnsureLockObjectCreated()
        // This would normally be tagged 'volatile', but we can avoid this as it is only used nearby 
        // access to Waiters which internally uses the volatile m_combinedState.  See specific 
        // comments in Set(bool duringCancellation)
 
        private ManualResetEvent m_eventObj; // A true Win32 event used for waiting.

        // -- State -- //
        //For a packed word a uint would seem better, but Interlocked.* doesn't support them as uint isn't CLS-compliant. 
        private volatile int m_combinedState; //ie a UInt32. Used for the state items listed below.
 
        //1-bit for  signalled state 
        private const int SignalledState_BitMask = unchecked((int)0x80000000);//1000 0000 0000 0000 0000 0000 0000 0000
        private const int SignalledState_ShiftCount = 31; 

        //1-bit for disposed state
        private const int Dispose_BitMask = unchecked((int)0x40000000);//0100 0000 0000 0000 0000 0000 0000 0000
 
        //11-bits for m_spinCount
        private const int SpinCountState_BitMask = unchecked((int)0x3FF80000); //0011 1111 1111 1000 0000 0000 0000 0000 
        private const int SpinCountState_ShiftCount = 19; 
        private const int SpinCountState_MaxValue = (1 << 11) - 1; //2047
 
        //19-bits for m_waiters.  This allows support of 512K threads waiting which should be ample
        private const int NumWaitersState_BitMask = unchecked((int)0x0007FFFF); // 0000 0000 0000 0111 1111 1111 1111 1111
        private const int NumWaitersState_ShiftCount = 0;
        private const int NumWaitersState_MaxValue = (1 << 19) - 1; //512K-1 
        // ----------- //
 
#if DEBUG 
        private static int s_nextId; // The next id that will be given out.
        private int m_id = Interlocked.Increment(ref s_nextId); // A unique id for debugging purposes only. 
        private long m_lastSetTime;
        private long m_lastResetTime;
#endif
 
        /// 
        /// Gets the underlying  object for this . 
        /// 
        /// The underlying  event object fore this .
        /// 
        /// Accessing this property forces initialization of an underlying event object if one hasn't
        /// already been created.  To simply wait on this , 
        /// the public Wait methods should be preferred.
        ///  
        public WaitHandle WaitHandle 
        {
 
            get
            {
                ThrowIfDisposed();
                if (m_eventObj == null) 
                {
                    // Lazily initialize the event object if needed. 
                    LazyInitializeEvent(); 
                }
 
                return m_eventObj;
            }
        }
 
        /// 
        /// Gets whether the event is set. 
        ///  
        /// true if the event has is set; otherwise, false.
        public bool IsSet 
        {
            get
            {
                return 0 != ExtractStatePortion(m_combinedState, SignalledState_BitMask); 
            }
 
            private set 
            {
                UpdateStateAtomically(((value) ? 1 : 0) << SignalledState_ShiftCount, SignalledState_BitMask); 
            }
        }

        ///  
        /// Gets the number of spin waits that will be occur before falling back to a true wait.
        ///  
        public int SpinCount 
        {
            get 
            {
                return ExtractStatePortionAndShiftRight(m_combinedState, SpinCountState_BitMask, SpinCountState_ShiftCount);
            }
 
            private set
            { 
                Contract.Assert(value >= 0, "SpinCount is a restricted-width integer. The value supplied is outside the legal range."); 
                Contract.Assert(value <= SpinCountState_MaxValue, "SpinCount is a restricted-width integer. The value supplied is outside the legal range.");
                // Don't worry about thread safety because it's set one time from the constructor 
                m_combinedState = (m_combinedState & ~SpinCountState_BitMask) | (value << SpinCountState_ShiftCount);
            }
        }
 
        /// 
        /// How many threads are waiting. 
        ///  
        private int Waiters
        { 
            get
            {
                return ExtractStatePortionAndShiftRight(m_combinedState, NumWaitersState_BitMask, NumWaitersState_ShiftCount);
            } 

            set 
            { 
                //setting to <0 would indicate an internal flaw, hence Assert is appropriate.
                Contract.Assert(value >= 0, "NumWaiters should never be less than zero. This indicates an internal error."); 

                // it is possible for the max number of waiters to be exceeded via user-code, hence we use a real exception here.
                if (value >= NumWaitersState_MaxValue)
                    throw new InvalidOperationException(String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_TooManyWaiters"), NumWaitersState_MaxValue)); 

                UpdateStateAtomically(value << NumWaitersState_ShiftCount, NumWaitersState_BitMask); 
            } 

        } 

        //------------------------------------------------------------------------------------
        // Constructs a new event, optionally specifying the initial state and spin count.
        // The defaults are that the event is unsignaled and some reasonable default spin. 
        //
 
        ///  
        /// Initializes a new instance of the 
        /// class with an initial state of nonsignaled. 
        /// 
        public ManualResetEventSlim()
            : this(false)
        { 

        } 
 
        /// 
        /// Initializes a new instance of the  
        /// class with a Boolen value indicating whether to set the intial state to signaled.
        /// 
        /// true to set the initial state signaled; false to set the initial state
        /// to nonsignaled. 
        public ManualResetEventSlim(bool initialState)
        { 
            // Specify the defualt spin count, and use default spin if we're 
            // on a multi-processor machine. Otherwise, we won't.
            Initialize(initialState, DEFAULT_SPIN_MP); 
        }

        /// 
        /// Initializes a new instance of the  
        /// class with a Boolen value indicating whether to set the intial state to signaled and a specified
        /// spin count. 
        ///  
        /// true to set the initial state to signaled; false to set the initial state
        /// to nonsignaled. 
        /// The number of spin waits that will occur before falling back to a true
        /// wait.
        ///  is less than
        /// 0 or greater than the maximum allowed value. 
        public ManualResetEventSlim(bool initialState, int spinCount)
        { 
            if (spinCount < 0) 
            {
                throw new ArgumentOutOfRangeException("spinCount"); 
            }

            if (spinCount > SpinCountState_MaxValue)
            { 
                throw new ArgumentOutOfRangeException(
                    "spinCount", 
                    String.Format(Environment.GetResourceString("ManualResetEventSlim_ctor_SpinCountOutOfRange"), SpinCountState_MaxValue)); 
            }
 
            // We will suppress default spin  because the user specified a count.
            Initialize(initialState, spinCount);
        }
 
        /// 
        /// Initializes the internal state of the event. 
        ///  
        /// Whether the event is set initially or not.
        /// The spin count that decides when the event will block. 
        private void Initialize(bool initialState, int spinCount)
        {
            IsSet = initialState;
 
            //the spinCount argument has been validated by the ctors.
            //but we now sanity check our predefined constants. 
            Contract.Assert(DEFAULT_SPIN_SP >= 0, "Internal error - DEFAULT_SPIN_SP is outside the legal range."); 
            Contract.Assert(DEFAULT_SPIN_SP <= SpinCountState_MaxValue, "Internal error - DEFAULT_SPIN_SP is outside the legal range.");
 
            SpinCount = PlatformHelper.IsSingleProcessor ? DEFAULT_SPIN_SP : spinCount;

        }
 
        /// 
        /// Helper to ensure the lock object is created before first use. 
        ///  
        private void EnsureLockObjectCreated()
        { 
            Contract.Ensures(m_lock != null);

            if (m_lock != null)
                return; 

            object newObj = new object(); 
            Interlocked.CompareExchange(ref m_lock, newObj, null); // failure is benign.. someone else won the ----. 
        }
 
        /// 
        /// This method lazily initializes the event object. It uses CAS to guarantee that
        /// many threads racing to call this at once don't result in more than one event
        /// being stored and used. The event will be signaled or unsignaled depending on 
        /// the state of the thin-event itself, with synchronization taken into account.
        ///  
        /// True if a new event was created and stored, false otherwise. 
        private bool LazyInitializeEvent()
        { 
            bool preInitializeIsSet = IsSet;
            ManualResetEvent newEventObj = new ManualResetEvent(preInitializeIsSet);

            // We have to CAS this in case we are racing with another thread. We must 
            // guarantee only one event is actually stored in this field.
            if (Interlocked.CompareExchange(ref m_eventObj, newEventObj, null) != null) 
            { 
                // We ----d with someone else and lost. Destroy the garbage event.
                newEventObj.Close(); 

                return false;
            }
            else 
            {
 
                // Now that the event is published, verify that the state hasn't changed since 
                // we snapped the preInitializeState. Another thread could have done that
                // between our initial observation above and here. The barrier incurred from 
                // the CAS above (in addition to m_state being volatile) prevents this read
                // from moving earlier and being collapsed with our original one.
                bool currentIsSet = IsSet;
                if (currentIsSet != preInitializeIsSet) 
                {
                    Contract.Assert(currentIsSet, 
                        "The only safe concurrent transition is from unset->set: detected set->unset."); 

                    // We saw it as unsignaled, but it has since become set. 
                    lock (newEventObj)
                    {
                        // If our event hasn't already been disposed of, we must set it.
                        if (m_eventObj == newEventObj) 
                        {
                            newEventObj.Set(); 
                        } 
                    }
                } 

                return true;
            }
        } 

        ///  
        /// Sets the state of the event to signaled, which allows one or more threads waiting on the event to 
        /// proceed.
        ///  
        public void Set()
        {
            Set(false);
        } 

        ///  
        /// Private helper to actually perform the Set. 
        /// 
        /// Indicates whether we are calling Set() during cancellation. 
        /// The object has been canceled.
        private void Set(bool duringCancellation)
        {
            // We need to ensure that IsSet=true does not get reordered past the read of m_eventObj 
            // This would be a legal movement according to the .NET memory model.
            // The code is safe as IsSet involves an Interlocked.CompareExchange which provides a full memory barrier. 
            IsSet = true; 

            // If there are waiting threads, we need to pulse them. 
            if (Waiters > 0)
            {

                //m_lock is not volatile, but the backing store of Waiters is volatile. 
                //Hence READ(Waiters) was a load-acquire. READ(m_lock) will not reorder above.
                //and there is no risk of loading a null m_lock and then observing Waiters > 0. 
                //Also, the writes to m_lock and Waiters in Wait(int millisecondsTimeout, CancellationToken cancellationToken) 
                //cannot reorder as all writes are Store-Release in MM
 
                Contract.Assert(m_lock != null); //if waiters>0, then m_lock has already been created.
                lock (m_lock)
                {
 
                    Monitor.PulseAll(m_lock);
                } 
            } 

            ManualResetEvent eventObj = m_eventObj; 

            //Design-decision: do not set the event if we are in cancellation -> better to deadlock than to wake up waiters incorrectly
            //It would be preferable to wake up the event and have it throw OCE. This requires MRE to implement cancellation logic
 
            if (eventObj != null && !duringCancellation)
            { 
                // We must surround this call to Set in a lock.  The reason is fairly subtle. 
                // Sometimes a thread will issue a Wait and wake up after we have set m_state,
                // but before we have gotten around to setting m_eventObj (just below). That's 
                // because Wait first checks m_state and will only access the event if absolutely
                // necessary.  However, the coding pattern { event.Wait(); event.Dispose() } is
                // quite common, and we must support it.  If the waiter woke up and disposed of
                // the event object before the setter has finished, however, we would try to set a 
                // now-disposed Win32 event.  Crash!  To deal with this ----, we use a lock to
                // protect access to the event object when setting and disposing of it.  We also 
                // double-check that the event has not become null in the meantime when in the lock. 

                lock (eventObj) 
                {
                    if (m_eventObj != null)
                    {
                        // If somebody is waiting, we must set the event. 
                        m_eventObj.Set();
                    } 
                } 
            }
 
#if DEBUG
            m_lastSetTime = DateTime.Now.Ticks;
#endif
        } 

        ///  
        /// Sets the state of the event to nonsignaled, which causes threads to block. 
        /// 
        ///  
        /// Unlike most of the members of ,  is not
        /// thread-safe and may not be used concurrently with other members of this instance.
        /// 
        public void Reset() 
        {
            ThrowIfDisposed(); 
            // If there's an event, reset it. 
            if (m_eventObj != null)
            { 
                m_eventObj.Reset();
            }

            // There is a ---- here. If another thread Sets the event, we will get into a state 
            // where m_state will be unsignaled, yet the Win32 event object will have been signaled.
            // This could cause waiting threads to wake up even though the event is in an 
            // unsignaled state. This is fine -- those that are calling Reset concurrently are 
            // responsible for doing "the right thing" -- e.g. rechecking the condition and
            // resetting the event manually. 

            // And finally set our state back to unsignaled.
            IsSet = false;
 
#if DEBUG
            m_lastResetTime = DateTime.Now.Ticks; 
#endif 
        }
 
        /// 
        /// Blocks the current thread until the current  is set.
        /// 
        ///  
        /// The maximum number of waiters has been exceeded.
        ///  
        ///  
        /// 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. 
        /// 
        public void Wait()
        {
            Wait(Timeout.Infinite, new CancellationToken()); 
        }
 
        ///  
        /// Blocks the current thread until the current  receives a signal,
        /// while observing a . 
        /// 
        /// The  to
        /// observe.
        ///  
        /// The maximum number of waiters has been exceeded.
        ///  
        ///  was 
        /// canceled.
        ///  
        /// 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.
        /// 
        public void Wait(CancellationToken cancellationToken) 
        {
            Wait(Timeout.Infinite, cancellationToken); 
        } 

        ///  
        /// Blocks the current thread until the current  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 maximum number of waiters has been exceeded. 
        /// 
        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 current  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 . 
        ///  was canceled. 
        ///  
        /// The maximum number of waiters has been exceeded.
        ///  
        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 current  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 maximum number of waiters has been exceeded.
        ///  
        public bool Wait(int millisecondsTimeout) 
        {
            return Wait(millisecondsTimeout, new CancellationToken()); 
        }

        /// 
        /// Blocks the current thread until the current  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 maximum number of waiters has been exceeded.
        ///  
        ///  was canceled.
        public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
        { 
            ThrowIfDisposed();
            cancellationToken.ThrowIfCancellationRequested(); // an early convenience check 
 
            if (millisecondsTimeout < -1)
            { 
                throw new ArgumentOutOfRangeException("millisecondsTimeout");
            }

            if (!IsSet) 
            {
                if (millisecondsTimeout == 0) 
                { 
                    // For 0-timeouts, we just return immediately.
                    return false; 
                }


                // We spin briefly before falling back to allocating and/or waiting on a true event. 
                long startTimeTicks = 0;
                bool bNeedTimeoutAdjustment = false; 
                int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary. 

                if (millisecondsTimeout != Timeout.Infinite) 
                {
                    // We will account for time spent spinning, so that we can decrement it from our
                    // timeout.  In most cases the time spent in this section will be negligible.  But
                    // we can't discount the possibility of our thread being switched out for a lengthy 
                    // period of time.  The timeout adjustments only take effect when and if we actually
                    // decide to block in the kernel below. 
 
                    startTimeTicks = DateTime.UtcNow.Ticks;
                    bNeedTimeoutAdjustment = true; 
                }

                //spin
                int HOW_MANY_SPIN_BEFORE_YIELD = 10; 
                int HOW_MANY_YIELD_EVERY_SLEEP_0 = 5;
                int HOW_MANY_YIELD_EVERY_SLEEP_1 = 20; 
 
                for (int i = 0; i < SpinCount; i++)
                { 
                    if (IsSet)
                    {
                        return true;
                    } 

                    else if (i < HOW_MANY_SPIN_BEFORE_YIELD) 
                    { 
                        if (i == HOW_MANY_SPIN_BEFORE_YIELD / 2)
                        { 
#if PFX_LEGACY_3_5
                            Platform.Yield();
#else
                            Thread.Yield(); 
#endif
                        } 
                        else 
                        {
                            Thread.SpinWait(Environment.ProcessorCount * (4 << i)); 
                        }
                    }
                    else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0)
                    { 
                        Thread.Sleep(1);
                    } 
                    else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0) 
                    {
                        Thread.Sleep(0); 
                    }
                    else
                    {
#if PFX_LEGACY_3_5 
                        Platform.Yield();
#else 
                        Thread.Yield(); 
#endif
                    } 

                    if (i >= 100 && i % 10 == 0) // check the cancellation token if the user passed a very large spin count
                        cancellationToken.ThrowIfCancellationRequested();
                } 

                // Now enter the lock and wait. 
                EnsureLockObjectCreated(); 

                // We must register and deregister the token outside of the lock, to avoid deadlocks. 
                using (cancellationToken.Register(s_cancellationTokenCallback, this))
                {
                    lock (m_lock)
                    { 
                        // Loop to cope with spurious wakeups from other waits being canceled
                        while (!IsSet) 
                        { 
                            // If our token was canceled, we must throw and exit.
                            cancellationToken.ThrowIfCancellationRequested(); 

                            //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled)
                            if (bNeedTimeoutAdjustment)
                            { 
                                realMillisecondsTimeout = UpdateTimeOut(startTimeTicks, millisecondsTimeout);
                                if (realMillisecondsTimeout <= 0) 
                                    return false; 
                            }
 
                            // There is a ---- that Set will fail to see that there are waiters as Set does not take the lock,
                            // so after updating waiters, we must check IsSet again.
                            // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the
                            // read from IsSet.  This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange 
                            // operation which provides a full memory barrier.
                            // If we see IsSet=false, then we are guaranteed that Set() will see that we are 
                            // waiting and will pulse the monitor correctly. 

                            Waiters = Waiters + 1; 

                            if (IsSet) //This check must occur after updating Waiters.
                            {
                                Waiters--; //revert the increment. 
                                return true;
                            } 
 
                            // Now finally perform the wait.
                            try 
                            {
                                // ** the actual wait **
                                if (!Monitor.Wait(m_lock, realMillisecondsTimeout))
                                    return false; //return immediately if the timeout has expired. 
                            }
                            finally 
                            { 
                                // Clean up: we're done waiting.
                                Waiters = Waiters - 1; 
                            }

                            // Now just loop back around, and the right thing will happen.  Either:
                            //     1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) 
                            // or  2. the wait was successful. (the loop will break)
 
                        } 
                    }
                } 
            } // automatically disposes (and deregisters) the callback

            return true; //done. The wait was satisfied.
        } 

        ///  
        /// 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 ((m_combinedState & Dispose_BitMask) != 0) 
                return; // already disposed
 
            m_combinedState |= Dispose_BitMask; //set the dispose bit 
            if (disposing)
            { 
                // We will dispose of the event object.  We do this under a lock to protect
                // against the race condition outlined in the Set method above.
                ManualResetEvent eventObj = m_eventObj;
                if (eventObj != null) 
                {
                    lock (eventObj) 
                    { 
                        eventObj.Close();
                        m_eventObj = null; 
                    }
                }
            }
        } 

        ///  
        /// Throw ObjectDisposedException if the MRES is disposed 
        /// 
        private void ThrowIfDisposed() 
        {
            if ((m_combinedState & Dispose_BitMask) != 0)
                throw new ObjectDisposedException(Environment.GetResourceString("ManualResetEventSlim_Disposed"));
        } 

        ///  
        /// Private helper method to wake up waiters when a cancellationToken gets canceled. 
        /// 
        private static Action s_cancellationTokenCallback = new Action(CancellationTokenCallback); 
        private static void CancellationTokenCallback(object obj)
        {
            ManualResetEventSlim mre = obj as ManualResetEventSlim;
            Contract.Assert(mre != null, "Expected a ManualResetEventSlim"); 
            Contract.Assert(mre.m_lock != null); //the lock should have been created before this callback is registered for use.
            lock (mre.m_lock) 
            { 
                Monitor.PulseAll(mre.m_lock); // awaken all waiters
            } 
        }

        /// 
        /// Private helper method for updating parts of a bit-string state value. 
        /// Mainly called from the IsSet and Waiters properties setters
        ///  
        ///  
        /// Note: the parameter types must be int as CompareExchange cannot take a Uint
        ///  
        /// The new value
        /// The mask used to set the bits
        private void UpdateStateAtomically(int newBits, int updateBitsMask)
        { 
            SpinWait sw = new SpinWait();
 
            Contract.Assert((newBits | updateBitsMask) == updateBitsMask, "newBits do not fall within the updateBitsMask."); 

            do 
            {
                int oldState = m_combinedState; // cache the old value for testing in CAS

                // Procedure:(1) zero the updateBits.  eg oldState = [11111111] flag= [00111000] newState = [11000111] 
                //           then (2) map in the newBits. eg [11000111] newBits=00101000, newState=[11101111]
                int newState = (oldState & ~updateBitsMask) | newBits; 
 
                if (Interlocked.CompareExchange(ref m_combinedState, newState, oldState) == oldState)
                { 
                    return;
                }

                sw.SpinOnce(); 
            } while (true);
        } 
 
        /// 
        /// Private helper method - performs Mask and shift, particular helpful to extract a field from a packed word. 
        /// eg ExtractStatePortionAndShiftRight(0x12345678, 0xFF000000, 24) => 0x12, ie extracting the top 8-bits as a simple integer
        ///
        /// ?? is there a common place to put this rather than being private to MRES?
        ///  
        /// 
        ///  
        ///  
        /// 
        private static int ExtractStatePortionAndShiftRight(int state, int mask, int rightBitShiftCount) 
        {
            //convert to uint before shifting so that right-shift does not replicate the sign-bit,
            //then convert back to int.
            return unchecked((int)(((uint)(state & mask)) >> rightBitShiftCount)); 
        }
 
        ///  
        /// Performs a Mask operation, but does not perform the shift.
        /// This is acceptable for boolean values for which the shift is unnecessary 
        /// eg (val & Mask) != 0 is an appropriate way to extract a boolean rather than using
        /// ((val & Mask) >> shiftAmount) == 1
        ///
        /// ?? is there a common place to put this rather than being private to MRES? 
        /// 
        ///  
        ///  
        private static int ExtractStatePortion(int state, int mask)
        { 
            return state & mask;
        }

        ///  
        /// 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, -2 if overflow in counters 
        /// has occurred.
        private static int UpdateTimeOut(long startTimeTicks, int originalWaitMillisecondsTimeout)
        {
            // The function may only be called if 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 -2;
            } 

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

            return currentWaitTimeout; 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

                        

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