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

                            #pragma warning disable 0420 
// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 
// 
// SpinLock.cs
// A spin lock is a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop ("spins") 
// repeatedly checking until the lock becomes available. As the thread remains active performing a non-useful task,
// the use of such a lock is a kind of busy waiting and consumes CPU resources without performing real work.
//
// [....] 
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
using System; 
using System.Diagnostics;
using System.Security.Permissions; 
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Diagnostics.Contracts; 

namespace System.Threading 
{ 

    ///  
    /// Provides a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop
    /// repeatedly checking until the lock becomes available.
    /// 
    ///  
    /// 
    /// Spin locks can be used for leaf-level locks where the object allocation implied by using a , in size or due to garbage collection pressure, is overly 
    /// expensive. Avoiding blocking is another reason that a spin lock can be useful, however if you expect
    /// any significant amount of blocking, you are probably best not using spin locks due to excessive 
    /// spinning. Spinning can be beneficial when locks are fine grained and large in number (for example, a
    /// lock per node in a linked list) as well as when lock hold times are always extremely short. In
    /// general, while holding a spin lock, one should avoid blocking, calling anything that itself may
    /// block, holding more than one spin lock at once, making dynamically dispatched calls (interface and 
    /// virtuals), making statically dispatched calls into any code one doesn't own, or allocating memory.
    ///  
    ///  
    ///  should only be used when it's been determined that doing so will improve an
    /// application's performance. It's also important to note that  is a value type, 
    /// for performance reasons. As such, one must be very careful not to accidentally copy a SpinLock
    /// instance, as the two instances (the original and the copy) would then be completely independent of
    /// one another, which would likely lead to erroneous behavior of the application. If a SpinLock instance
    /// must be passed around, it should be passed by reference rather than by value. 
    /// 
    ///  
    /// Do not store  instances in readonly fields. 
    /// 
    ///  
    /// All members of  are thread-safe and may be used from multiple threads
    /// concurrently.
    /// 
    ///  
    [ComVisible(false)]
    [HostProtection(Synchronization = true, ExternalThreading = true)] 
    [DebuggerTypeProxy(typeof(SystemThreading_SpinLockDebugView))] 
    [DebuggerDisplay("IsHeld = {IsHeld}")]
    public struct SpinLock 
    {
        // The current ownership state is a single signed int. There are two modes:
        //
        //    1) Ownership tracking enabled: the high bit is 0, and the remaining bits 
        //       store the managed thread ID of the current owner.  When the 31 low bits
        //       are 0, the lock is available. 
        //    2) Performance mode: when the high bit is 1, lock availability is indicated by the low bit. 
        //       When the low bit is 1 -- the lock is held; 0 -- the lock is available.
        // 
        // There are several masks and constants below for convenience.

        private volatile int m_owner;
 
        // The multiplier factor for the each spinning iteration
        // This number has been chosen after trying different numbers on different CPUs (4, 8 and 16 ) and this provided the best results 
        private const int SPINNING_FACTOR = 100; 

        // After how many yields, call Sleep(1) 
        private const int SLEEP_ONE_FREQUENCY = 40;

        // After how many yields, call Sleep(0)
        private const int SLEEP_ZERO_FREQUENCY = 10; 

        // After how many yields, check the timeout 
        private const int TIMEOUT_CHECK_FREQUENCY = 10; 

        // Thr thread tracking disabled mask 
        private const int LOCK_ID_DISABLE_MASK = unchecked((int)0x80000000);        //1000 0000 0000 0000 0000 0000 0000 0000

        //the lock is held by some thread, but we don't know which
        private const int LOCK_ANONYMOUS_OWNED = 0x1;                               //0000 0000 0000 0000 0000 0000 0000 0001 

        // Waiters mask if the thread tracking is disabled 
        private const int WAITERS_MASK = ~(LOCK_ID_DISABLE_MASK | 1);               //0111 1111 1111 1111 1111 1111 1111 1110 

