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); } } }