Esempio n. 1
0
        public bool TryEnterWriteLock(int millisecondsTimeout)
        {
            var currentThreadState = CurrentThreadState;

            if (CheckState(currentThreadState, millisecondsTimeout, LockState.Write))
            {
                ++currentThreadState.WriterRecursiveCount;
                return(true);
            }

            ++WaitingWriteCount;
            var isUpgradable = (currentThreadState.LockState & LockState.Upgradable) > 0;
            var registered   = false;
            var success      = false;

            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                /* If the code goes there that means we had a read lock beforehand
                 * that need to be suppressed, we also take the opportunity to register
                 * our interest in the write lock to avoid other write wannabe process
                 * coming in the middle
                 */
                if (isUpgradable && _rwLock >= _rwRead)
                {
                    try
                    {
                        // Empty
                    }
                    finally
                    {
                        if (Interlocked.Add(ref _rwLock, _rwWaitUpgrade - _rwRead) >> _rwReadBit == 0)
                        {
                            _readerDoneEvent.Set();
                        }

                        registered = true;
                    }
                }

                var stateCheck   = isUpgradable ? _rwWaitUpgrade + _rwWait : _rwWait;
                var start        = millisecondsTimeout == -1 ? 0 : _stopwatch.ElapsedMilliseconds;
                var registration = isUpgradable ? _rwWaitUpgrade : _rwWait;

                do
                {
                    var state = _rwLock;

                    if (state <= stateCheck)
                    {
                        try
                        {
                            // Empty
                        }
                        finally
                        {
                            var toWrite = state + _rwWrite - (registered ? registration : 0);
                            if (Interlocked.CompareExchange(ref _rwLock, toWrite, state) == state)
                            {
                                _writerDoneEvent.Reset();
                                currentThreadState.LockState ^= LockState.Write;
                                ++currentThreadState.WriterRecursiveCount;
                                --WaitingWriteCount;
                                registered = false;
                                success    = true;
                            }
                        }

                        if (success)
                        {
                            return(true);
                        }
                    }

                    state = _rwLock;

                    // We register our interest in taking the Write lock (if upgradeable it's already done)
                    if (!isUpgradable)
                    {
                        while ((state & _rwWait) == 0)
                        {
                            try
                            {
                                // Empty
                            }
                            finally
                            {
                                registered |= Interlocked.CompareExchange(ref _rwLock, state | _rwWait, state) == state;
                            }

                            if (registered)
                            {
                                break;
                            }

                            state = _rwLock;
                        }
                    }

                    // Before falling to sleep
                    do
                    {
                        if (_rwLock <= stateCheck)
                        {
                            break;
                        }

                        if ((_rwLock & _rwWrite) != 0)
                        {
                            _writerDoneEvent.Wait(ComputeTimeout(millisecondsTimeout, start));
                        }
                        else if (_rwLock >> _rwReadBit > 0)
                        {
                            _readerDoneEvent.Wait(ComputeTimeout(millisecondsTimeout, start));
                        }
                    } while (millisecondsTimeout < 0 || _stopwatch.ElapsedMilliseconds - start < millisecondsTimeout);
                } while (millisecondsTimeout < 0 || _stopwatch.ElapsedMilliseconds - start < millisecondsTimeout);

                --WaitingWriteCount;
            }
            finally
            {
                if (registered)
                {
                    Interlocked.Add(ref _rwLock, isUpgradable ? -_rwWaitUpgrade : -_rwWait);
                }
            }

            return(false);
        }