        // If the thread is unowned if: 
        // m_owner zero and the threa tracking is enabled
        // m_owner & LOCK_ANONYMOUS_OWNED = zero and the thread tracking is disabled
        private const int LOCK_UNOWNED = 0;
 
        // The maximum number of waiters (only used if the thread tracking is disabled)
        // The actual maximum waiters count is this number divided by two because each waiter increments the waiters count by 2 
        // The waiters count is calculated by m_owner & WAITERS_MASK 01111....110 
        private static int MAXIMUM_WAITERS = WAITERS_MASK;
 

        /// 
        /// Initializes a new instance of the 
        /// structure with the option to track thread IDs to improve debugging. 
        /// 
        ///  
        /// The default constructor for  tracks thread ownership. 
        /// 
        /// Whether to capture and use thread IDs for debugging 
        /// purposes.
        public SpinLock(bool enableThreadOwnerTracking)
        {
            m_owner = LOCK_UNOWNED; 
            if (!enableThreadOwnerTracking)
            { 
                m_owner |= LOCK_ID_DISABLE_MASK; 
                Contract.Assert(!IsThreadOwnerTrackingEnabled, "property should be false by now");
            } 
        }


        ///  
        /// Initializes a new instance of the 
        /// structure with the option to track thread IDs to improve debugging. 
        ///  
        /// 
        /// The default constructor for  tracks thread ownership. 
        /// 
        /// 
        /// Acquires the lock in a reliable manner, such that even if an exception occurs within the method
        /// call,  can be examined reliably to determine whether the lock was 
        /// acquired.
        ///  
        ///  
        ///  is a non-reentrant lock, meaning that if a thread holds the lock, it is
        /// not allowed to enter the lock again. If thread ownership tracking is enabled (whether it's 
        /// enabled is available through ), an exception will be
        /// thrown when a thread tries to re-enter a lock it already holds. However, if thread ownership
        /// tracking is disabled, attempting to enter a lock already held will result in deadlock.
        ///  
        /// True if the lock is acquired; otherwise, false.  must be initialized to false prior to calling this method. 
        ///  
        /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
        ///  
        /// 
        /// The  argument must be initialized to false prior to calling Enter.
        /// 
        public void Enter(ref bool lockTaken) 
        {
            if (lockTaken) 
            { 
                lockTaken = false;
                throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException")); 
            }

            // Fast path to acquire the lock if the lock is released
            // If the thread tracking enabled set the new owner to the current thread id 
            // Id not, set the anonymous bit lock
            int observedOwner = m_owner; 
            int newOwner = 0; 
            bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0;
            if (threadTrackingEnabled) 
            {
                if (observedOwner == LOCK_UNOWNED)
                    newOwner = Thread.CurrentThread.ManagedThreadId;
            } 
            else if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
            { 
                newOwner = observedOwner | LOCK_ANONYMOUS_OWNED; // set the lock bit 
            }
            if (newOwner != 0) 
            {
#if !FEATURE_CORECLR
                Thread.BeginCriticalRegion();
#endif 

#if PFX_LEGACY_3_5 
                if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner) 
                {
                    lockTaken = true; 
                    return;
                }
#else
                if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) 
                {
                    // Fast path succeeded 
                    return; 
                }
#endif 
#if !FEATURE_CORECLR
                Thread.EndCriticalRegion();
#endif
            } 
            //Fast path failed, try slow path
            ContinueTryEnter(Timeout.Infinite, ref lockTaken); 
        } 

 
        /// 
        /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
        /// the method call,  can be examined reliably to determine whether the
        /// lock was acquired. 
        /// 
        ///  
        /// Unlike , TryEnter will not block waiting for the lock to be available. If the 
        /// lock is not available when TryEnter is called, it will return immediately without any further
        /// spinning. 
        /// 
        /// True if the lock is acquired; otherwise, false.  must be initialized to false prior to calling this method.
        ///  
        /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
        ///  
        ///  
        /// The  argument must be initialized to false prior to calling TryEnter.
        ///  
        public void TryEnter(ref bool lockTaken)
        {
            TryEnter(0, ref lockTaken);
        } 

        ///  
        /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within 
        /// the method call,  can be examined reliably to determine whether the
        /// lock was acquired. 
        /// 
        /// 
        /// Unlike , TryEnter will not block indefinitely waiting for the lock to be
        /// available. It will block until either the lock is available or until the 
        /// has expired. 
        ///  
        /// A  that represents the number of milliseconds
        /// to wait, or a  that represents -1 milliseconds to wait indefinitely. 
        /// 
        /// True if the lock is acquired; otherwise, false.  must be initialized to false prior to calling this method.
        ///  
        /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
        ///  
        ///  
        /// The  argument must be initialized to false prior to calling TryEnter.
        ///  
        ///  is a negative
        /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
        /// than  milliseconds.
        ///  
        public void TryEnter(TimeSpan timeout, ref bool lockTaken)
        { 
            // Validate the timeout 
            Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) 
            {
                throw new System.ArgumentOutOfRangeException(
                    "timeout", timeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange"));
            } 

            // Call reliable enter with the int-based timeout milliseconds 
            TryEnter((int)timeout.TotalMilliseconds, ref lockTaken); 
        }
 
        /// 
        /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
        /// the method call,  can be examined reliably to determine whether the
        /// lock was acquired. 
        /// 
        ///  
        /// Unlike , TryEnter will not block indefinitely waiting for the lock to be 
        /// available. It will block until either the lock is available or until the  has expired. 
        /// 
        /// The number of milliseconds to wait, or  (-1) to wait indefinitely.
        /// True if the lock is acquired; otherwise, false.  must be initialized to false prior to calling this method.
        ///  
        /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. 
        /// 
        ///  
        /// The  argument must be initialized to false prior to calling TryEnter.
        /// 
        ///  is
        /// a negative number other than -1, which represents an infinite time-out. 
        public void TryEnter(int millisecondsTimeout, ref bool lockTaken)
        { 
            // validate input 
            if (lockTaken)
            { 
                lockTaken = false;
                throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException"));
            }
            if (millisecondsTimeout < -1) 
            {
                throw new ArgumentOutOfRangeException( 
                    "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); 
            }
 
            // Fast path to acquire the lock if the lock is released
            // If the thread tracking enabled set the new owner to the current thread id
            // Id not, set the anonymous bit lock
            int observedOwner = m_owner; 
            int newOwner = 0;
            if (IsThreadOwnerTrackingEnabled) 
            { 
                if (observedOwner == LOCK_UNOWNED)
                    newOwner = Thread.CurrentThread.ManagedThreadId; 
            }
            else if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
            {
                newOwner = observedOwner | LOCK_ANONYMOUS_OWNED; 
            }
            if (newOwner != 0) 
            { 
#if !FEATURE_CORECLR
                Thread.BeginCriticalRegion(); 
#endif


#if PFX_LEGACY_3_5 
            if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
            { 
                lockTaken = true; 
                return;
            } 
#else
            if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
            {
                return; 
            }
#endif 
 
#if !FEATURE_CORECLR
                Thread.EndCriticalRegion(); 
#endif
            }
            // Fast path failed, try slow path
            ContinueTryEnter(millisecondsTimeout, ref lockTaken); 
        }
 
        ///  
        /// Try acquire the lock with long path, this is usually called after the first path in Enter and
        /// TryEnter failed The reason for short path is to make it inline in the run time which improves the 
        /// performance. This method assumed that the parameter are validated in Enter ir TryENter method
        /// 
        /// The timeout milliseconds
        /// The lockTaken param 
        private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken)
        { 
            long startTicks = 0; 
            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0)
            { 
                startTicks = DateTime.UtcNow.Ticks;
            }

