System.Threading.ReaderWriterLock.AcquireReaderLock C# (CSharp) Method

AcquireReaderLock() public method

public AcquireReaderLock ( int millisecondsTimeout ) : void
millisecondsTimeout int
return void
        public void AcquireReaderLock(int millisecondsTimeout)
        {
            if (millisecondsTimeout < -1)
            {
                throw GetInvalidTimeoutException(nameof(millisecondsTimeout));
            }

            ThreadLocalLockEntry threadLocalLockEntry = ThreadLocalLockEntry.GetOrCreateCurrent(_lockID);

            // Check for the fast path
            if (Interlocked.CompareExchange(ref _state, LockStates.Reader, 0) == 0)
            {
                Debug.Assert(threadLocalLockEntry._readerLevel == 0);
            }
            // Check for nested reader
            else if (threadLocalLockEntry._readerLevel > 0)
            {
                Debug.Assert((_state & LockStates.ReadersMask) != 0);

                if (threadLocalLockEntry._readerLevel == MaxAcquireCount)
                {
                    throw new OverflowException(SR.Overflow_UInt16);
                }
                ++threadLocalLockEntry._readerLevel;
                return;
            }
            // Check if the thread already has writer lock
            else if (_writerID == GetCurrentThreadID())
            {
                AcquireWriterLock(millisecondsTimeout);
                Debug.Assert(threadLocalLockEntry.IsFree);
                return;
            }
            else
            {
                int spinCount = 0;
                int currentState = _state;
                do
                {
                    int knownState = currentState;

                    // Reader need not wait if there are only readers and no writer
                    if (knownState < LockStates.ReadersMask ||
                        (
                            (knownState & LockStates.ReaderSignaled) != 0 &&
                            (knownState & LockStates.Writer) == 0 &&
                            (
                                // A waiting reader, after successfully completing the wait, expects that it can become a
                                // reader, so ensure that there is enough room for waiting readers and this potential reader.
                                (
                                    (knownState & LockStates.ReadersMask) +
                                    ((knownState & LockStates.WaitingReadersMask) >> LockStates.WaitingReadersShift)
                                ) <= LockStates.ReadersMask - 2
                            )
                        ))
                    {
                        // Add to readers
                        currentState = Interlocked.CompareExchange(ref _state, knownState + LockStates.Reader, knownState);
                        if (currentState == knownState)
                        {
                            // One more reader
                            break;
                        }
                        continue;
                    }

                    // Check for too many readers or waiting readers, or if signaling is in progress. The check for signaling
                    // prevents new readers from starting to wait for a read lock while the previous set of waiting readers are
                    // being granted their lock. This is necessary to guarantee thread safety for the 'finally' block below.
                    if ((knownState & LockStates.ReadersMask) == LockStates.ReadersMask ||
                        (knownState & LockStates.WaitingReadersMask) == LockStates.WaitingReadersMask ||
                        (knownState & LockStates.CachingEvents) == LockStates.ReaderSignaled)
                    {
                        // Sleep for a while, then update to the latest state and try again
                        int sleepDurationMilliseconds = 100;
                        if ((knownState & LockStates.ReadersMask) == LockStates.ReadersMask ||
                            (knownState & LockStates.WaitingReadersMask) == LockStates.WaitingReadersMask)
                        {
                            sleepDurationMilliseconds = 1000;
                        }
                        Helpers.Sleep(sleepDurationMilliseconds);
                        spinCount = 0;
                        currentState = _state;
                        continue;
                    }

                    ++spinCount;

                    // Check if events are being cached. The purpose of this check is that "caching" events could involve
                    // disposing one or both of {_readerEvent, _writerEvent}. This check prevents the waiting code below from
                    // trying to use these events during this dangerous time, and instead causes the loop to spin until the
                    // caching state is cleared and events can be recreated. See ReleaseEvents() and callers.
                    if ((knownState & LockStates.CachingEvents) == LockStates.CachingEvents)
                    {
                        if (spinCount > DefaultSpinCount)
                        {
                            Helpers.Sleep(1);
                            spinCount = 0;
                        }
                        currentState = _state;
                        continue;
                    }

                    // Check spin count
                    if (spinCount <= DefaultSpinCount)
                    {
                        currentState = _state;
                        continue;
                    }

                    // Add to waiting readers
                    currentState = Interlocked.CompareExchange(ref _state, knownState + LockStates.WaitingReader, knownState);
                    if (currentState != knownState)
                    {
                        continue;
                    }

                    int modifyState = -LockStates.WaitingReader;
                    ManualResetEventSlim readerEvent = null;
                    bool waitSucceeded = false;
                    try
                    {
                        readerEvent = GetOrCreateReaderEvent();
                        waitSucceeded = readerEvent.Wait(millisecondsTimeout);

                        // AcquireReaderLock cannot have reentry via pumping while waiting for readerEvent, so
                        // threadLocalLockEntry's state should not change from underneath us
                        Debug.Assert(threadLocalLockEntry.HasLockID(_lockID));

                        if (waitSucceeded)
                        {
                            // Become a reader
                            Debug.Assert((_state & LockStates.ReaderSignaled) != 0);
                            Debug.Assert((_state & LockStates.ReadersMask) < LockStates.ReadersMask);
                            modifyState += LockStates.Reader;
                        }
                    }
                    finally
                    {
                        // Make the state changes determined above
                        knownState = Interlocked.Add(ref _state, modifyState) - modifyState;

                        if (!waitSucceeded)
                        {
                            // Check for last signaled waiting reader. This is a rare case where the wait timed out, but shortly
                            // afterwards, waiting readers got released, hence the ReaderSignaled bit is set. In that case,
                            // remove the ReaderSignaled bit from the state, acquire a read lock, and release it. While the
                            // ReaderSignaled bit is set, new requests for a write lock must spin or wait to acquire the lock,
                            // so it is safe for this thread to acquire a read lock and call ReleaseReaderLock() as a shortcut
                            // to do the work of releasing other waiters.
                            if ((knownState & LockStates.ReaderSignaled) != 0 &&
                                (knownState & LockStates.WaitingReadersMask) == LockStates.WaitingReader)
                            {
                                if (readerEvent == null)
                                {
                                    readerEvent = _readerEvent;
                                    Debug.Assert(readerEvent != null);
                                }

                                // Ensure the event is signaled before resetting it, since the ReaderSignaled state is set
                                // before the event is set.
                                readerEvent.Wait();
                                Debug.Assert((_state & LockStates.ReadersMask) < LockStates.ReadersMask);

                                // Reset the event and lower reader signaled flag
                                readerEvent.Reset();
                                Interlocked.Add(ref _state, LockStates.Reader - LockStates.ReaderSignaled);

                                // Honor the orginal status
                                ++threadLocalLockEntry._readerLevel;
                                ReleaseReaderLock();
                            }

                            Debug.Assert(threadLocalLockEntry.IsFree);
                        }
                    }

                    if (!waitSucceeded)
                    {
                        throw GetTimeoutException();
                    }

                    // Check for last signaled waiting reader
                    Debug.Assert((knownState & LockStates.ReaderSignaled) != 0);
                    Debug.Assert((knownState & LockStates.ReadersMask) < LockStates.ReadersMask);
                    if ((knownState & LockStates.WaitingReadersMask) == LockStates.WaitingReader)
                    {
                        // Reset the event and the reader signaled flag
                        readerEvent.Reset();
                        Interlocked.Add(ref _state, -LockStates.ReaderSignaled);
                    }

                    break;
                } while (YieldProcessor());
            }

            // Success
            Debug.Assert((_state & LockStates.Writer) == 0);
            Debug.Assert((_state & LockStates.ReadersMask) != 0);
            ++threadLocalLockEntry._readerLevel;
        }

Same methods

ReaderWriterLock::AcquireReaderLock ( System.TimeSpan timeout ) : void

Usage Example

Esempio n. 1
1
        private static void LockOnReaderWriterLock()
        {
            Console.WriteLine("About to lock on the ReaderWriterLock. Debug after seeing \"Signaled to acquire the reader lock.\"");
            ReaderWriterLock rwLock = new ReaderWriterLock();

            ManualResetEvent rEvent = new ManualResetEvent(false);
            ManualResetEvent pEvent = new ManualResetEvent(false);

            ThreadPool.QueueUserWorkItem((object state) =>
            {
                rwLock.AcquireWriterLock(-1);
                Console.WriteLine("Writer lock acquired!");
                rEvent.Set();
                pEvent.WaitOne();
            });

            rEvent.WaitOne();

            Console.WriteLine("Signaled to acquire the reader lock.");

            rwLock.AcquireReaderLock(-1);

            Console.WriteLine("Reader lock acquired");

            pEvent.Set();

            Console.WriteLine("About to end the program. Press any key to exit.");
            Console.ReadKey();
        }
All Usage Examples Of System.Threading.ReaderWriterLock::AcquireReaderLock