/// <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);
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        // ============ 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));
        }
Beispiel #5
0
        // =============== 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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        /// <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);
        }
Beispiel #9
0
        /// <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));
            }
        }
Beispiel #10
0
        /// <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;
        }
Beispiel #14
0
        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;
        }
Beispiel #16
0
        /// <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);
        }
Beispiel #17
0
        /// <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);
        }
Beispiel #18
0
        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);
        }
Beispiel #19
0
 private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout)
 {
     return(TryEnterUpgradeableReadLockCore(timeout));
 }
Beispiel #20
0
        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);
        }
Beispiel #21
0
 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);
 }
Beispiel #23
0
 private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout)
 {
     return TryEnterUpgradeableReadLockCore(timeout);
 }
Beispiel #24
0
 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;
        }
Beispiel #26
0
        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);
        }
Beispiel #27
0
        /// <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;
        }
Beispiel #28
0
 public static TimeoutTracker Start(int timeout)
 {
     TimeoutTracker tracker = new TimeoutTracker();
     tracker._timeout = timeout;
     if (timeout != Timeout.Infinite)
         tracker._start = Environment.TickCount;
     return tracker;
 }