#if !FEATURE_PAL && !FEATURE_CORECLR   // PAL doesn't support  eventing, and we don't compile CDS providers for Coreclr 
            if (CdsSyncEtwBCLProvider.Log.IsEnabled())
            { 
                CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner); 
            }
#endif 

            if (IsThreadOwnerTrackingEnabled)
            {
                // Slow path for enabled thread tracking mode 
                ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTicks, ref lockTaken);
                return; 
            } 

            // then thread tracking is disabled 
            // In this case there are three ways to acquire the lock
            // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2
            // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn
            // the late the thread arrives the more it spins and less frequent it check the lock avilability 
            // Also the spins count is increaes each iteration
            // If the spins iterations finished and failed to acquire the lock, go to step 3 
            // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1) 
            // If the timeout is expired in after step 1, we need to decrement the waiters count before returning
 
            int observedOwner;

            //***Step 1, take the lock or update the waiters
 
            // try to acquire the lock directly if possoble or update the waiters count
            SpinWait spinner = new SpinWait(); 
            while (true) 
            {
                observedOwner = m_owner; 
                if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
                {
#if !FEATURE_CORECLR
                    Thread.BeginCriticalRegion(); 
#endif
 
#if PFX_LEGACY_3_5 
                    if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner) == observedOwner)
                    { 
                        lockTaken = true;
                        return;
                    }
