private void EnterReadLockPostface(WriteLockState existingLockState, bool waitResult)
        {
            lock (this.syncRoot)
            {
                // Check if we need to dispose the semaphore after the write lock state
                // has already been released.
                existingLockState.WaitingReadLocksCount--;
                if (existingLockState.StateIsReleased &&
                    existingLockState.WaitingReadLocksCount == 0)
                {
                    existingLockState.WaitingReadLocksSemaphore.Dispose();
                }

                if (waitResult)
                {
                    // The write lock has already incremented the currentReadLockCount
                    // field, so we can simply return.
                    Debug.Assert(existingLockState.StateIsReleased);
                }
                else if (existingLockState.StateIsReleased)
                {
                    // Need to release the read lock since we do not want to take it
                    // (because a OperationCanceledException might have been thrown).
                    ExitReadLockCore(getLock: false);
                }
            }
        }
        private bool EnterReadLockPreface(out WriteLockState existingWriteLockState)
        {
            existingWriteLockState = null;

            // Increment the read lock count. If the MSB is not set, no write lock is
            // currently held and we can return immediately without the need to lock
            // on syncRoot.
            int readLockResult = Interlocked.Increment(ref this.currentReadLockCount);

            if (readLockResult >= 0)
            {
                return(true);
            }

            // A write lock state might be present, so we need to lock and check that.
            lock (this.syncRoot)
            {
                existingWriteLockState = this.currentWriteLockState;
                if (existingWriteLockState == null)
                {
                    // There was a write lock state but it has already been released,
                    // so we don't need to do anything.
                    return(true);
                }
                else
                {
                    // There is already another write lock, so we need to decrement
                    // the read lock count and then wait until the write lock's
                    // WaitingReadLocksSemaphore is released.
                    ExitReadLockCore(getLock: false);

                    // Ensure that there exists a semaphore on which we can wait.
                    if (existingWriteLockState.WaitingReadLocksSemaphore == null)
                    {
                        existingWriteLockState.WaitingReadLocksSemaphore = new SemaphoreSlim(0);
                    }

                    // Announce that we will wait on the semaphore.
                    existingWriteLockState.WaitingReadLocksCount++;

                    return(false);
                }
            }
        }
        private void ReleaseWriteLockState()
        {
            var writeLockState = this.currentWriteLockState;

            writeLockState.StateIsReleased = true;

            // Clear the MSB on the read lock count.
            Interlocked.Add(ref this.currentReadLockCount, -0x80000000);

            if (writeLockState.WaitingReadLocksSemaphore != null)
            {
                // If there is currently no other task or thread waiting on the semaphore, we can
                // dispose it here. Otherwise, the last waiting task or thread must dispose the
                // semaphore by checking the WriteLockReleased property.
                if (writeLockState.WaitingReadLocksCount == 0)
                {
                    writeLockState.WaitingReadLocksSemaphore.Dispose();
                }
                else
                {
                    // Directly mark the read locks as entered.
                    Interlocked.Add(
                        ref this.currentReadLockCount,
                        writeLockState.WaitingReadLocksCount);
                    // Release the waiting read locks semaphore as often as needed to ensure
                    // all other waiting tasks or threads are released and get the read lock.
                    // The semaphore however will only have been created if there actually was at
                    // least one other task or thread trying to get a read lock.
                    writeLockState.WaitingReadLocksSemaphore.Release(
                        writeLockState.WaitingReadLocksCount);
                }
            }

            // Clear the write lock state.
            this.currentWriteLockState = null;
        }
        private void EnterWriteLockPostface(
            bool writeLockWaitResult,
            out bool waitForReadLocks,
            bool getLock = true)
        {
            waitForReadLocks = false;

            if (getLock)
            {
                Monitor.Enter(this.syncRoot);
            }
            try
            {
                this.currentWaitingWriteLockCount--;

                if (writeLockWaitResult)
                {
                    // If there's already a write lock state from a previous write lock,
                    // we simply use it. Otherwise, create a new one.
                    if (this.currentWriteLockState == null)
                    {
                        this.currentWriteLockState = new WriteLockState();

                        // Set the MSB on the current read lock count, so that other
                        // threads that want to enter the lock know that they need to
                        // wait until the write lock is released.
                        int readLockCount = Interlocked.Add(
                            ref this.currentReadLockCount,
                            -0x80000000) &
                                            0x7FFFFFFF;

                        // Check if the write lock will need to wait for existing read
                        // locks to be released.
                        waitForReadLocks = readLockCount > 0;
                        this.currentWriteLockState.WaitForReadLocks = waitForReadLocks;

                        if (!waitForReadLocks)
                        {
                            this.currentWriteLockState.ReadLockReleaseSemaphoreReleased = true;
                        }
                    }
                    else
                    {
                        waitForReadLocks = this.currentWriteLockState.WaitForReadLocks;
                    }

                    this.currentWriteLockState.StateIsActive = true;
                }
                else if (this.currentWriteLockState?.StateIsActive == false &&
                         this.currentWaitingWriteLockCount == 0)
                {
                    // We were the last write lock and a previous (inactive) write lock
                    // state is still set, so we need to release it.
                    // This could happen e.g. if a write lock downgrades to a read lock
                    // and then the wait on the writeLockSemaphore times out.
                    ReleaseWriteLockState();
                }
            }
            finally
            {
                if (getLock)
                {
                    Monitor.Exit(this.syncRoot);
                }
            }
        }