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

DowngradeFromWriterLock() public method

public DowngradeFromWriterLock ( LockCookie &lockCookie ) : void
lockCookie LockCookie
return void
        public void DowngradeFromWriterLock(ref LockCookie lockCookie)
        {
            int threadID = GetCurrentThreadID();
            if (_writerID != threadID)
            {
                throw GetNotOwnerException();
            }

            // Validate cookie
            LockCookieFlags flags = lockCookie._flags;
            ushort requestedWriterLevel = lockCookie._writerLevel;
            if ((flags & LockCookieFlags.Invalid) != 0 ||
                lockCookie._threadID != threadID ||
                (
                    // Cannot downgrade to a writer level that is greater than or equal to the current
                    (flags & (LockCookieFlags.OwnedWriter | LockCookieFlags.OwnedNone)) != 0 &&
                    _writerLevel <= requestedWriterLevel
                ))
            {
                throw GetInvalidLockCookieException();
            }

            // Check if the thread was a reader
            if ((flags & LockCookieFlags.OwnedReader) != 0)
            {
                Debug.Assert(_writerLevel > 0);

                ThreadLocalLockEntry threadLocalLockEntry = ThreadLocalLockEntry.GetOrCreateCurrent(_lockID);

                // Downgrade to a reader
                _writerID = InvalidThreadID;
                _writerLevel = 0;
                ManualResetEventSlim readerEvent = null;
                int currentState = _state;
                int knownState;
                do
                {
                    knownState = currentState;
                    int modifyState = LockStates.Reader - LockStates.Writer;
                    if ((knownState & LockStates.WaitingReadersMask) != 0)
                    {
                        readerEvent = TryGetOrCreateReaderEvent();
                        if (readerEvent == null)
                        {
                            // Wait for some time and try again. Since a WaitingReaders bit is set, the event would usually
                            // already be created (if the waiting reader that called AcquireReaderLock is already waiting on the
                            // event, it would have created the event). However, AcquireReaderLock adds WaitingReader to the
                            // state before trying to create the event.
                            //
                            // This is such a situation, where the event has not yet been created, and likely due to the system
                            // being low on resources, this thread failed to create the event. We don't want to throw here,
                            // because it could potentially leave waiters waiting and cause a deadlock.
                            //
                            // Instead, we let the threads that set the WaitingReader bit throw, and here, just wait and try
                            // again. In a low-resource situation, eventually, all such new waiting readers would throw, and the
                            // WaitingReaders bits would not be set anymore, breaking the loop and releasing this thread.
                            Helpers.Sleep(100);
                            currentState = _state;
                            knownState = 0;
                            Debug.Assert(currentState != knownState);
                            continue;
                        }
                        modifyState += LockStates.ReaderSignaled;
                    }

                    Debug.Assert((knownState & LockStates.ReadersMask) == 0);
                    currentState = Interlocked.CompareExchange(ref _state, knownState + modifyState, knownState);
                } while (currentState != knownState);

                // Check for waiting readers
                if ((knownState & LockStates.WaitingReadersMask) != 0)
                {
                    Debug.Assert((_state & LockStates.ReaderSignaled) != 0);
                    Debug.Assert(readerEvent != null);
                    readerEvent.Set();
                }

                // Restore reader nesting level
                threadLocalLockEntry._readerLevel = lockCookie._readerLevel;
            }
            else if ((flags & (LockCookieFlags.OwnedWriter | LockCookieFlags.OwnedNone)) != 0)
            {
                // Original code:
                //     ReleaseWriterLock();
                //     Debug.Assert((flags & LockCookieFlags.OwnedWriter) != 0 || _writerID != threadID);
                //
                // Previously, the lock cookie was ignored on this path. UpgradeToWriterLock allows upgrading from an unlocked
                // state or when the write lock is already held, where it just calls AcquireWriteLock. To compensate, I
                // DowngradeFromWriterLock intends to just behave as ReleaseWriterLock.
                //
                // However, the lock cookie could be several operations old. Consider:
                //   lockCookie = UpgradeToWriterLock()
                //   AcquireWriterLock()
                //   DowngradeFromWriterLock(ref lockCookie)
                //
                // Since the lock cookie indicates that no lock was held at the time of the upgrade, The ReleaseWriterLock in
                // the original code above does not result in releasing all writer locks as requested by the lock cookie and as
                // expected by the assertion. The code should respect the lock cookie (as it does in the case above where the
                // lock cookie indicates that a read lock was held), and restore the writer level appropriately.
                //
                // Similarly, when the lock cookie does indicate that a write lock was held, the downgrade does not restore the
                // write lock recursion level to that indicated by the lock cookie. Consider:
                //   AcquireWriterLock()
                //   lockCookie = UpgradeToWriterLock()
                //   AcquireWriterLock()
                //   DowngradeFromWriterLock(ref lockCookie) // does not restore recursion level of write lock!
                Debug.Assert(_writerLevel > 0);
                Debug.Assert(_writerLevel > requestedWriterLevel);
                if (requestedWriterLevel > 0)
                {
                    _writerLevel = requestedWriterLevel;
                }
                else
                {
                    if (_writerLevel != 1)
                    {
                        _writerLevel = 1;
                    }
                    ReleaseWriterLock();
                }
                Debug.Assert((flags & LockCookieFlags.OwnedWriter) != 0 || _writerID != threadID);
            }

            // Update the validation fields of the cookie
            lockCookie._flags = LockCookieFlags.Invalid;
        }

Usage Example

Esempio n. 1
0
        /// <summary>
        /// Removes a context from the context pool.
        /// </summary>
        /// <param name="context">The context to remove.</param>
        /// <returns>False if the context does not exist, true otherwise.</returns>
        public static bool RemoveContext(string context)
        {
            bool retval = true;

            readWriteLock.AcquireReaderLock(MAX_LOCK_WAIT);
            try
            {
                if (caches.ContainsKey(context))
                {
                    caches[context].Clear();

                    LockCookie lc = readWriteLock.UpgradeToWriterLock(MAX_LOCK_WAIT);
                    try
                    {
                        caches.Remove(context);
                    }
                    finally
                    {
                        readWriteLock.DowngradeFromWriterLock(ref lc);
                    }
                }
                else
                {
                    retval = false;
                }
            }
            finally
            {
                readWriteLock.ReleaseReaderLock();
            }
            return(retval);
        }
All Usage Examples Of System.Threading.ReaderWriterLock::DowngradeFromWriterLock