#else 
                    if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner)
                    { 
                        return; 
                    }
#endif 

#if !FEATURE_CORECLR
                    Thread.EndCriticalRegion();
#endif 
                }
                else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow 
                    if ((observedOwner & WAITERS_MASK) ==  MAXIMUM_WAITERS || Interlocked.CompareExchange(ref m_owner, observedOwner + 2, observedOwner) == observedOwner) 
                        break;
 
                spinner.SpinOnce();
            }

            // Check the timeout. 
            if (millisecondsTimeout == 0 ||
                (millisecondsTimeout != Timeout.Infinite && 
                TimeoutExpired(startTicks, millisecondsTimeout))) 
            {
                DecrementWaiters(); 
                return;
            }

            //***Step 2. Spinning 
            //lock acquired failed and waiters updated
            int turn = ((observedOwner + 2) & WAITERS_MASK) / 2; 
            int processorCount = PlatformHelper.ProcessorCount; 
            if (turn < processorCount)
            { 
                int processFactor = 1;
                for (int i = 1; i <= turn * SPINNING_FACTOR; i++)
                {
                    Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); 
                    if (processFactor < processorCount)
                        processFactor++; 
                    observedOwner = m_owner; 
                    if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
                    { 
#if !FEATURE_CORECLR
                        Thread.BeginCriticalRegion();
#endif
 
                        int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
                            observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters 
                            : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit 
                        Contract.Assert((newOwner & WAITERS_MASK) >= 0);
#if PFX_LEGACY_3_5 
                        if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
                        {
                            lockTaken = true;
                            return; 
                        }
#else 
                        if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) 
                        {
                            return; 
                        }
#endif

#if !FEATURE_CORECLR 
                        Thread.EndCriticalRegion();
#endif 
                    } 
                }
            } 

            // Check the timeout.
            if (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout))
            { 
                DecrementWaiters();
                return; 
            } 

            //*** Step 3, Yielding 
            //Sleep(1) every 50 yields
            int yieldsoFar = 0;
            while (true)
            { 
                observedOwner = m_owner;
                if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) 
                { 
#if !FEATURE_CORECLR
                    Thread.BeginCriticalRegion(); 
#endif
                    int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
                           observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters
                           : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit 
                    Contract.Assert((newOwner & WAITERS_MASK) >= 0);
#if PFX_LEGACY_3_5 
                    if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner) 
                    {
                        lockTaken = true; 
                        return;
                    }
#else
                    if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) 
                    {
                        return; 
                    } 
#endif
 
#if !FEATURE_CORECLR
                    Thread.EndCriticalRegion();
