/// <summary> /// Waits on 'waitEvent' with a timeout /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// </summary> private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout) { #if DEBUG Debug.Assert(MyLockHeld); #endif waitEvent.Reset(); numWaiters++; fNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (numWriteWaiters == 1) { SetWritersWaiting(); } if (numWriteUpgradeWaiters == 1) { SetUpgraderWaiting(); } bool waitSuccessful = false; ExitMyLock(); // Do the wait outside of any lock try { waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); } finally { EnterMyLock(); --numWaiters; if (numWriteWaiters == 0 && numWriteUpgradeWaiters == 0 && numUpgradeWaiters == 0 && numReadWaiters == 0) { fNoWaiters = true; } if (numWriteWaiters == 0) { ClearWritersWaiting(); } if (numWriteUpgradeWaiters == 0) { ClearUpgraderWaiting(); } if (!waitSuccessful) // We may also be aboutto throw for some reason. Exit myLock. { ExitMyLock(); } } return(waitSuccessful); }
/// <summary> /// Returns the item at the head of the queue without removing it (core method) /// </summary> /// <param name="item">The item at the head of the queue</param> /// <param name="timeout">Peeking timeout</param> /// <param name="token">Cancellation token</param> /// <returns>True if the item was read</returns> protected override bool TryPeekCore(out T item, int timeout, CancellationToken token) { if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } bool result = false; item = default(T); var timeoutTracker = new TimeoutTracker(timeout); if (_isBackgroundTransferingEnabled) { result = _highLevelQueue.TryPeek(out item, 0, default(CancellationToken)); if (!result && _addingMode == LevelingQueueAddingMode.PreferLiveData) { result = _lowLevelQueue.TryPeek(out item, 0, default(CancellationToken)); // Can peek from lower queue only when ordering is not required } if (!result) { result = TryPeekExclusively(out item, timeoutTracker, token); // Should be mutually exclusive with background transferer to prevent item lost or reordering } } else { if (_peekMonitor.WaiterCount == 0) { result = TryPeekFast(out item); if (!result && timeout != 0) { result = TryPeekSlow(out item, timeoutTracker, token); } } else { // Preserve fairness result = TryPeekSlow(out item, timeoutTracker, token); } } return(result); }
// ============ Take ============ /// <summary> /// Removes item from the head of the queue (slow path) /// </summary> /// <returns>Was taken sucessufully</returns> private bool TryTakeSlow(out T item, int timeout, CancellationToken token) { TimeoutTracker timeoutTracker = new TimeoutTracker(timeout); // Check WaiterCount to preserve fairness // Check ItemCount to prevent stucking inside lock on _takeMonitor bool canTakeConcurrently = _takeMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > _takeMonitor.WaiterCount + _nonFairItemThreshold; if (timeout != 0 && !canTakeConcurrently) { Thread.Yield(); canTakeConcurrently = _takeMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > _takeMonitor.WaiterCount + _nonFairItemThreshold; } if (canTakeConcurrently) { var headSegment = TryGetNonCompletedHeadSegment(); if (headSegment != null && headSegment.TryTake(out item)) { return(true); } if (timeout == 0 || timeoutTracker.IsTimeouted) { item = default(T); return(false); } } // Use waiting scheme using (var waiter = _takeMonitor.Enter(timeoutTracker.RemainingMilliseconds, token)) { do { var headSegment = TryGetNonCompletedHeadSegment(); if (headSegment != null && headSegment.TryTake(out item)) { return(true); } }while (waiter.Wait()); } item = default(T); return(false); }
/// <param name="t"> A token identifying an operation being tracked </param> /// <returns> Returns a disposable object which (when disposed) will release the token from being tracked </returns> public IDisposable StartTrackingOperation(TOperationToken t) { if (DateTime.Now > _currentTimeoutTracker.StopUsingDueTime) { // Create a new tracker var currentTimeoutTracker = _currentTimeoutTracker; var newTimeoutTracker = new TimeoutTracker(_timeoutMs, _timeoutCallback); // and make sure we only use one new tracker if (Interlocked.CompareExchange(ref _currentTimeoutTracker, newTimeoutTracker, currentTimeoutTracker) != currentTimeoutTracker) { newTimeoutTracker.Dispose(); } // Don't touch the old currentTimeoutTracker which still tracks its stuff for completion } return(_currentTimeoutTracker.TrackOperationTimeout(t)); }
// =============== Peek =============== /// <summary> /// Returns the item at the head of the queue without removing it (slow path) /// </summary> /// <returns>Was sucessufully</returns> private bool TryPeekSlow(out T item, int timeout, CancellationToken token) { TimeoutTracker timeoutTracker = new TimeoutTracker(timeout); if (timeout != 0 && _peekMonitor.WaiterCount > 0) { Thread.Yield(); } // Check WaiterCount to preserve fairness // Check ItemCount to prevent stucking inside lock on _peekMonitor if (_peekMonitor.WaiterCount == 0 || Volatile.Read(ref _itemCount) > 0) { var headSegment = TryGetNonCompletedHeadSegment(); if (headSegment != null && headSegment.TryPeek(out item)) { return(true); } if (timeout == 0 || timeoutTracker.IsTimeouted) { item = default(T); return(false); } } // Use waiting scheme using (var waiter = _peekMonitor.Enter(timeoutTracker.RemainingMilliseconds, token)) { do { var headSegment = TryGetNonCompletedHeadSegment(); if (headSegment != null && headSegment.TryPeek(out item)) { return(true); } }while (waiter.Wait()); } item = default(T); return(false); }
private bool TryEnterReadLockSlow(int timeout) { var tracker = new TimeoutTracker(timeout); while (tracker.IsExpired == false) { bool lockTaken = false; _readWaitLock.TryEnter(tracker.RemainingMilliseconds, ref lockTaken); try { if (lockTaken == false) { return(false); } if (_readerWait.IsSet) { _readerWait.Reset(); } if (TryEnterReadLockCore()) { return(true); } } finally { if (lockTaken) { _readWaitLock.Exit(false); } } _readerWait.Wait(tracker.RemainingMilliseconds); if (TryEnterReadLockCore()) { return(true); } } return(false); }
/// <summary> /// Adds new item to the tail of the queue (slow path) /// </summary> /// <returns>Was added sucessufully</returns> private bool TryAddSlow(T item, int timeout, CancellationToken token) { TimeoutTracker timeoutTracker = new TimeoutTracker(timeout); if (timeout != 0 && _addMonitor.WaiterCount > 0) { Thread.Yield(); } // Check WaiterCount to preserve fairness // Check _segments.Count to prevent stucking inside lock if (_addMonitor.WaiterCount == 0 || _segments.Count <= _maxSegmentCount - _nonFairSegmentThreshold) { var tailSegment = TryGetNonFullTailSegment(); if (tailSegment != null && tailSegment.TryAdd(item)) { return(true); } if (timeout == 0 || timeoutTracker.IsTimeouted) { return(false); } } // Use waiting scheme using (var waiter = _addMonitor.Enter(timeoutTracker.RemainingMilliseconds, token)) { do { var tailSegment = TryGetNonFullTailSegment(); if (tailSegment != null && tailSegment.TryAdd(item)) { return(true); } Debug.Assert(tailSegment == null || tailSegment.IsFull); }while (waiter.Wait()); } return(false); }
private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) { #if !FEATURE_NETCORE Thread.BeginCriticalRegion(); #endif // !FEATURE_NETCORE bool result = false; try { result = TryEnterUpgradeableReadLockCore(timeout); } finally { #if !FEATURE_NETCORE if (!result) { Thread.EndCriticalRegion(); } #endif // !FEATURE_NETCORE } return(result); }
/// <summary> /// Blocks background transferer and attempts to peek the item /// </summary> private bool TryPeekExclusively(out T item, TimeoutTracker timeoutTracker, CancellationToken token) { TurboContract.Assert(_isBackgroundTransferingEnabled, conditionString: "_isBackgroundTransferingEnabled"); using (var gateGuard = _backgoundTransfererExclusive.EnterMain(Timeout.Infinite, token)) // This should happen fast { TurboContract.Assert(gateGuard.IsAcquired, conditionString: "gateGuard.IsAcquired"); if (_peekMonitor.WaiterCount == 0) { if (TryPeekFast(out item)) { return(true); } if (timeoutTracker.OriginalTimeout == 0) { return(false); } } return(TryPeekSlow(out item, timeoutTracker, token)); } }
/// <summary> /// Blocks background transferer and attempts to take the item /// </summary> private bool TryTakeExclusively(out T item, TimeoutTracker timeoutTracker, CancellationToken token) { Debug.Assert(_isBackgroundTransferingEnabled); using (var gateGuard = _backgoundTransfererExclusive.EnterMain(Timeout.Infinite, token)) // This should happen fast { Debug.Assert(gateGuard.IsAcquired); if (_takeMonitor.WaiterCount == 0) { if (TryTakeFast(out item)) { return(true); } if (timeoutTracker.OriginalTimeout == 0) { return(false); } } return(TryTakeSlow(out item, timeoutTracker, token)); } }
private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout) { if(fDisposed) throw new ObjectDisposedException(null); int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; if (!fIsReentrant) { if (id == upgradeLockOwnerId) { //Check for AU->AU throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveUpgradeNotAllowed)); } else if (id == writeLockOwnerId) { //Check for AU->AW throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterWriteNotAllowed)); } EnterMyLock(); lrwc = GetThreadRWCount(true); //Can't acquire upgrade lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed)); } } else { EnterMyLock(); lrwc = GetThreadRWCount(false); if (id == upgradeLockOwnerId) { lrwc.upgradecount++; ExitMyLock(); return true; } else if (id == writeLockOwnerId) { //Write lock is already held, Just update the global state //to show presence of upgrader. Debug.Assert((owners & WRITER_HELD) > 0); owners++; upgradeLockOwnerId = id; lrwc.upgradecount++; if (lrwc.readercount > 0) fUpgradeThreadHoldingRead = true; ExitMyLock(); return true; } else if (lrwc.readercount > 0) { //Upgrade locks may not be acquired if only read locks have been //acquired. ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed)); } } bool retVal = true; int spincount = 0; for (; ; ) { //Once an upgrade lock is taken, it's like having a reader lock held //until upgrade or downgrade operations are performed. if ((upgradeLockOwnerId == -1) && (owners < MAX_READER)) { owners++; upgradeLockOwnerId = id; break; } if (spincount < MaxSpinCount) { ExitMyLock(); if (timeout.IsExpired) return false; spincount++; SpinWait(spincount); EnterMyLock(); continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (upgradeEvent == null) // Create the needed event { LazyCreateEvent(ref upgradeEvent, true); continue; // since we left the lock, start over. } //Only one thread with the upgrade lock held can proceed. retVal = WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, timeout); if (!retVal) return false; } if (fIsReentrant) { //The lock may have been dropped getting here, so make a quick check to see whether some other //thread did not grab the entry. if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); lrwc.upgradecount++; } ExitMyLock(); return true; }
private bool TryEnterWriteLockCore(TimeoutTracker timeout) { if(fDisposed) throw new ObjectDisposedException(null); int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; bool upgradingToWrite = false; if (!fIsReentrant) { if (id == writeLockOwnerId) { //Check for AW->AW throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveWriteNotAllowed)); } else if (id == upgradeLockOwnerId) { //AU->AW case is allowed once. upgradingToWrite = true; } EnterMyLock(); lrwc = GetThreadRWCount(true); //Can't acquire write lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed)); } } else { EnterMyLock(); lrwc = GetThreadRWCount(false); if (id == writeLockOwnerId) { lrwc.writercount++; ExitMyLock(); return true; } else if (id == upgradeLockOwnerId) { upgradingToWrite = true; } else if (lrwc.readercount > 0) { //Write locks may not be acquired if only read locks have been //acquired. ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed)); } } int spincount = 0; bool retVal = true; for (; ; ) { if (IsWriterAcquired()) { // Good case, there is no contention, we are basically done SetWriterAcquired(); break; } //Check if there is just one upgrader, and no readers. //Assumption: Only one thread can have the upgrade lock, so the //following check will fail for all other threads that may sneak in //when the upgrading thread is waiting. if (upgradingToWrite) { uint readercount = GetNumReaders(); if (readercount == 1) { //Good case again, there is just one upgrader, and no readers. SetWriterAcquired(); // indicate we have a writer. break; } else if (readercount == 2) { if (lrwc != null) { if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); if (lrwc.readercount > 0) { //This check is needed for EU->ER->EW case, as the owner count will be two. Debug.Assert(fIsReentrant); Debug.Assert(fUpgradeThreadHoldingRead); //Good case again, there is just one upgrader, and no readers. SetWriterAcquired(); // indicate we have a writer. break; } } } } if (spincount < MaxSpinCount) { ExitMyLock(); if (timeout.IsExpired) return false; spincount++; SpinWait(spincount); EnterMyLock(); continue; } if (upgradingToWrite) { if (waitUpgradeEvent == null) // Create the needed event { LazyCreateEvent(ref waitUpgradeEvent, true); continue; // since we left the lock, start over. } Debug.Assert(numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); retVal = WaitOnEvent(waitUpgradeEvent, ref numWriteUpgradeWaiters, timeout); //The lock is not held in case of failure. if (!retVal) return false; } else { // Drat, we need to wait. Mark that we have waiters and wait. if (writeEvent == null) // create the needed event. { LazyCreateEvent(ref writeEvent, true); continue; // since we left the lock, start over. } retVal = WaitOnEvent(writeEvent, ref numWriteWaiters, timeout); //The lock is not held in case of failure. if (!retVal) return false; } } Debug.Assert((owners & WRITER_HELD) > 0); if (fIsReentrant) { if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); lrwc.writercount++; } ExitMyLock(); writeLockOwnerId = id; return true; }
private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) { #if !FEATURE_NETCORE Thread.BeginCriticalRegion(); #endif // !FEATURE_NETCORE bool result = false; try { result = TryEnterUpgradeableReadLockCore(timeout); } finally { #if !FEATURE_NETCORE if (!result) Thread.EndCriticalRegion(); #endif // !FEATURE_NETCORE } return result; }
private bool TryAcquireContended(IntPtr currentThreadId, int millisecondsTimeout) { // // If we already own the lock, just increment the recursion count. // if (_owningThreadId == currentThreadId) { checked { _recursionCount++; } return(true); } // // We've already made one lock attempt at this point, so bail early if the timeout is zero. // if (millisecondsTimeout == 0) { return(false); } int spins = 1; if (s_maxSpinCount == SpinningNotInitialized) { s_maxSpinCount = (Environment.ProcessorCount > 1) ? MaxSpinningValue : SpinningDisabled; } while (true) { // // Try to grab the lock. We may take the lock here even if there are existing waiters. This creates the possibility // of starvation of waiters, but it also prevents lock convoys from destroying perf. // The starvation issue is largely mitigated by the priority boost the OS gives to a waiter when we set // the event, after we release the lock. Eventually waiters will be boosted high enough to preempt this thread. // int oldState = _state; if ((oldState & Locked) == 0 && Interlocked.CompareExchange(ref _state, oldState | Locked, oldState) == oldState) { goto GotTheLock; } // // Back off by a factor of 2 for each attempt, up to MaxSpinCount // if (spins <= s_maxSpinCount) { RuntimeThread.SpinWait(spins); spins *= 2; } else { // // We reached our spin limit, and need to wait. Increment the waiter count. // Note that we do not do any overflow checking on this increment. In order to overflow, // we'd need to have about 1 billion waiting threads, which is inconceivable anytime in the // forseeable future. // int newState = (oldState + WaiterCountIncrement) & ~WaiterWoken; if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { break; } } } // // Now we wait. // TimeoutTracker timeoutTracker = TimeoutTracker.Start(millisecondsTimeout); AutoResetEvent ev = Event; while (true) { Debug.Assert(_state >= WaiterCountIncrement); bool waitSucceeded = ev.WaitOne(timeoutTracker.Remaining); while (true) { int oldState = _state; Debug.Assert(oldState >= WaiterCountIncrement); // Clear the "waiter woken" bit. int newState = oldState & ~WaiterWoken; if ((oldState & Locked) == 0) { // The lock is available, try to get it. newState |= Locked; newState -= WaiterCountIncrement; if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { goto GotTheLock; } } else if (!waitSucceeded) { // The lock is not available, and we timed out. We're not going to wait agin. newState -= WaiterCountIncrement; if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { return(false); } } else { // The lock is not available, and we didn't time out. We're going to wait again. if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { break; } } } } GotTheLock: Debug.Assert((_state | Locked) != 0); Debug.Assert(_owningThreadId == IntPtr.Zero); Debug.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return(true); }
private bool TryEnterReadLockCore(TimeoutTracker timeout) { if(fDisposed) throw new ObjectDisposedException(null); ReaderWriterCount lrwc = null; int id = Thread.CurrentThread.ManagedThreadId; if (!fIsReentrant) { if (id == writeLockOwnerId) { //Check for AW->AR throw new LockRecursionException(SR.GetString(SR.LockRecursionException_ReadAfterWriteNotAllowed)); } EnterMyLock(); lrwc = GetThreadRWCount(false); //Check if the reader lock is already acquired. Note, we could //check the presence of a reader by not allocating rwc (But that //would lead to two lookups in the common case. It's better to keep //a count in the struucture). if (lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveReadNotAllowed)); } else if (id == upgradeLockOwnerId) { //The upgrade lock is already held. //Update the global read counts and exit. lrwc.readercount++; owners++; ExitMyLock(); return true; } } else { EnterMyLock(); lrwc = GetThreadRWCount(false); if (lrwc.readercount > 0) { lrwc.readercount++; ExitMyLock(); return true; } else if (id == upgradeLockOwnerId) { //The upgrade lock is already held. //Update the global read counts and exit. lrwc.readercount++; owners++; ExitMyLock(); fUpgradeThreadHoldingRead = true; return true; } else if (id == writeLockOwnerId) { //The write lock is already held. //Update global read counts here, lrwc.readercount++; owners++; ExitMyLock(); return true; } } bool retVal = true; int spincount = 0; for (; ; ) { // We can enter a read lock if there are only read-locks have been given out // and a writer is not trying to get in. if (owners < MAX_READER) { // Good case, there is no contention, we are basically done owners++; // Indicate we have another reader lrwc.readercount++; break; } if (spincount < MaxSpinCount) { ExitMyLock(); if (timeout.IsExpired) return false; spincount++; SpinWait(spincount); EnterMyLock(); //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. if(IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (readEvent == null) // Create the needed event { LazyCreateEvent(ref readEvent, false); if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); continue; // since we left the lock, start over. } retVal = WaitOnEvent(readEvent, ref numReadWaiters, timeout); if (!retVal) { return false; } if (IsRwHashEntryChanged(lrwc)) lrwc = GetThreadRWCount(false); } ExitMyLock(); return retVal; }
/// <summary> /// Adds new item to the tail of the queue (core method) /// </summary> /// <param name="item">New item</param> /// <param name="timeout">Adding timeout</param> /// <param name="token">Cancellation token</param> /// <returns>Was added sucessufully</returns> protected override bool TryAddCore(T item, int timeout, CancellationToken token) { if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } bool result = false; var timeoutTracker = new TimeoutTracker(timeout); if (_addingMode == LevelingQueueAddingMode.PreferLiveData) { if (_addMonitor.WaiterCount == 0) { result = TryAddFast(item); if (!result && timeout != 0) { result = TryAddSlow(item, timeoutTracker, token); // Use slow path to add to any queue } } else { // Enter slow path if any waiter presented result = TryAddSlow(item, timeoutTracker, token); } if (_isBackgroundTransferingEnabled && !_lowLevelQueue.IsEmpty) { _backgoundTransfererExclusive.AllowBackgroundGate(); // Allow background transfering } } else { if (_isBackgroundTransferingEnabled && timeout != 0 && !_lowLevelQueue.IsEmpty && IsInsideInterval(_lowLevelQueue.Count, 0, 2)) { // Attempt to wait for lowLevelQueue to become empty SpinWait sw = new SpinWait(); while (!sw.NextSpinWillYield && !_lowLevelQueue.IsEmpty) { sw.SpinOnce(); } if (sw.NextSpinWillYield && timeout != 0 && !_lowLevelQueue.IsEmpty) { sw.SpinOnce(); } } if (_isBackgroundTransferingEnabled) { if (_lowLevelQueue.IsEmpty) { // Only in exclusive mode using (var gateGuard = _backgoundTransfererExclusive.EnterMain(Timeout.Infinite, token)) // This should happen fast { TurboContract.Assert(gateGuard.IsAcquired, conditionString: "gateGuard.IsAcquired"); result = _lowLevelQueue.IsEmpty && _highLevelQueue.TryAdd(item, 0, default(CancellationToken)); } } } else { result = _lowLevelQueue.IsEmpty && _highLevelQueue.TryAdd(item, 0, default(CancellationToken)); } if (!result) { bool isLowLevelEmptyBeforeAdd = _lowLevelQueue.IsEmpty; result = _lowLevelQueue.TryAdd(item, timeout, token); // To preserve order we try to add only to the lower queue if (result && !isLowLevelEmptyBeforeAdd && _isBackgroundTransferingEnabled) { _backgoundTransfererExclusive.AllowBackgroundGate(); // Allow background transfering when at least 2 elements in lowLevelQueue } } } if (result) { NotifyItemAdded(); } return(result); }
/// <summary> /// Waits on 'waitEvent' with a timeout /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// </summary> private bool WaitOnEvent( EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout, bool isWriteWaiter) { #if DEBUG Debug.Assert(MyLockHeld); #endif waitEvent.Reset(); numWaiters++; _fNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (_numWriteWaiters == 1) { SetWritersWaiting(); } if (_numWriteUpgradeWaiters == 1) { SetUpgraderWaiting(); } bool waitSuccessful = false; ExitMyLock(); // Do the wait outside of any lock try { waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); } finally { EnterMyLock(); --numWaiters; if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) { _fNoWaiters = true; } if (_numWriteWaiters == 0) { ClearWritersWaiting(); } if (_numWriteUpgradeWaiters == 0) { ClearUpgraderWaiting(); } if (!waitSuccessful) // We may also be about to throw for some reason. Exit myLock. { if (isWriteWaiter && _numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && !_fNoWaiters) { // Write waiters block read waiters from acquiring the lock. Since this was the last write waiter and // there is a read waiter, wake up the appropriate read waiters. ExitAndWakeUpAppropriateReadWaiters(); } else { ExitMyLock(); } } } return(waitSuccessful); }
private bool TryEnterWriteLockCore(TimeoutTracker timeout) { if (_fDisposed) { throw new ObjectDisposedException(null); } int id = Environment.CurrentManagedThreadId; ReaderWriterCount lrwc; bool upgradingToWrite = false; if (!_fIsReentrant) { if (id == _writeLockOwnerId) { //Check for AW->AW throw new LockRecursionException(SR.LockRecursionException_RecursiveWriteNotAllowed); } else if (id == _upgradeLockOwnerId) { //AU->AW case is allowed once. upgradingToWrite = true; } EnterMyLock(); lrwc = GetThreadRWCount(true); //Can't acquire write lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); } } else { EnterMyLock(); lrwc = GetThreadRWCount(false); if (id == _writeLockOwnerId) { lrwc.writercount++; ExitMyLock(); return(true); } else if (id == _upgradeLockOwnerId) { upgradingToWrite = true; } else if (lrwc.readercount > 0) { //Write locks may not be acquired if only read locks have been //acquired. ExitMyLock(); throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); } } int spincount = 0; bool retVal = true; for (; ;) { if (IsWriterAcquired()) { // Good case, there is no contention, we are basically done SetWriterAcquired(); break; } //Check if there is just one upgrader, and no readers. //Assumption: Only one thread can have the upgrade lock, so the //following check will fail for all other threads that may sneak in //when the upgrading thread is waiting. if (upgradingToWrite) { uint readercount = GetNumReaders(); if (readercount == 1) { //Good case again, there is just one upgrader, and no readers. SetWriterAcquired(); // indicate we have a writer. break; } else if (readercount == 2) { if (lrwc != null) { if (IsRwHashEntryChanged(lrwc)) { lrwc = GetThreadRWCount(false); } if (lrwc.readercount > 0) { //This check is needed for EU->ER->EW case, as the owner count will be two. Debug.Assert(_fIsReentrant); Debug.Assert(_fUpgradeThreadHoldingRead); //Good case again, there is just one upgrader, and no readers. SetWriterAcquired(); // indicate we have a writer. break; } } } } if (spincount < MaxSpinCount) { ExitMyLock(); if (timeout.IsExpired) { return(false); } spincount++; SpinWait(spincount); EnterMyLock(); continue; } if (upgradingToWrite) { if (_waitUpgradeEvent == null) // Create the needed event { LazyCreateEvent(ref _waitUpgradeEvent, true); continue; // since we left the lock, start over. } Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout, isWriteWaiter: true); //The lock is not held in case of failure. if (!retVal) { return(false); } } else { // Drat, we need to wait. Mark that we have waiters and wait. if (_writeEvent == null) // create the needed event. { LazyCreateEvent(ref _writeEvent, true); continue; // since we left the lock, start over. } retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout, isWriteWaiter: true); //The lock is not held in case of failure. if (!retVal) { return(false); } } } Debug.Assert((_owners & WRITER_HELD) > 0); if (_fIsReentrant) { if (IsRwHashEntryChanged(lrwc)) { lrwc = GetThreadRWCount(false); } lrwc.writercount++; } ExitMyLock(); _writeLockOwnerId = id; return(true); }
private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) { return(TryEnterUpgradeableReadLockCore(timeout)); }
private bool TryEnterReadLockCore(TimeoutTracker timeout) { if (_fDisposed) { throw new ObjectDisposedException(null); } ReaderWriterCount lrwc = null; int id = Environment.CurrentManagedThreadId; if (!_fIsReentrant) { if (id == _writeLockOwnerId) { //Check for AW->AR throw new LockRecursionException(SR.LockRecursionException_ReadAfterWriteNotAllowed); } EnterMyLock(); lrwc = GetThreadRWCount(false); //Check if the reader lock is already acquired. Note, we could //check the presence of a reader by not allocating rwc (But that //would lead to two lookups in the common case. It's better to keep //a count in the structure). if (lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.LockRecursionException_RecursiveReadNotAllowed); } else if (id == _upgradeLockOwnerId) { //The upgrade lock is already held. //Update the global read counts and exit. lrwc.readercount++; _owners++; ExitMyLock(); return(true); } } else { EnterMyLock(); lrwc = GetThreadRWCount(false); if (lrwc.readercount > 0) { lrwc.readercount++; ExitMyLock(); return(true); } else if (id == _upgradeLockOwnerId) { //The upgrade lock is already held. //Update the global read counts and exit. lrwc.readercount++; _owners++; ExitMyLock(); _fUpgradeThreadHoldingRead = true; return(true); } else if (id == _writeLockOwnerId) { //The write lock is already held. //Update global read counts here, lrwc.readercount++; _owners++; ExitMyLock(); return(true); } } bool retVal = true; int spincount = 0; for (; ;) { // We can enter a read lock if there are only read-locks have been given out // and a writer is not trying to get in. if (_owners < MAX_READER) { // Good case, there is no contention, we are basically done _owners++; // Indicate we have another reader lrwc.readercount++; break; } if (spincount < MaxSpinCount) { ExitMyLock(); if (timeout.IsExpired) { return(false); } spincount++; SpinWait(spincount); EnterMyLock(); //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. if (IsRwHashEntryChanged(lrwc)) { lrwc = GetThreadRWCount(false); } continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (_readEvent == null) // Create the needed event { LazyCreateEvent(ref _readEvent, false); if (IsRwHashEntryChanged(lrwc)) { lrwc = GetThreadRWCount(false); } continue; // since we left the lock, start over. } retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout, isWriteWaiter: false); if (!retVal) { return(false); } if (IsRwHashEntryChanged(lrwc)) { lrwc = GetThreadRWCount(false); } } ExitMyLock(); return(retVal); }
private bool TryEnterWriteLock(TimeoutTracker timeout) { return(TryEnterWriteLockCore(timeout)); }
/// <summary> /// All operations tracked by this instance will need to have /// - the same timeout length timeoutMs /// - the same timeout callback timeoutCallback /// </summary> /// <param name="timeoutCallback"> /// This callback will be called when OperationTimeoutTracker detects operations that exceeded timeouts. /// </param> public OperationTimeoutTracker(int timeoutMs, Action <TOperationToken[]> timeoutCallback) { _timeoutMs = timeoutMs; _timeoutCallback = timeoutCallback; _currentTimeoutTracker = new TimeoutTracker(_timeoutMs, _timeoutCallback); }
private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) { return TryEnterUpgradeableReadLockCore(timeout); }
private bool TryEnterWriteLock(TimeoutTracker timeout) { return TryEnterWriteLockCore(timeout); }
/// <summary> /// Waits on 'waitEvent' with a timeout /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// </summary> private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout) { #if DEBUG Debug.Assert(MyLockHeld); #endif waitEvent.Reset(); numWaiters++; fNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (numWriteWaiters == 1) SetWritersWaiting(); if (numWriteUpgradeWaiters == 1) SetUpgraderWaiting(); bool waitSuccessful = false; ExitMyLock(); // Do the wait outside of any lock try { waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); } finally { EnterMyLock(); --numWaiters; if (numWriteWaiters == 0 && numWriteUpgradeWaiters == 0 && numUpgradeWaiters == 0 && numReadWaiters == 0) fNoWaiters = true; if (numWriteWaiters == 0) ClearWritersWaiting(); if (numWriteUpgradeWaiters == 0) ClearUpgraderWaiting(); if (!waitSuccessful) // We may also be aboutto throw for some reason. Exit myLock. ExitMyLock(); } return waitSuccessful; }
private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout) { if (_fDisposed) { throw new ObjectDisposedException(null); } int id = Environment.CurrentManagedThreadId; ReaderWriterCount lrwc; if (!_fIsReentrant) { if (id == _upgradeLockOwnerId) { //Check for AU->AU throw new LockRecursionException(SR.LockRecursionException_RecursiveUpgradeNotAllowed); } else if (id == _writeLockOwnerId) { //Check for AU->AW throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterWriteNotAllowed); } EnterMyLock(); lrwc = GetThreadRWCount(true); //Can't acquire upgrade lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); } } else { EnterMyLock(); lrwc = GetThreadRWCount(false); if (id == _upgradeLockOwnerId) { lrwc.upgradecount++; ExitMyLock(); return(true); } else if (id == _writeLockOwnerId) { //Write lock is already held, Just update the global state //to show presence of upgrader. Debug.Assert((_owners & WRITER_HELD) > 0); _owners++; _upgradeLockOwnerId = id; lrwc.upgradecount++; if (lrwc.readercount > 0) { _fUpgradeThreadHoldingRead = true; } ExitMyLock(); return(true); } else if (lrwc.readercount > 0) { //Upgrade locks may not be acquired if only read locks have been //acquired. ExitMyLock(); throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); } } bool retVal = true; int spincount = 0; for (; ;) { //Once an upgrade lock is taken, it's like having a reader lock held //until upgrade or downgrade operations are performed. if ((_upgradeLockOwnerId == -1) && (_owners < MAX_READER)) { _owners++; _upgradeLockOwnerId = id; break; } if (spincount < MaxSpinCount) { ExitMyLock(); if (timeout.IsExpired) { return(false); } spincount++; SpinWait(spincount); EnterMyLock(); continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (_upgradeEvent == null) // Create the needed event { LazyCreateEvent(ref _upgradeEvent, true); continue; // since we left the lock, start over. } //Only one thread with the upgrade lock held can proceed. retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout, isWriteWaiter: false); if (!retVal) { return(false); } } if (_fIsReentrant) { //The lock may have been dropped getting here, so make a quick check to see whether some other //thread did not grab the entry. if (IsRwHashEntryChanged(lrwc)) { lrwc = GetThreadRWCount(false); } lrwc.upgradecount++; } ExitMyLock(); return(true); }
/// <summary> /// Waits on 'waitEvent' with a timeout /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// </summary> private bool WaitOnEvent( EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout, bool isWriteWaiter) { #if DEBUG Debug.Assert(MyLockHeld); #endif waitEvent.Reset(); numWaiters++; _fNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (_numWriteWaiters == 1) SetWritersWaiting(); if (_numWriteUpgradeWaiters == 1) SetUpgraderWaiting(); bool waitSuccessful = false; ExitMyLock(); // Do the wait outside of any lock try { waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); } finally { EnterMyLock(); --numWaiters; if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) _fNoWaiters = true; if (_numWriteWaiters == 0) ClearWritersWaiting(); if (_numWriteUpgradeWaiters == 0) ClearUpgraderWaiting(); if (!waitSuccessful) // We may also be about to throw for some reason. Exit myLock. { if (isWriteWaiter && _numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && !_fNoWaiters) { // Write waiters block read waiters from acquiring the lock. Since this was the last write waiter and // there is a read waiter, wake up the appropriate read waiters. ExitAndWakeUpAppropriateReadWaiters(); } else ExitMyLock(); } } return waitSuccessful; }
public static TimeoutTracker Start(int timeout) { TimeoutTracker tracker = new TimeoutTracker(); tracker._timeout = timeout; if (timeout != Timeout.Infinite) tracker._start = Environment.TickCount; return tracker; }