/// <summary> /// Unblocks a wait block. /// </summary> /// <param name="waitBlock">The wait block to unblock.</param> private void Unblock(WaitBlock *waitBlock) { int flags; // Clear the spinning flag. do { flags = waitBlock->Flags; } while (Interlocked.CompareExchange( ref waitBlock->Flags, flags & ~WaiterSpinning, flags ) != flags); if ((flags & WaiterSpinning) == 0) { #if RIGOROUS_CHECKS System.Diagnostics.Trace.Assert(_wakeEvent != IntPtr.Zero); #endif NativeMethods.NtReleaseKeyedEvent( _wakeEvent, new IntPtr(waitBlock), false, IntPtr.Zero ); } }
private void VerifyWaitersList() { bool firstSharedWaiterInList = false; if (_firstSharedWaiter == _waitersListHead) { firstSharedWaiterInList = true; } for ( WaitBlock *wb = _waitersListHead->Flink; wb != _waitersListHead; wb = wb->Flink ) { System.Diagnostics.Trace.Assert(wb == wb->Flink->Blink); System.Diagnostics.Trace.Assert((wb->Flags & ~WaiterFlags) == 0); if (_firstSharedWaiter == wb) { firstSharedWaiterInList = true; } } System.Diagnostics.Trace.Assert(firstSharedWaiterInList); }
private static void InsertTailList(WaitBlock *listHead, WaitBlock *entry) { WaitBlock *blink; blink = listHead->Blink; entry->Flink = listHead; entry->Blink = blink; blink->Flink = entry; listHead->Blink = entry; }
private static void InsertHeadList(WaitBlock *listHead, WaitBlock *entry) { WaitBlock *flink; flink = listHead->Flink; entry->Flink = flink; entry->Blink = listHead; flink->Blink = entry; listHead->Flink = entry; }
private static WaitBlock *RemoveHeadList(WaitBlock *listHead) { WaitBlock *flink; WaitBlock *entry; entry = listHead->Flink; flink = entry->Flink; listHead->Flink = flink; flink->Blink = listHead; return(entry); }
private static bool RemoveEntryList(WaitBlock *entry) { WaitBlock *blink; WaitBlock *flink; flink = entry->Flink; blink = entry->Blink; blink->Flink = flink; flink->Blink = blink; return(flink == blink); }
private void Dispose(bool disposing) { if (_waitersListHead != null) { Marshal.FreeHGlobal((IntPtr)_waitersListHead); _waitersListHead = null; } if (_wakeEvent != IntPtr.Zero) { NativeMethods.CloseHandle(_wakeEvent); _wakeEvent = IntPtr.Zero; } }
/// <summary> /// Wakes one exclusive waiter. /// </summary> private void WakeExclusive() { WaitBlock *wb; WaitBlock *exclusiveWb = null; _lock.Acquire(); try { wb = _waitersListHead->Flink; if ( wb != _waitersListHead && (wb->Flags & WaiterExclusive) != 0 ) { exclusiveWb = RemoveHeadList(_waitersListHead); wb = _waitersListHead->Flink; #if ENABLE_STATISTICS _exclusiveWaitersCount--; #endif } if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); } } finally { _lock.Release(); } if (exclusiveWb != null) { this.Unblock(exclusiveWb); } }
/// <summary> /// Creates a FairResourceLock, specifying a spin count. /// </summary> /// <param name="spinCount"> /// The number of times to spin before going to sleep. /// </param> public FairResourceLock(int spinCount) { _value = 0; _lock = new SpinLock(); _spinCount = Environment.ProcessorCount != 1 ? spinCount : 0; _waitersListHead = (WaitBlock *)Marshal.AllocHGlobal(WaitBlock.SizeOf); _waitersListHead->Flink = _waitersListHead; _waitersListHead->Blink = _waitersListHead; _waitersListHead->Flags = 0; _firstSharedWaiter = _waitersListHead; #if !DEFER_EVENT_CREATION _wakeEvent = this.CreateWakeEvent(); #endif }
/// <summary> /// Inserts a wait block into the waiters list. /// </summary> /// <param name="waitBlock">The wait block to insert.</param> /// <param name="position">Specifies where to insert the wait block.</param> private void InsertWaitBlock(WaitBlock *waitBlock, ListPosition position) { switch (position) { case ListPosition.First: InsertHeadList(_waitersListHead, waitBlock); break; case ListPosition.LastExclusive: InsertTailList(_firstSharedWaiter, waitBlock); break; case ListPosition.Last: InsertTailList(_waitersListHead, waitBlock); break; } }
/// <summary> /// Wakes multiple shared waiters. /// </summary> private void WakeShared() { WaitBlock wakeList = new WaitBlock(); WaitBlock* wb; wakeList.Flink = &wakeList; wakeList.Blink = &wakeList; _lock.Acquire(); try { wb = _firstSharedWaiter; while (true) { WaitBlock* flink; if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); break; } #if RIGOROUS_CHECKS // We shouldn't have *any* exclusive waiters at this // point since we started at _firstSharedWaiter. if ((wb->Flags & WaiterExclusive) != 0) System.Diagnostics.Trace.Fail("Exclusive waiter behind shared waiters!"); #endif // Remove the waiter and add it to the wake list. flink = wb->Flink; RemoveEntryList(wb); InsertTailList(&wakeList, wb); #if ENABLE_STATISTICS _sharedWaitersCount--; #endif wb = flink; } // Reset the first shared waiter pointer. _firstSharedWaiter = _waitersListHead; } finally { _lock.Release(); } // Carefully traverse the wake list and wake each waiter. wb = wakeList.Flink; while (wb != &wakeList) { WaitBlock* flink; flink = wb->Flink; this.Unblock(wb); wb = flink; } }
/// <summary> /// Blocks on a wait block. /// </summary> /// <param name="waitBlock">The wait block to block on.</param> private void Block(WaitBlock *waitBlock) { int flags; // Spin for a while. for (int j = 0; j < _spinCount; j++) { if ((Thread.VolatileRead(ref waitBlock->Flags) & WaiterSpinning) == 0) { break; } } #if DEFER_EVENT_CREATION IntPtr wakeEvent; wakeEvent = Interlocked.CompareExchange(ref _wakeEvent, IntPtr.Zero, IntPtr.Zero); if (wakeEvent == IntPtr.Zero) { wakeEvent = this.CreateWakeEvent(); if (Interlocked.CompareExchange(ref _wakeEvent, wakeEvent, IntPtr.Zero) != IntPtr.Zero) { NativeMethods.CloseHandle(wakeEvent); } } #endif // Clear the spinning flag. do { flags = waitBlock->Flags; } while (Interlocked.CompareExchange( ref waitBlock->Flags, flags & ~WaiterSpinning, flags ) != flags); // Go to sleep if necessary. if ((flags & WaiterSpinning) != 0) { #if ENABLE_STATISTICS if ((waitBlock->Flags & WaiterExclusive) != 0) { Interlocked.Increment(ref _acqExclSlpCount); } else { Interlocked.Increment(ref _acqShrdSlpCount); } #endif if (NativeMethods.NtWaitForKeyedEvent( _wakeEvent, new IntPtr(waitBlock), false, IntPtr.Zero ) != 0) { throw new Exception(Utils.MsgFailedToWaitIndefinitely); } } }
/// <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; // Try to obtain the lock. // Note that we don't acquire if there are waiters and // the lock is already owned in shared mode, in order to // give exclusive acquires precedence. if ( (value & LockOwned) == 0 || ((value & LockWaiters) == 0 && ((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0) ) { if ((value & LockOwned) == 0) { #if RIGOROUS_CHECKS System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0); #endif if (Interlocked.CompareExchange( ref _value, value + LockOwned + LockSharedOwnersIncrement, value ) == value) { break; } } else { if (Interlocked.CompareExchange( ref _value, value + LockSharedOwnersIncrement, value ) == value) { break; } } } else if (i >= _spinCount) { // We need to wait. WaitBlock waitBlock; waitBlock.Flags = WaiterSpinning; // Obtain the waiters list lock. _lock.Acquire(); try { // Try to set the waiters bit. if (Interlocked.CompareExchange( ref _value, value | LockWaiters, value ) != value) { #if ENABLE_STATISTICS Interlocked.Increment(ref _insWaitBlkRetryCount); #endif continue; } // Put our wait block behind other waiters. this.InsertWaitBlock(&waitBlock, ListPosition.Last); // Set the first shared waiter pointer. if ( waitBlock.Blink == _waitersListHead || (waitBlock.Blink->Flags & WaiterExclusive) != 0 ) { _firstSharedWaiter = &waitBlock; } #if ENABLE_STATISTICS _sharedWaitersCount++; if (_peakShrdWtrsCount < _sharedWaitersCount) { _peakShrdWtrsCount = _sharedWaitersCount; } #endif } finally { _lock.Release(); } #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdBlkCount); #endif this.Block(&waitBlock); // Go back and try again. continue; } #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdContCount); #endif i++; } }
/// <summary> /// Creates a FairResourceLock, specifying a spin count. /// </summary> /// <param name="spinCount"> /// The number of times to spin before going to sleep. /// </param> public FairResourceLock(int spinCount) { _value = 0; _lock = new SpinLock(); _spinCount = Environment.ProcessorCount != 1 ? spinCount : 0; _waitersListHead = (WaitBlock*)Marshal.AllocHGlobal(WaitBlock.SizeOf); _waitersListHead->Flink = _waitersListHead; _waitersListHead->Blink = _waitersListHead; _waitersListHead->Flags = 0; _firstSharedWaiter = _waitersListHead; #if !DEFER_EVENT_CREATION _wakeEvent = this.CreateWakeEvent(); #endif }
/// <summary> /// Wakes multiple shared waiters. /// </summary> private void WakeShared() { WaitBlock wakeList = new WaitBlock(); WaitBlock *wb; wakeList.Flink = &wakeList; wakeList.Blink = &wakeList; _lock.Acquire(); try { wb = _firstSharedWaiter; while (true) { WaitBlock *flink; if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); break; } #if RIGOROUS_CHECKS // We shouldn't have *any* exclusive waiters at this // point since we started at _firstSharedWaiter. if ((wb->Flags & WaiterExclusive) != 0) { System.Diagnostics.Trace.Fail("Exclusive waiter behind shared waiters!"); } #endif // Remove the waiter and add it to the wake list. flink = wb->Flink; RemoveEntryList(wb); InsertTailList(&wakeList, wb); #if ENABLE_STATISTICS _sharedWaitersCount--; #endif wb = flink; } // Reset the first shared waiter pointer. _firstSharedWaiter = _waitersListHead; } finally { _lock.Release(); } // Carefully traverse the wake list and wake each waiter. wb = wakeList.Flink; while (wb != &wakeList) { WaitBlock *flink; flink = wb->Flink; this.Unblock(wb); wb = flink; } }
/// <summary> /// Wakes either one exclusive waiter or multiple shared waiters. /// </summary> private void Wake() { WaitBlock wakeList = new WaitBlock(); WaitBlock *wb; WaitBlock *exclusiveWb = null; wakeList.Flink = &wakeList; wakeList.Blink = &wakeList; _lock.Acquire(); try { bool first = true; while (true) { wb = _waitersListHead->Flink; if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); break; } // If this is an exclusive waiter, don't wake // anyone else. if (first && (wb->Flags & WaiterExclusive) != 0) { exclusiveWb = RemoveHeadList(_waitersListHead); #if ENABLE_STATISTICS _exclusiveWaitersCount--; #endif break; } #if RIGOROUS_CHECKS // If this is not the first waiter we have looked at // and it is an exclusive waiter, then we have a bug - // we should have stopped upon encountering the first // exclusive waiter (previous block), so this means // we have an exclusive waiter *behind* shared waiters. if (!first && (wb->Flags & WaiterExclusive) != 0) { System.Diagnostics.Trace.Fail("Exclusive waiter behind shared waiters!"); } #endif // Remove the (shared) waiter and add it to the wake list. wb = RemoveHeadList(_waitersListHead); InsertTailList(&wakeList, wb); #if ENABLE_STATISTICS _sharedWaitersCount--; #endif first = false; } if (exclusiveWb == null) { // If we removed shared waiters, we removed all of them. // Reset the first shared waiter pointer. // Note that this also applies if we haven't woken anyone // at all; this just becomes a redundant assignment. _firstSharedWaiter = _waitersListHead; } } finally { _lock.Release(); } // If we removed one exclusive waiter, unblock it. if (exclusiveWb != null) { this.Unblock(exclusiveWb); return; } // Carefully traverse the wake list and wake each shared waiter. wb = wakeList.Flink; while (wb != &wakeList) { WaitBlock *flink; flink = wb->Flink; this.Unblock(wb); wb = flink; } }
/// <summary> /// Wakes either one exclusive waiter or multiple shared waiters. /// </summary> private void Wake() { WaitBlock wakeList = new WaitBlock(); WaitBlock* wb; WaitBlock* exclusiveWb = null; wakeList.Flink = &wakeList; wakeList.Blink = &wakeList; _lock.Acquire(); try { bool first = true; while (true) { wb = _waitersListHead->Flink; if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); break; } // If this is an exclusive waiter, don't wake // anyone else. if (first && (wb->Flags & WaiterExclusive) != 0) { exclusiveWb = RemoveHeadList(_waitersListHead); #if ENABLE_STATISTICS _exclusiveWaitersCount--; #endif break; } #if RIGOROUS_CHECKS // If this is not the first waiter we have looked at // and it is an exclusive waiter, then we have a bug - // we should have stopped upon encountering the first // exclusive waiter (previous block), so this means // we have an exclusive waiter *behind* shared waiters. if (!first && (wb->Flags & WaiterExclusive) != 0) { System.Diagnostics.Trace.Fail("Exclusive waiter behind shared waiters!"); } #endif // Remove the (shared) waiter and add it to the wake list. wb = RemoveHeadList(_waitersListHead); InsertTailList(&wakeList, wb); #if ENABLE_STATISTICS _sharedWaitersCount--; #endif first = false; } if (exclusiveWb == null) { // If we removed shared waiters, we removed all of them. // Reset the first shared waiter pointer. // Note that this also applies if we haven't woken anyone // at all; this just becomes a redundant assignment. _firstSharedWaiter = _waitersListHead; } } finally { _lock.Release(); } // If we removed one exclusive waiter, unblock it. if (exclusiveWb != null) { this.Unblock(exclusiveWb); return; } // Carefully traverse the wake list and wake each shared waiter. wb = wakeList.Flink; while (wb != &wakeList) { WaitBlock* flink; flink = wb->Flink; this.Unblock(wb); wb = flink; } }
/// <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; // Try to obtain the lock. // Note that we don't acquire if there are waiters and // the lock is already owned in shared mode, in order to // give exclusive acquires precedence. if ( (value & LockOwned) == 0 || ((value & LockWaiters) == 0 && ((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0) ) { if ((value & LockOwned) == 0) { #if RIGOROUS_CHECKS System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0); #endif if (Interlocked.CompareExchange( ref _value, value + LockOwned + LockSharedOwnersIncrement, value ) == value) break; } else { if (Interlocked.CompareExchange( ref _value, value + LockSharedOwnersIncrement, value ) == value) break; } } else if (i >= _spinCount) { // We need to wait. WaitBlock waitBlock; waitBlock.Flags = WaiterSpinning; // Obtain the waiters list lock. _lock.Acquire(); try { // Try to set the waiters bit. if (Interlocked.CompareExchange( ref _value, value | LockWaiters, value ) != value) { #if ENABLE_STATISTICS Interlocked.Increment(ref _insWaitBlkRetryCount); #endif continue; } // Put our wait block behind other waiters. this.InsertWaitBlock(&waitBlock, ListPosition.Last); // Set the first shared waiter pointer. if ( waitBlock.Blink == _waitersListHead || (waitBlock.Blink->Flags & WaiterExclusive) != 0 ) { _firstSharedWaiter = &waitBlock; } #if ENABLE_STATISTICS _sharedWaitersCount++; if (_peakShrdWtrsCount < _sharedWaitersCount) _peakShrdWtrsCount = _sharedWaitersCount; #endif } finally { _lock.Release(); } #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdBlkCount); #endif this.Block(&waitBlock); // Go back and try again. continue; } #if ENABLE_STATISTICS Interlocked.Increment(ref _acqShrdContCount); #endif i++; } }