#endif
                } 

                if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) 
                { 
                    Thread.Sleep(1);
                } 
                else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0)
                {
                    Thread.Sleep(0);
                } 
                else
                { 
#if PFX_LEGACY_3_5 
                    Platform.Yield();
#else 
                    Thread.Yield();
#endif
                }
 
                if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0)
                { 
                    //Check the timeout. 
                    if (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout))
                    { 
                        DecrementWaiters();
                        return;
                    }
                } 

                yieldsoFar++; 
            } 
        }
 
        /// 
        /// decrements the waiters, in case of the timeout is expired
        /// 
        private void DecrementWaiters() 
        {
            SpinWait spinner = new SpinWait(); 
            while (true) 
            {
                int observedOwner = m_owner; 
                if ((observedOwner & WAITERS_MASK) == 0) return; // don't decrement the waiters if it's corrupted by previous call of Exit(false)
                if (Interlocked.CompareExchange(ref m_owner, observedOwner - 2, observedOwner) == observedOwner)
                {
                    Contract.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped 
                    break;
                } 
                spinner.SpinOnce(); 
            }
 
        }

        /// 
        /// ContinueTryEnter for the thread tracking mode enabled 
        /// 
        private void ContinueTryEnterWithThreadTracking(int millisecondsTimeout, long startTicks, ref bool lockTaken) 
        { 
            Contract.Assert(IsThreadOwnerTrackingEnabled);
 
            int lockUnowned = 0;
            // We are using thread IDs to mark ownership. Snap the thread ID and check for recursion.
            // We also must or the ID enablement bit, to ensure we propagate when we CAS it in.
            int m_newOwner = Thread.CurrentThread.ManagedThreadId; 
            if (m_owner == m_newOwner)
            { 
                // We don't allow lock recursion. 
                throw new LockRecursionException(Environment.GetResourceString("SpinLock_TryEnter_LockRecursionException"));
            } 


            SpinWait spinner = new SpinWait();
 
            // Loop until the lock has been successfully acquired or, if specified, the timeout expires.
            do 
            { 

                // We failed to get the lock, either from the fast route or the last iteration 
                // and the timeout hasn't expired; spin once and try again.
                spinner.SpinOnce();

                // Test before trying to CAS, to avoid acquiring the line exclusively unnecessarily. 

                if (m_owner == lockUnowned) 
                { 
#if !FEATURE_CORECLR
                    Thread.BeginCriticalRegion(); 
#endif
#if PFX_LEGACY_3_5
                    if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned) == lockUnowned)
                    { 
                        lockTaken = true;
                        return; 
                    } 
#else
                    if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned) 
                    {
                        return;
                    }
#endif 

#if !FEATURE_CORECLR 
                    // The thread failed to get the lock, so we don't need to remain in a critical region. 
                    Thread.EndCriticalRegion();
