diff options
author | Anipik <anirudhagnihotry098@gmail.com> | 2018-08-11 09:35:13 +0300 |
---|---|---|
committer | Anirudh Agnihotry <anirudhagnihotry098@gmail.com> | 2018-08-11 10:29:57 +0300 |
commit | 0fd0ca289196d2e5d5308bbb3e8e51a4a9cef31a (patch) | |
tree | 1e8a1b54e78d0347fd784874ed32911e1bbaab5f /src | |
parent | 64573a6e44383f8483be49cfc733f6084da98a4b (diff) |
Non shared changes
Diffstat (limited to 'src')
-rw-r--r-- | src/System.Private.CoreLib/src/System.Private.CoreLib.csproj | 1 | ||||
-rw-r--r-- | src/System.Private.CoreLib/src/System/Threading/SpinLock.cs | 704 |
2 files changed, 0 insertions, 705 deletions
diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index a6b3b2c5b..f898a11a2 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -302,7 +302,6 @@ <Compile Include="System\Threading\Monitor.cs" /> <Compile Include="System\Threading\ObjectHeader.cs" Condition="'$(UseSyncTable)' == 'true'" /> <Compile Include="System\Threading\Overlapped.cs" /> - <Compile Include="System\Threading\SpinLock.cs" /> <Compile Include="System\Threading\SynchronizationContext.cs" /> <Compile Include="System\Threading\SynchronizationContext.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" /> <Compile Include="System\Threading\SynchronizationContext.Dummy.cs" Condition="'$(EnableWinRT)' != 'true'" /> diff --git a/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs b/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs deleted file mode 100644 index 7f3cb120b..000000000 --- a/src/System.Private.CoreLib/src/System/Threading/SpinLock.cs +++ /dev/null @@ -1,704 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -#pragma warning disable 0420 - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// 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.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using Internal.Runtime.Augments; -using Internal.Threading.Tracing; - -namespace System.Threading -{ - /// <summary> - /// 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. - /// </summary> - /// <remarks> - /// <para> - /// Spin locks can be used for leaf-level locks where the object allocation implied by using a <see - /// cref="System.Threading.Monitor"/>, 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. - /// </para> - /// <para> - /// <see cref="SpinLock"/> 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 <see cref="SpinLock"/> 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. - /// </para> - /// <para> - /// Do not store <see cref="SpinLock"/> instances in readonly fields. - /// </para> - /// <para> - /// All members of <see cref="SpinLock"/> are thread-safe and may be used from multiple threads - /// concurrently. - /// </para> - /// </remarks> - [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 - - // 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 - private const int ID_DISABLED_AND_ANONYMOUS_OWNED = unchecked((int)0x80000001); //1000 0000 0000 0000 0000 0000 0000 0001 - - // 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; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int CompareExchange(ref int location, int value, int comparand, ref bool success) - { - int result = Interlocked.CompareExchange(ref location, value, comparand); - success = (result == comparand); - return result; - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/> - /// structure with the option to track thread IDs to improve debugging. - /// </summary> - /// <remarks> - /// The default constructor for <see cref="SpinLock"/> tracks thread ownership. - /// </remarks> - /// <param name="enableThreadOwnerTracking">Whether to capture and use thread IDs for debugging - /// purposes.</param> - public SpinLock(bool enableThreadOwnerTracking) - { - m_owner = LOCK_UNOWNED; - if (!enableThreadOwnerTracking) - { - m_owner |= LOCK_ID_DISABLE_MASK; - Debug.Assert(!IsThreadOwnerTrackingEnabled, "property should be false by now"); - } - } - - /// <summary> - /// Initializes a new instance of the <see cref="T:System.Threading.SpinLock"/> - /// structure with the option to track thread IDs to improve debugging. - /// </summary> - /// <remarks> - /// The default constructor for <see cref="SpinLock"/> tracks thread ownership. - /// </remarks> - /// <summary> - /// Acquires the lock in a reliable manner, such that even if an exception occurs within the method - /// call, <paramref name="lockTaken"/> can be examined reliably to determine whether the lock was - /// acquired. - /// </summary> - /// <remarks> - /// <see cref="SpinLock"/> 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 <see cref="IsThreadOwnerTrackingEnabled"/>), 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. - /// </remarks> - /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref - /// name="lockTaken"/> must be initialized to false prior to calling this method.</param> - /// <exception cref="T:System.Threading.LockRecursionException"> - /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. - /// </exception> - /// <exception cref="T:System.ArgumentException"> - /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling Enter. - /// </exception> - public void Enter(ref bool lockTaken) - { - //Try to keep the code and branching in this method as small as possible in order to inline the method - int observedOwner = m_owner; - if (lockTaken || //invalid parameter - (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired - CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) //acquiring the lock failed - ContinueTryEnter(Timeout.Infinite, ref lockTaken); // Then try the slow path if any of the above conditions is met - } - - /// <summary> - /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within - /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the - /// lock was acquired. - /// </summary> - /// <remarks> - /// Unlike <see cref="Enter"/>, 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. - /// </remarks> - /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref - /// name="lockTaken"/> must be initialized to false prior to calling this method.</param> - /// <exception cref="T:System.Threading.LockRecursionException"> - /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. - /// </exception> - /// <exception cref="T:System.ArgumentException"> - /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter. - /// </exception> - public void TryEnter(ref bool lockTaken) - { - int observedOwner = m_owner; - if (((observedOwner & LOCK_ID_DISABLE_MASK) == 0) | lockTaken) - { - // Thread tracking enabled or invalid arg. Take slow path. - ContinueTryEnter(0, ref lockTaken); - } - else if ((observedOwner & LOCK_ANONYMOUS_OWNED) != 0) - { - // Lock already held by someone - lockTaken = false; - } - else - { - // Lock wasn't held; try to acquire it. - CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken); - } - } - - /// <summary> - /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within - /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the - /// lock was acquired. - /// </summary> - /// <remarks> - /// Unlike <see cref="Enter"/>, TryEnter will not block indefinitely waiting for the lock to be - /// available. It will block until either the lock is available or until the <paramref - /// name="timeout"/> - /// has expired. - /// </remarks> - /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds - /// to wait, or a <see cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely. - /// </param> - /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref - /// name="lockTaken"/> must be initialized to false prior to calling this method.</param> - /// <exception cref="T:System.Threading.LockRecursionException"> - /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. - /// </exception> - /// <exception cref="T:System.ArgumentException"> - /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter. - /// </exception> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative - /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than <see cref="System.Int32.MaxValue"/> milliseconds. - /// </exception> - public void TryEnter(TimeSpan timeout, ref bool lockTaken) - { - // Validate the timeout - long totalMilliseconds = (long)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new System.ArgumentOutOfRangeException( - nameof(timeout), timeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); - } - - // Call reliable enter with the int-based timeout milliseconds - TryEnter((int)timeout.TotalMilliseconds, ref lockTaken); - } - - /// <summary> - /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within - /// the method call, <paramref name="lockTaken"/> can be examined reliably to determine whether the - /// lock was acquired. - /// </summary> - /// <remarks> - /// Unlike <see cref="Enter"/>, TryEnter will not block indefinitely waiting for the lock to be - /// available. It will block until either the lock is available or until the <paramref - /// name="millisecondsTimeout"/> has expired. - /// </remarks> - /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see - /// cref="System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param> - /// <param name="lockTaken">True if the lock is acquired; otherwise, false. <paramref - /// name="lockTaken"/> must be initialized to false prior to calling this method.</param> - /// <exception cref="T:System.Threading.LockRecursionException"> - /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. - /// </exception> - /// <exception cref="T:System.ArgumentException"> - /// The <paramref name="lockTaken"/> argument must be initialized to false prior to calling TryEnter. - /// </exception> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is - /// a negative number other than -1, which represents an infinite time-out.</exception> - public void TryEnter(int millisecondsTimeout, ref bool lockTaken) - { - int observedOwner = m_owner; - if (millisecondsTimeout < -1 || //invalid parameter - lockTaken || //invalid parameter - (observedOwner & ID_DISABLED_AND_ANONYMOUS_OWNED) != LOCK_ID_DISABLE_MASK || //thread tracking is enabled or the lock is already acquired - CompareExchange(ref m_owner, observedOwner | LOCK_ANONYMOUS_OWNED, observedOwner, ref lockTaken) != observedOwner) // acquiring the lock failed - ContinueTryEnter(millisecondsTimeout, ref lockTaken); // The call the slow pth - } - - /// <summary> - /// 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 - /// </summary> - /// <param name="millisecondsTimeout">The timeout milliseconds</param> - /// <param name="lockTaken">The lockTaken param</param> - private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) - { - // The fast path doesn't throw any exception, so we have to validate the parameters here - if (lockTaken) - { - lockTaken = false; - throw new System.ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); - } - - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException( - nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); - } - - uint startTime = 0; - if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) - { - startTime = TimeoutHelper.GetTime(); - } - - if (SpinLockTrace.Enabled) - { - SpinLockTrace.SpinLock_FastPathFailed(m_owner); - } - - if (IsThreadOwnerTrackingEnabled) - { - // Slow path for enabled thread tracking mode - ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, 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 availability - // Also the spins count is increases 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; - int turn = int.MaxValue; - //***Step 1, take the lock or update the waiters - - // try to acquire the lock directly if possible or update the waiters count - observedOwner = m_owner; - if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) - { - if (CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) - { - // Acquired lock - return; - } - - if (millisecondsTimeout == 0) - { - // Did not acquire lock in CompareExchange and timeout is 0 so fail fast - return; - } - } - else if (millisecondsTimeout == 0) - { - // Did not acquire lock as owned and timeout is 0 so fail fast - return; - } - 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) - turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1; - } - - //***Step 2. Spinning - //lock acquired failed and waiters updated - int processorCount = PlatformHelper.ProcessorCount; - if (turn < processorCount) - { - int processFactor = 1; - for (int i = 1; i <= turn * SPINNING_FACTOR; i++) - { - RuntimeThread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); - if (processFactor < processorCount) - processFactor++; - observedOwner = m_owner; - if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) - { - 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 - Debug.Assert((newOwner & WAITERS_MASK) >= 0); - - if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) - { - return; - } - } - } - - // Check the timeout. - if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) - { - 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) - { - 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 - Debug.Assert((newOwner & WAITERS_MASK) >= 0); - - if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) - { - return; - } - } - - if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) - { - RuntimeThread.Sleep(1); - } - else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) - { - RuntimeThread.Sleep(0); - } - else - { - RuntimeThread.Yield(); - } - - if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) - { - //Check the timeout. - if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) - { - DecrementWaiters(); - return; - } - } - - yieldsoFar++; - } - } - - /// <summary> - /// decrements the waiters, in case of the timeout is expired - /// </summary> - 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) - { - Debug.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped - break; - } - spinner.SpinOnce(); - } - } - - /// <summary> - /// ContinueTryEnter for the thread tracking mode enabled - /// </summary> - private void ContinueTryEnterWithThreadTracking(int millisecondsTimeout, uint startTime, ref bool lockTaken) - { - Debug.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 = Environment.CurrentManagedThreadId; - if (m_owner == m_newOwner) - { - // We don't allow lock recursion. - throw new LockRecursionException(SR.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 (CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned) - { - return; - } - } - // Check the timeout. We only RDTSC if the next spin will yield, to amortize the cost. - if (millisecondsTimeout == 0 || - (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield && - TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)) - { - return; - } - } while (true); - } - - /// <summary> - /// Releases the lock. - /// </summary> - /// <remarks> - /// The default overload of <see cref="Exit()"/> provides the same behavior as if calling <see - /// cref="Exit(bool)"/> using true as the argument, but Exit() could be slightly faster than Exit(true). - /// </remarks> - /// <exception cref="SynchronizationLockException"> - /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock. - /// </exception> - public void Exit() - { - //This is the fast path for the thread tracking is disabled, otherwise go to the slow path - if ((m_owner & LOCK_ID_DISABLE_MASK) == 0) - ExitSlowPath(true); - else - Interlocked.Decrement(ref m_owner); - } - - /// <summary> - /// Releases the lock. - /// </summary> - /// <param name="useMemoryBarrier"> - /// A Boolean value that indicates whether a memory fence should be issued in order to immediately - /// publish the exit operation to other threads. - /// </param> - /// <remarks> - /// Calling <see cref="Exit(bool)"/> with the <paramref name="useMemoryBarrier"/> argument set to - /// true will improve the fairness of the lock at the expense of some performance. The default <see - /// cref="Enter"/> - /// overload behaves as if specifying true for <paramref name="useMemoryBarrier"/>. - /// </remarks> - /// <exception cref="SynchronizationLockException"> - /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock. - /// </exception> - public void Exit(bool useMemoryBarrier) - { - // This is the fast path for the thread tracking is diabled and not to use memory barrier, otherwise go to the slow path - // The reason not to add else statement if the usememorybarrier is that it will add more branching in the code and will prevent - // method inlining, so this is optimized for useMemoryBarrier=false and Exit() overload optimized for useMemoryBarrier=true. - int tmpOwner = m_owner; - if ((tmpOwner & LOCK_ID_DISABLE_MASK) != 0 & !useMemoryBarrier) - { - m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); - } - else - ExitSlowPath(useMemoryBarrier); - } - - /// <summary> - /// The slow path for exit method if the fast path failed - /// </summary> - /// <param name="useMemoryBarrier"> - /// A Boolean value that indicates whether a memory fence should be issued in order to immediately - /// publish the exit operation to other threads - /// </param> - private void ExitSlowPath(bool useMemoryBarrier) - { - bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0; - if (threadTrackingEnabled && !IsHeldByCurrentThread) - { - throw new System.Threading.SynchronizationLockException( - SR.SpinLock_Exit_SynchronizationLockException); - } - - if (useMemoryBarrier) - { - if (threadTrackingEnabled) - Interlocked.Exchange(ref m_owner, LOCK_UNOWNED); - else - Interlocked.Decrement(ref m_owner); - } - else - { - if (threadTrackingEnabled) - m_owner = LOCK_UNOWNED; - else - { - int tmpOwner = m_owner; - m_owner = tmpOwner & (~LOCK_ANONYMOUS_OWNED); - } - } - } - - /// <summary> - /// Gets whether the lock is currently held by any thread. - /// </summary> - public bool IsHeld - { - get - { - if (IsThreadOwnerTrackingEnabled) - return m_owner != LOCK_UNOWNED; - - return (m_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED; - } - } - - /// <summary> - /// Gets whether the lock is currently held by any thread. - /// </summary> - /// <summary> - /// Gets whether the lock is held by the current thread. - /// </summary> - /// <remarks> - /// 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. - /// </remarks> - /// <exception cref="T:System.InvalidOperationException"> - /// Thread ownership tracking is disabled. - /// </exception> - public bool IsHeldByCurrentThread - { - get - { - if (!IsThreadOwnerTrackingEnabled) - { - throw new InvalidOperationException(SR.SpinLock_IsHeldByCurrentThread); - } - return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Environment.CurrentManagedThreadId); - } - } - - /// <summary>Gets whether thread ownership tracking is enabled for this instance.</summary> - public bool IsThreadOwnerTrackingEnabled - { - get { return (m_owner & LOCK_ID_DISABLE_MASK) == 0; } - } - - #region Debugger proxy class - /// <summary> - /// Internal class used by debug type proxy attribute to display the owner thread ID - /// </summary> - internal class SystemThreading_SpinLockDebugView - { - // SpinLock object - private SpinLock m_spinLock; - - /// <summary> - /// SystemThreading_SpinLockDebugView constructor - /// </summary> - /// <param name="spinLock">The SpinLock to be proxied.</param> - 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; - } - - /// <summary> - /// Checks if the lock is held by the current thread or not - /// </summary> - public bool? IsHeldByCurrentThread - { - get - { - try - { - return m_spinLock.IsHeldByCurrentThread; - } - catch (InvalidOperationException) - { - return null; - } - } - } - - /// <summary> - /// Gets the current owner thread, zero if it is released - /// </summary> - public int? OwnerThreadID - { - get - { - if (m_spinLock.IsThreadOwnerTrackingEnabled) - { - return m_spinLock.m_owner; - } - else - { - return null; - } - } - } - - - /// <summary> - /// Gets whether the lock is currently held by any thread or not. - /// </summary> - public bool IsHeld - { - get { return m_spinLock.IsHeld; } - } - } - #endregion - - } -} -#pragma warning restore 0420 |