1 #pragma warning disable 0420
4 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
10 // A spin lock is a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop ("spins")
11 // repeatedly checking until the lock becomes available. As the thread remains active performing a non-useful task,
12 // the use of such a lock is a kind of busy waiting and consumes CPU resources without performing real work.
14 // <OWNER>[....]</OWNER>
16 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
18 using System.Diagnostics;
19 using System.Security.Permissions;
20 using System.Runtime.InteropServices;
21 using System.Runtime.CompilerServices;
22 using System.Runtime.ConstrainedExecution;
23 using System.Diagnostics.Contracts;
25 namespace System.Threading
29 /// Provides a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop
30 /// repeatedly checking until the lock becomes available.
34 /// Spin locks can be used for leaf-level locks where the object allocation implied by using a <see
35 /// cref="System.Threading.Monitor"/>, in size or due to garbage collection pressure, is overly
36 /// expensive. Avoiding blocking is another reason that a spin lock can be useful, however if you expect
37 /// any significant amount of blocking, you are probably best not using spin locks due to excessive
38 /// spinning. Spinning can be beneficial when locks are fine grained and large in number (for example, a
39 /// lock per node in a linked list) as well as when lock hold times are always extremely short. In
40 /// general, while holding a spin lock, one should avoid blocking, calling anything that itself may
41 /// block, holding more than one spin lock at once, making dynamically dispatched calls (interface and
42 /// virtuals), making statically dispatched calls into any code one doesn't own, or allocating memory.
45 /// <see cref="SpinLock"/> should only be used when it's been determined that doing so will improve an
46 /// application's performance. It's also important to note that <see cref="SpinLock"/> is a value type,
47 /// for performance reasons. As such, one must be very careful not to accidentally copy a SpinLock
48 /// instance, as the two instances (the original and the copy) would then be completely independent of
49 /// one another, which would likely lead to erroneous behavior of the application. If a SpinLock instance
50 /// must be passed around, it should be passed by reference rather than by value.
53 /// Do not store <see cref="SpinLock"/> instances in readonly fields.
56 /// All members of <see cref="SpinLock"/> are thread-safe and may be used from multiple threads
61 [HostProtection(Synchronization = true, ExternalThreading = true)]
62 [DebuggerTypeProxy(typeof(SystemThreading_SpinLockDebugView))]
63 [DebuggerDisplay("IsHeld = {IsHeld}")]
64 public struct SpinLock
66 // The current ownership state is a single signed int. There are two modes:
68 // 1) Ownership tracking enabled: the high bit is 0, and the remaining bits
69 // store the managed thread ID of the current owner. When the 31 low bits
70 // are 0, the lock is available.
71 // 2) Performance mode: when the high bit is 1, lock availability is indicated by the low bit.
72 // When the low bit is 1 -- the lock is held; 0 -- the lock is available.
74 // There are several masks and constants below for convenience.
76 private volatile int m_owner;
78 // The multiplier factor for the each spinning iteration
79 // This number has been chosen after trying different numbers on different CPUs (4, 8 and 16 ) and this provided the best results
80 private const int SPINNING_FACTOR = 100;
82 // After how many yields, call Sleep(1)
83 private const int SLEEP_ONE_FREQUENCY = 40;
85 // After how many yields, call Sleep(0)
86 private const int SLEEP_ZERO_FREQUENCY = 10;
88 // After how many yields, check the timeout
89 private const int TIMEOUT_CHECK_FREQUENCY = 10;
91 // Thr thread tracking disabled mask
92 private const int LOCK_ID_DISABLE_MASK = unchecked((int)0x80000000); //1000 0000 0000 0000 0000 0000 0000 0000
94 //the lock is held by some thread, but we don't know which
95 private const int LOCK_ANONYMOUS_OWNED = 0x1; //0000 0000 0000 0000 0000 0000 0000 0001
97 // Waiters mask if the thread tracking is disabled
98 private const int WAITERS_MASK = ~(LOCK_ID_DISABLE_MASK | 1); //0111 1111 1111 1111 1111 1111 1111 1110
100 // The Thread tacking is disabled and the lock bit is set, used in Enter fast path to make sure the id is disabled and lock is available
101 private const int ID_DISABLED_AND_ANONYMOUS_OWNED = unchecked((int)0x80000001); //1000 0000 0000 0000 0000 0000 0000 0001
103 // If the thread is unowned if:
104 // m_owner zero and the threa tracking is enabled
105 // m_owner & LOCK_ANONYMOUS_OWNED = zero and the thread tracking is disabled
106 private const int LOCK_UNOWNED = 0;
108 // The maximum number of waiters (only used if the thread tracking is disabled)
109 // The actual maximum waiters count is this number divided by two because each waiter increments the waiters count by 2
110 // The waiters count is calculated by m_owner & WAITERS_MASK 01111....110
111 private static int MAXIMUM_WAITERS = WAITERS_MASK;
115 /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/>
116 /// structure with the option to track thread IDs to improve debugging.
119 /// The default constructor for <see cref="SpinLock"/> tracks thread ownership.
121 /// <param name="enableThreadOwnerTracking">Whether to capture and use thread IDs for debugging
122 /// purposes.</param>
123 public SpinLock(bool enableThreadOwnerTracking)
125 m_owner = LOCK_UNOWNED;
126 if (!enableThreadOwnerTracking)
128 m_owner |= LOCK_ID_DISABLE_MASK;
129 Contract.Assert(!IsThreadOwnerTrackingEnabled, "property should be false by now");
135 /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/>
136 /// structure with the option to track thread IDs to improve debugging.
139 /// The default constructor for <see cref="SpinLock"/> tracks thread ownership.
142 /// Acquires the lock in a reliable manner, such that even if an exception occurs within the method
143 /// call, <paramref name="lockTaken"/> can be examined reliably to determine whether the lock was
147 /// <see cref="SpinLock"/> is a non-reentrant lock, meaning that if a thread holds the lock, it is
148 /// not allowed to enter the lock again. If thread ownership tracking is enabled (whether it's
149 /// enabled is available through <see cref="IsThreadOwnerTrackingEnabled"/>), an exception will be
150 /// thrown when a thread tries to re-enter a lock it already holds. However, if thread ownership
151 /// tracking is disabled, attempting to enter a lock already held will result in deadlock.
153 /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
154 /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
155 /// <exception cref="T:System.Threading.LockRecursionException">
156 /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
158 /// <exception cref="T:System.ArgumentException">
159 /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling Enter.
161 public void Enter(ref bool lockTaken)
164 Thread.BeginCriticalRegion();
166 //Try to keep the code and branching in this method as small as possible in order to inline the method
167 int observedOwner = m_owner;
168 if (lockTaken || //invalid parameter
169 (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired
170 Interlocked.CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) //acquiring the lock failed
171 ContinueTryEnter(Timeout.Infinite, ref lockTaken); // Then try the slow path if any of the above conditions is met
176 /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
177 /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the
178 /// lock was acquired.
181 /// Unlike <see cref="Enter"/>, TryEnter will not block waiting for the lock to be available. If the
182 /// lock is not available when TryEnter is called, it will return immediately without any further
185 /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
186 /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
187 /// <exception cref="T:System.Threading.LockRecursionException">
188 /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
190 /// <exception cref="T:System.ArgumentException">
191 /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter.
193 public void TryEnter(ref bool lockTaken)
195 TryEnter(0, ref lockTaken);
199 /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
200 /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the
201 /// lock was acquired.
204 /// Unlike <see cref="Enter"/>, TryEnter will not block indefinitely waiting for the lock to be
205 /// available. It will block until either the lock is available or until the <paramref
209 /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds
210 /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
212 /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
213 /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
214 /// <exception cref="T:System.Threading.LockRecursionException">
215 /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
217 /// <exception cref="T:System.ArgumentException">
218 /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter.
220 /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative
221 /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater
222 /// than <see cref="System.Int32.MaxValue"/> milliseconds.
224 public void TryEnter(TimeSpan timeout, ref bool lockTaken)
226 // Validate the timeout
227 Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
228 if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
230 throw new System.ArgumentOutOfRangeException(
231 "timeout", timeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange"));
234 // Call reliable enter with the int-based timeout milliseconds
235 TryEnter((int)timeout.TotalMilliseconds, ref lockTaken);
239 /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within
240 /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the
241 /// lock was acquired.
244 /// Unlike <see cref="Enter"/>, TryEnter will not block indefinitely waiting for the lock to be
245 /// available. It will block until either the lock is available or until the <paramref
246 /// name="millisecondsTimeout"/> has expired.
248 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see
249 /// cref="System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param>
250 /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref
251 /// name="lockTaken"/> must be initialized to false prior to calling this method.</param>
252 /// <exception cref="T:System.Threading.LockRecursionException">
253 /// Thread ownership tracking is enabled, and the current thread has already acquired this lock.
255 /// <exception cref="T:System.ArgumentException">
256 /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter.
258 /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is
259 /// a negative number other than -1, which represents an infinite time-out.</exception>
260 public void TryEnter(int millisecondsTimeout, ref bool lockTaken)
263 Thread.BeginCriticalRegion();
266 int observedOwner = m_owner;
267 if (millisecondsTimeout < -1 || //invalid parameter
268 lockTaken || //invalid parameter
269 (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired
270 Interlocked.CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) // acquiring the lock failed
271 ContinueTryEnter(millisecondsTimeout, ref lockTaken); // The call the slow pth
275 /// Try acquire the lock with long path, this is usually called after the first path in Enter and
276 /// TryEnter failed The reason for short path is to make it inline in the run time which improves the
277 /// performance. This method assumed that the parameter are validated in Enter ir TryENter method
279 /// <param name="millisecondsTimeout">The timeout milliseconds</param>
280 /// <param name="lockTaken">The lockTaken param</param>
281 private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken)
283 //Leave the critical region which is entered by the fast path
285 Thread.EndCriticalRegion();
287 // The fast path doesn't throw any exception, so we have to validate the parameters here
291 throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException"));
294 if (millisecondsTimeout < -1)
296 throw new ArgumentOutOfRangeException(
297 "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange"));
302 if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0)
304 startTime = TimeoutHelper.GetTime();
307 #if !FEATURE_PAL && !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile CDS providers for Coreclr
308 if (CdsSyncEtwBCLProvider.Log.IsEnabled())
310 CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner);
314 if (IsThreadOwnerTrackingEnabled)
316 // Slow path for enabled thread tracking mode
317 ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken);
321 // then thread tracking is disabled
322 // In this case there are three ways to acquire the lock
323 // 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
324 // 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
325 // the late the thread arrives the more it spins and less frequent it check the lock avilability
326 // Also the spins count is increases each iteration
327 // If the spins iterations finished and failed to acquire the lock, go to step 3
328 // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1)
329 // If the timeout is expired in after step 1, we need to decrement the waiters count before returning
332 int turn = int.MaxValue;
333 //***Step 1, take the lock or update the waiters
335 // try to acquire the lock directly if possible or update the waiters count
336 observedOwner = m_owner;
337 if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
340 Thread.BeginCriticalRegion();
344 if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner) == observedOwner)
350 if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner)
357 Thread.EndCriticalRegion();
360 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
362 if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS)
363 turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1 ;
368 // Check the timeout.
369 if (millisecondsTimeout == 0 ||
370 (millisecondsTimeout != Timeout.Infinite &&
371 TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0))
377 //***Step 2. Spinning
378 //lock acquired failed and waiters updated
379 int processorCount = PlatformHelper.ProcessorCount;
380 if (turn < processorCount)
382 int processFactor = 1;
383 for (int i = 1; i <= turn * SPINNING_FACTOR; i++)
385 Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor);
386 if (processFactor < processorCount)
388 observedOwner = m_owner;
389 if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
392 Thread.BeginCriticalRegion();
395 int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
396 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
397 : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
398 Contract.Assert((newOwner & WAITERS_MASK) >= 0);
400 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
406 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
413 Thread.EndCriticalRegion();
419 // Check the timeout.
420 if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
426 //*** Step 3, Yielding
427 //Sleep(1) every 50 yields
431 observedOwner = m_owner;
432 if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
435 Thread.BeginCriticalRegion();
437 int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
438 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
439 : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
440 Contract.Assert((newOwner & WAITERS_MASK) >= 0);
442 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner)
448 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
455 Thread.EndCriticalRegion();
459 if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0)
463 else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0)
476 if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0)
479 if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
491 /// decrements the waiters, in case of the timeout is expired
493 private void DecrementWaiters()
495 SpinWait spinner = new SpinWait();
498 int observedOwner = m_owner;
499 if ((observedOwner & WAITERS_MASK) == 0) return; // don't decrement the waiters if it's corrupted by previous call of Exit(false)
500 if (Interlocked.CompareExchange(ref m_owner, observedOwner - 2, observedOwner) == observedOwner)
502 Contract.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped
511 /// ContinueTryEnter for the thread tracking mode enabled
513 private void ContinueTryEnterWithThreadTracking(int millisecondsTimeout, uint startTime, ref bool lockTaken)
515 Contract.Assert(IsThreadOwnerTrackingEnabled);
518 // We are using thread IDs to mark ownership. Snap the thread ID and check for recursion.
519 // We also must or the ID enablement bit, to ensure we propagate when we CAS it in.
520 int m_newOwner = Thread.CurrentThread.ManagedThreadId;
521 if (m_owner == m_newOwner)
523 // We don't allow lock recursion.
524 throw new LockRecursionException(Environment.GetResourceString("SpinLock_TryEnter_LockRecursionException"));
528 SpinWait spinner = new SpinWait();
530 // Loop until the lock has been successfully acquired or, if specified, the timeout expires.
534 // We failed to get the lock, either from the fast route or the last iteration
535 // and the timeout hasn't expired; spin once and try again.
538 // Test before trying to CAS, to avoid acquiring the line exclusively unnecessarily.
540 if (m_owner == lockUnowned)
543 Thread.BeginCriticalRegion();
546 if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned) == lockUnowned)
552 if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned)
559 // The thread failed to get the lock, so we don't need to remain in a critical region.
560 Thread.EndCriticalRegion();
563 // Check the timeout. We only RDTSC if the next spin will yield, to amortize the cost.
564 if (millisecondsTimeout == 0 ||
565 (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield &&
566 TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0))
574 /// Releases the lock.
577 /// The default overload of <see cref="Exit()"/> provides the same behavior as if calling <see
578 /// cref="Exit(Boolean)"/> using true as the argument, but Exit() could be slightly faster than Exit(true).
580 /// <exception cref="SynchronizationLockException">
581 /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock.
583 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
586 //This is the fast path for the thread tracking is disabled, otherwise go to the slow path
587 if ((m_owner & LOCK_ID_DISABLE_MASK) == 0)
590 Interlocked.Decrement(ref m_owner);
593 Thread.EndCriticalRegion();
599 /// Releases the lock.
601 /// <param name="useMemoryBarrier">
602 /// A Boolean value that indicates whether a memory fence should be issued in order to immediately
603 /// publish the exit operation to other threads.
606 /// Calling <see cref="Exit(Boolean)"/> with the <paramref name="useMemoryBarrier"/> argument set to
607 /// true will improve the fairness of the lock at the expense of some performance. The default <see
609 /// overload behaves as if specifying true for <paramref name="useMemoryBarrier"/>.
611 /// <exception cref="SynchronizationLockException">
612 /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock.
614 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
615 public void Exit(bool useMemoryBarrier)
617 // This is the fast path for the thread tracking is diabled and not to use memory barrier, otherwise go to the slow path
618 // The reason not to add else statement if the usememorybarrier is that it will add more barnching in the code and will prevent
619 // method inlining, so this is optimized for useMemoryBarrier=false and Exit() overload optimized for useMemoryBarrier=true
620 if ((m_owner & LOCK_ID_DISABLE_MASK) != 0 && !useMemoryBarrier)
622 int tmpOwner = m_owner;
623 m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED);
626 ExitSlowPath(useMemoryBarrier);
629 Thread.EndCriticalRegion();
634 /// The slow path for exit method if the fast path failed
636 /// <param name="useMemoryBarrier">
637 /// A Boolean value that indicates whether a memory fence should be issued in order to immediately
638 /// publish the exit operation to other threads
640 private void ExitSlowPath(bool useMemoryBarrier)
642 bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0;
643 if (threadTrackingEnabled && !IsHeldByCurrentThread)
645 throw new System.Threading.SynchronizationLockException(
646 Environment.GetResourceString("SpinLock_Exit_SynchronizationLockException"));
649 if (useMemoryBarrier)
651 if (threadTrackingEnabled)
652 Interlocked.Exchange(ref m_owner, LOCK_UNOWNED);
654 Interlocked.Decrement(ref m_owner);
659 if (threadTrackingEnabled)
660 m_owner = LOCK_UNOWNED;
663 int tmpOwner = m_owner;
664 m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED);
672 /// Gets whether the lock is currently held by any thread.
676 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
679 if (IsThreadOwnerTrackingEnabled)
680 return m_owner != LOCK_UNOWNED;
682 return (m_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED;
687 /// Gets whether the lock is currently held by any thread.
690 /// Gets whether the lock is held by the current thread.
693 /// If the lock was initialized to track owner threads, this will return whether the lock is acquired
694 /// by the current thread. It is invalid to use this property when the lock was initialized to not
695 /// track thread ownership.
697 /// <exception cref="T:System.InvalidOperationException">
698 /// Thread ownership tracking is disabled.
700 public bool IsHeldByCurrentThread
702 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
705 if (!IsThreadOwnerTrackingEnabled)
707 throw new InvalidOperationException(Environment.GetResourceString("SpinLock_IsHeldByCurrentThread"));
709 return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Thread.CurrentThread.ManagedThreadId);
713 /// <summary>Gets whether thread ownership tracking is enabled for this instance.</summary>
714 public bool IsThreadOwnerTrackingEnabled
716 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
717 get { return (m_owner & LOCK_ID_DISABLE_MASK) == 0; }
720 #region Debugger proxy class
722 /// Internal class used by debug type proxy attribute to display the owner thread ID
724 internal class SystemThreading_SpinLockDebugView
727 private SpinLock m_spinLock;
730 /// SystemThreading_SpinLockDebugView constructor
732 /// <param name="spinLock">The SpinLock to be proxied.</param>
733 public SystemThreading_SpinLockDebugView(SpinLock spinLock)
735 // Note that this makes a copy of the SpinLock (struct). It doesn't hold a reference to it.
736 m_spinLock = spinLock;
740 /// Checks if the lock is held by the current thread or not
742 public bool? IsHeldByCurrentThread
748 return m_spinLock.IsHeldByCurrentThread;
750 catch (InvalidOperationException)
758 /// Gets the current owner thread, zero if it is released
760 public int? OwnerThreadID
764 if (m_spinLock.IsThreadOwnerTrackingEnabled)
766 return m_spinLock.m_owner;
777 /// Gets whether the lock is currently held by any thread or not.
781 get { return m_spinLock.IsHeld; }
788 #pragma warning restore 0420