#endif 
                }
                // Check the timeout.  We only RDTSC if the next spin will yield, to amortize the cost.
                if (millisecondsTimeout == 0 ||
                    (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield && 
                    TimeoutExpired(startTicks, millisecondsTimeout)))
                { 
                    return; 
                }
            } while (true); 
        }

        /// 
        /// Helper function to validate the timeout 
        /// 
        ///  The start time in ticks 
        /// The orginal wait time 
        /// True if expired, false otherwise
        private static bool TimeoutExpired(long startTicks, int originalWaitTime) 
        {
            Contract.Assert(originalWaitTime != Timeout.Infinite);

            long elapsedTicks = DateTime.UtcNow.Ticks - startTicks; 
            return (elapsedTicks >= (originalWaitTime * TimeSpan.TicksPerMillisecond));
        } 
 
        /// 
        /// Releases the lock. 
        /// 
        /// 
        /// The default overload of  provides the same behavior as if calling  using true as the argument. 
        /// 
        ///  
        /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock. 
        /// 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        public void Exit()
        {
            Exit(true);
        } 

        ///  
        /// Releases the lock. 
        /// 
        ///  
        /// A Boolean value that indicates whether a memory fence should be issued in order to immediately
        /// publish the exit operation to other threads.
        /// 
        ///  
        /// Calling  with the  argument set to
        /// true will improve the fairness of the lock at the expense of some performance. The default  
        /// overload behaves as if specifying true for .
        ///  
        /// 
        /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock.
        /// 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        public void Exit(bool useMemoryBarrier)
        { 
            if (IsThreadOwnerTrackingEnabled && !IsHeldByCurrentThread) 
            {
                throw new System.Threading.SynchronizationLockException( 
                    Environment.GetResourceString("SpinLock_Exit_SynchronizationLockException"));
            }

            if (useMemoryBarrier) 
            {
                if (IsThreadOwnerTrackingEnabled) 
                    Interlocked.Exchange(ref m_owner, LOCK_UNOWNED); 
                else
                    Interlocked.Decrement(ref m_owner); 

            }
            else
            { 
                if (IsThreadOwnerTrackingEnabled)
                    m_owner = LOCK_UNOWNED; 
                else 
                {
                    int tmpOwner = m_owner; 
                    Contract.Assert((tmpOwner & LOCK_ANONYMOUS_OWNED) == LOCK_ANONYMOUS_OWNED, "The lock is not held.");
                    m_owner = tmpOwner - 1;
                }
 
            }
#if !FEATURE_CORECLR 
            Thread.EndCriticalRegion(); 
#endif
        } 

        /// 
        /// Gets whether the lock is currently held by any thread.
        ///  
        public bool IsHeld
        { 
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
            get
            { 
                if (IsThreadOwnerTrackingEnabled)
                    return m_owner != LOCK_UNOWNED;

                return (m_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED; 
            }
        } 
 
        /// 
        /// Gets whether the lock is currently held by any thread. 
        /// 
        /// 
        /// Gets whether the lock is held by the current thread.
        ///  
        /// 
        /// If the lock was initialized to track owner threads, this will return whether the lock is acquired 
        /// by the current thread. It is invalid to use this property when the lock was initialized to not 
        /// track thread ownership.
        ///  
        /// 
        /// Thread ownership tracking is disabled.
        /// 
        public bool IsHeldByCurrentThread 
        {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
            get 
            {
                if (!IsThreadOwnerTrackingEnabled) 
                {
                    throw new InvalidOperationException(Environment.GetResourceString("SpinLock_IsHeldByCurrentThread"));
                }
                return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Thread.CurrentThread.ManagedThreadId); 
            }
        } 
 
        /// Gets whether thread ownership tracking is enabled for this instance.
        public bool IsThreadOwnerTrackingEnabled 
        {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get { return (m_owner & LOCK_ID_DISABLE_MASK) == 0; }
        } 

        #region Debugger proxy class 
        ///  
        /// Internal class used by debug type proxy attribute to display the owner thread ID
        ///  
        internal class SystemThreading_SpinLockDebugView
        {
            // SpinLock object
            private SpinLock m_spinLock; 

            ///  
            /// SystemThreading_SpinLockDebugView constructor 
            /// 
            /// The SpinLock to be proxied. 
            public SystemThreading_SpinLockDebugView(SpinLock spinLock)
            {
                // Note that this makes a copy of the SpinLock (struct). It doesn't hold a reference to it.
                m_spinLock = spinLock; 
            }
 
            ///  
            /// Checks if the lock is held by the current thread or not
            ///  
            public bool? IsHeldByCurrentThread
            {
                get
                { 
                    try
                    { 
                        return m_spinLock.IsHeldByCurrentThread; 
                    }
                    catch (InvalidOperationException) 
                    {
                        return null;
                    }
                } 
            }
 
            ///  
            /// Gets the current owner thread, zero if it is released
            ///  
            public int? OwnerThreadID
            {
                get
                { 
                    if (m_spinLock.IsThreadOwnerTrackingEnabled)
                    { 
                        return m_spinLock.m_owner; 
                    }
                    else 
                    {
                        return null;
                    }
                } 
            }
 
 
            /// 
            ///  Gets whether the lock is currently held by any thread or not. 
            /// 
            public bool IsHeld
            {
                get { return m_spinLock.IsHeld; } 
            }
        } 
        #endregion 

    } 
}
#pragma warning restore 0420

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