/// <summary> /// Acquires the lock in shared mode, blocking /// if necessary. /// </summary> /// <remarks> /// Exclusive acquires are given precedence over shared /// acquires. /// </remarks> public void AcquireShared() { int value; int i = 0; #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdCount); #endif while (true) { value = _value; // Case 1: lock not owned AND no exclusive waiter is waking up AND // there are no shared owners AND there are no exclusive waiters if ((value & ( LockOwned | (LockSharedOwnersMask << LockSharedOwnersShift) | ExclusiveMask )) == 0) { if (Interlocked.CompareExchange( ref _value, value + LockOwned + LockSharedOwnersIncrement, value ) == value) { break; } } // Case 2: lock is owned AND no exclusive waiter is waking up AND // there are shared owners AND there are no exclusive waiters else if ( (value & LockOwned) != 0 && ((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0 && (value & ExclusiveMask) == 0 ) { if (Interlocked.CompareExchange( ref _value, value + LockSharedOwnersIncrement, value ) == value) { break; } } // Other cases. else if (i >= SpinCount) { #if DEFER_EVENT_CREATION this.EnsureEventCreated(ref _sharedWakeEvent); #endif if (Interlocked.CompareExchange( ref _value, value + LockSharedWaitersIncrement, value ) == value) { #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdSlpCount); int shrdWtrsCount = (value >> LockSharedWaitersShift) & LockSharedWaitersMask; Interlocked2.Set( ref _peakShrdWtrsCount, (p) => p < shrdWtrsCount, (p) => shrdWtrsCount ); #endif // Go to sleep. if (NativeMethods.WaitForSingleObject( _sharedWakeEvent, Timeout.Infinite ) != NativeMethods.WaitObject0) { UtilsBreak("Utils.MsgFailedToWaitIndefinitely"); } // Go back and try again. continue; } } #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdContCount); #endif i++; } }
/// <summary> /// Acquires the lock in exclusive mode, blocking /// if necessary. /// </summary> /// <remarks> /// Exclusive acquires are given precedence over shared /// acquires. /// </remarks> public void AcquireExclusive() { int value; int i = 0; #if ENABLE_STATISTICS Interlocked.Increment(ref _acqExclCount); #endif while (true) { value = _value; // Case 1: lock not owned AND an exclusive waiter is not waking up. // Here we don't have to check if there are exclusive waiters, because // if there are the lock would be owned, and we are checking that anyway. if ((value & (LockOwned | LockExclusiveWaking)) == 0) { #if RIGOROUS_CHECKS System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0); System.Diagnostics.Trace.Assert(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) == 0); #endif if (Interlocked.CompareExchange( ref _value, value + LockOwned, value ) == value) { break; } } // Case 2: lock owned OR lock not owned and an exclusive waiter is waking up. // The second case means an exclusive waiter has just been woken up and is // going to acquire the lock. We have to go to sleep to make sure we don't // steal the lock. else if (i >= SpinCount) { #if DEFER_EVENT_CREATION // This call must go *before* the next operation. Otherwise, // we will have a race condition between potential releasers // and us. this.EnsureEventCreated(ref _exclusiveWakeEvent); #endif if (Interlocked.CompareExchange( ref _value, value + LockExclusiveWaitersIncrement, value ) == value) { #if ENABLE_STATISTICS Interlocked.Increment(ref _acqExclSlpCount); int exclWtrsCount = (value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask; Interlocked2.Set( ref _peakExclWtrsCount, (p) => p < exclWtrsCount, (p) => exclWtrsCount ); #endif // Go to sleep. if (NativeMethods.WaitForSingleObject( _exclusiveWakeEvent, Timeout.Infinite ) != NativeMethods.WaitObject0) { UtilsBreak("Utils.MsgFailedToWaitIndefinitely"); } // Acquire the lock. // At this point *no one* should be able to steal the lock from us. do { value = _value; #if RIGOROUS_CHECKS System.Diagnostics.Trace.Assert((value & LockOwned) == 0); System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) != 0); #endif } while(Interlocked.CompareExchange( ref _value, value + LockOwned - LockExclusiveWaking, value ) != value); break; } } #if ENABLE_STATISTICS Interlocked.Increment(ref _acqExclContCount); #endif i++; } }