Esempio n. 2
0
        private bool TryEnterReadLock(int millisecondsTimeout, ref bool success)
        {
            var currentThreadState = CurrentThreadState;

            if (CheckState(currentThreadState, millisecondsTimeout, LockState.Read))
            {
                ++currentThreadState.ReaderRecursiveCount;
                return(true);
            }

            // This is downgrading from upgradable, no need for check since
            // we already have a sort-of read lock that's going to disappear
            // after user calls ExitUpgradeableReadLock.
            // Same idea when recursion is allowed and a write thread wants to
            // go for a Read too.
            if ((currentThreadState.LockState & LockState.Upgradable) > 0 || (!_noRecursion && (currentThreadState.LockState & LockState.Write) > 0))
            {
                RuntimeHelpers.PrepareConstrainedRegions();
                try
                {
                    // Empty
                }
                finally
                {
                    Interlocked.Add(ref _rwLock, _rwRead);
                    currentThreadState.LockState |= LockState.Read;
                    ++currentThreadState.ReaderRecursiveCount;
                    success = true;
                }

                return(true);
            }

            WaitingReadCount++;
            var start = millisecondsTimeout == -1 ? 0 : _stopwatch.ElapsedMilliseconds;

            do
            {
                /* Check if a writer is present (RwWrite) or if there is someone waiting to
                 * acquire a writer lock in the queue (RwWait | RwWaitUpgrade).
                 */
                if ((_rwLock & (_rwWrite | _rwWait | _rwWaitUpgrade)) > 0)
                {
                    _writerDoneEvent.Wait(ComputeTimeout(millisecondsTimeout, start));
                    continue;
                }

                /* Optimistically try to add ourselves to the reader value
                 * if the adding was too late and another writer came in between
                 * we revert the operation.
                 */
                RuntimeHelpers.PrepareConstrainedRegions();
                try
                {
                    // Empty
                }
                finally
                {
                    int add;
                    if (((add = Interlocked.Add(ref _rwLock, _rwRead)) & (_rwWrite | _rwWait | _rwWaitUpgrade)) == 0)
                    {
                        /* If we are the first reader, reset the event to let other threads
                         * sleep correctly if they try to acquire write lock
                         */
                        if (add >> _rwReadBit == 1)
                        {
                            _readerDoneEvent.Reset();
                        }

                        currentThreadState.LockState ^= LockState.Read;
                        ++currentThreadState.ReaderRecursiveCount;
                        --WaitingReadCount;
                        success = true;
                    }
                    else
                    {
                        Interlocked.Add(ref _rwLock, -_rwRead);
                    }
                }

                if (success)
                {
                    return(true);
                }

                _writerDoneEvent.Wait(ComputeTimeout(millisecondsTimeout, start));
            } while (millisecondsTimeout == -1 || _stopwatch.ElapsedMilliseconds - start < millisecondsTimeout);

            --WaitingReadCount;
            return(false);
        }
Esempio n. 3
0
        /// <summary>
        /// Try acquire the lock with long path, this is usually called after the first path in Enter and
        /// TryEnter failed The reason for short path is to make it inline in the run time which improves the
        /// performance. This method assumed that the parameter are validated in Enter or TryEnter method.
        /// </summary>
        /// <param name="millisecondsTimeout">The timeout milliseconds</param>
        /// <param name="lockTaken">The lockTaken param</param>
        private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken)
        {
            // The fast path doesn't throw any exception, so we have to validate the parameters here
            if (lockTaken)
            {
                lockTaken = false;
                throw new ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException);
            }

            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange);
            }

            uint startTime = 0;

            if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0)
            {
                startTime = TimeoutHelper.GetTime();
            }

            if (IsThreadOwnerTrackingEnabled)
            {
                // Slow path for enabled thread tracking mode
                ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken);
                return;
            }

            // then thread tracking is disabled
            // In this case there are three ways to acquire the lock
            // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2
            // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn
            // the late the thread arrives the more it spins and less frequent it check the lock availability
            // Also the spins count is increases each iteration
            // If the spins iterations finished and failed to acquire the lock, go to step 3
            // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1)
            // If the timeout is expired in after step 1, we need to decrement the waiters count before returning

            int observedOwner;
            int turn = int.MaxValue;

            // ***Step 1, take the lock or update the waiters

            // try to acquire the lock directly if possible or update the waiters count
            observedOwner = _owner;
            if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
            {
                if (CompareExchange(ref _owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner)
                {
                    // Acquired lock
                    return;
                }

                if (millisecondsTimeout == 0)
                {
                    // Did not acquire lock in CompareExchange and timeout is 0 so fail fast
                    return;
                }
            }
            else if (millisecondsTimeout == 0)
            {
                // Did not acquire lock as owned and timeout is 0 so fail fast
                return;
            }
            else // failed to acquire the lock, then try to update the waiters. If the waiters count reached the maximum, just break the loop to avoid overflow
            {
                if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS)
                {
                    // This can still overflow, but maybe there will never be that many waiters
                    turn = (Interlocked.Add(ref _owner, 2) & WAITERS_MASK) >> 1;
                }
            }

            // lock acquired failed and waiters updated

            // *** Step 2, Spinning and Yielding
            SpinWait spinner = default;

            if (turn > Environment.ProcessorCount)
            {
                spinner.Count = SpinWait.YieldThreshold;
            }
            while (true)
            {
                spinner.SpinOnce(SLEEP_ONE_FREQUENCY);

                observedOwner = _owner;
                if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
                {
                    int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
                                   observedOwner | 1                     // don't decrement it. just set the lock bit, it is zero because a previous call of Exit(false) which corrupted the waiters
                           : (observedOwner - 2) | 1;                    // otherwise decrement the waiters and set the lock bit
                    Debug.Assert((newOwner & WAITERS_MASK) >= 0);

                    if (CompareExchange(ref _owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
                    {
                        return;
                    }
                }

                if (spinner.Count % TIMEOUT_CHECK_FREQUENCY == 0)
                {
                    // Check the timeout.
                    if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
                    {
                        DecrementWaiters();
                        return;
                    }
                }
            }
        }