private bool TryEnterWriteLockCore(int millisecondsTimeout) { if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (fDisposed) { throw new ObjectDisposedException(null); } int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; bool upgradingToWrite = false; if (!fIsReentrant) { if (id == writeLockOwnerId) { //Check for AW->AW throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveWriteNotAllowed)); } else if (id == upgradeLockOwnerId) { //AU->AW case is allowed once. upgradingToWrite = true; } EnterMyLock(); lrwc = GetThreadRWCount(id, true); //Can't acquire write lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed)); } } else { EnterMyLock(); lrwc = GetThreadRWCount(id, false); if (id == writeLockOwnerId) { lrwc.rc.writercount++; ExitMyLock(); return(true); } else if (id == upgradeLockOwnerId) { upgradingToWrite = true; } else if (lrwc.readercount > 0) { //Write locks may not be acquired if only read locks have been //acquired. ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed)); } } int spincount = 0; bool retVal = true; for (;;) { if (IsWriterAcquired()) { // Good case, there is no contention, we are basically done SetWriterAcquired(); break; } //Check if there is just one upgrader, and no readers. //Assumption: Only one thread can have the upgrade lock, so the //following check will fail for all other threads that may sneak in //when the upgrading thread is waiting. if (upgradingToWrite) { uint readercount = GetNumReaders(); if (readercount == 1) { //Good case again, there is just one upgrader, and no readers. SetWriterAcquired(); // indicate we have a writer. break; } else if (readercount == 2) { if (lrwc != null) { if (IsRwHashEntryChanged(lrwc, id)) { lrwc = GetThreadRWCount(id, false); } if (lrwc.readercount > 0) { //This check is needed for EU->ER->EW case, as the owner count will be two. Debug.Assert(fIsReentrant); Debug.Assert(fUpgradeThreadHoldingRead); //Good case again, there is just one upgrader, and no readers. SetWriterAcquired(); // indicate we have a writer. break; } } } } if (spincount < MaxSpinCount) { ExitMyLock(); if (millisecondsTimeout == 0) { return(false); } spincount++; SpinWait(spincount); EnterMyLock(); continue; } if (upgradingToWrite) { if (waitUpgradeEvent == null) // Create the needed event { LazyCreateEvent(ref waitUpgradeEvent, true); continue; // since we left the lock, start over. } Debug.Assert(numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); retVal = WaitOnEvent(waitUpgradeEvent, ref numWriteUpgradeWaiters, millisecondsTimeout); //The lock is not held in case of failure. if (!retVal) { return(false); } } else { // Drat, we need to wait. Mark that we have waiters and wait. if (writeEvent == null) // create the needed event. { LazyCreateEvent(ref writeEvent, true); continue; // since we left the lock, start over. } retVal = WaitOnEvent(writeEvent, ref numWriteWaiters, millisecondsTimeout); //The lock is not held in case of failure. if (!retVal) { return(false); } } } Debug.Assert((owners & WRITER_HELD) > 0); if (fIsReentrant) { if (IsRwHashEntryChanged(lrwc, id)) { lrwc = GetThreadRWCount(id, false); } lrwc.rc.writercount++; } ExitMyLock(); writeLockOwnerId = id; return(true); }
private bool TryEnterUpgradeableReadLockCore(int millisecondsTimeout) { if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (fDisposed) { throw new ObjectDisposedException(null); } int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; if (!fIsReentrant) { if (id == upgradeLockOwnerId) { //Check for AU->AU throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveUpgradeNotAllowed)); } else if (id == writeLockOwnerId) { //Check for AU->AW throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterWriteNotAllowed)); } EnterMyLock(); lrwc = GetThreadRWCount(id, true); //Can't acquire upgrade lock with reader lock held. if (lrwc != null && lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed)); } } else { EnterMyLock(); lrwc = GetThreadRWCount(id, false); if (id == upgradeLockOwnerId) { lrwc.rc.upgradecount++; ExitMyLock(); return(true); } else if (id == writeLockOwnerId) { //Write lock is already held, Just update the global state //to show presence of upgrader. Debug.Assert((owners & WRITER_HELD) > 0); owners++; upgradeLockOwnerId = id; lrwc.rc.upgradecount++; if (lrwc.readercount > 0) { fUpgradeThreadHoldingRead = true; } ExitMyLock(); return(true); } else if (lrwc.readercount > 0) { //Upgrade locks may not be acquired if only read locks have been //acquired. ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed)); } } bool retVal = true; int spincount = 0; for (;;) { //Once an upgrade lock is taken, it's like having a reader lock held //until upgrade or downgrade operations are performed. if ((upgradeLockOwnerId == -1) && (owners < MAX_READER)) { owners++; upgradeLockOwnerId = id; break; } if (spincount < MaxSpinCount) { ExitMyLock(); if (millisecondsTimeout == 0) { return(false); } spincount++; SpinWait(spincount); EnterMyLock(); continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (upgradeEvent == null) // Create the needed event { LazyCreateEvent(ref upgradeEvent, true); continue; // since we left the lock, start over. } //Only one thread with the upgrade lock held can proceed. retVal = WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, millisecondsTimeout); if (!retVal) { return(false); } } if (fIsReentrant) { //The lock may have been dropped getting here, so make a quick check to see whether some other //thread did not grab the entry. if (IsRwHashEntryChanged(lrwc, id)) { lrwc = GetThreadRWCount(id, false); } lrwc.rc.upgradecount++; } ExitMyLock(); return(true); }
private bool TryEnterReadLockCore(int millisecondsTimeout) { if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (fDisposed) { throw new ObjectDisposedException(null); } ReaderWriterCount lrwc = null; int id = Thread.CurrentThread.ManagedThreadId; if (!fIsReentrant) { if (id == writeLockOwnerId) { //Check for AW->AR throw new LockRecursionException(SR.GetString(SR.LockRecursionException_ReadAfterWriteNotAllowed)); } EnterMyLock(); lrwc = GetThreadRWCount(id, false); //Check if the reader lock is already acquired. Note, we could //check the presence of a reader by not allocating rwc (But that //would lead to two lookups in the common case. It's better to keep //a count in the struucture). if (lrwc.readercount > 0) { ExitMyLock(); throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveReadNotAllowed)); } else if (id == upgradeLockOwnerId) { //The upgrade lock is already held. //Update the global read counts and exit. lrwc.readercount++; owners++; ExitMyLock(); return(true); } } else { EnterMyLock(); lrwc = GetThreadRWCount(id, false); if (lrwc.readercount > 0) { lrwc.readercount++; ExitMyLock(); return(true); } else if (id == upgradeLockOwnerId) { //The upgrade lock is already held. //Update the global read counts and exit. lrwc.readercount++; owners++; ExitMyLock(); fUpgradeThreadHoldingRead = true; return(true); } else if (id == writeLockOwnerId) { //The write lock is already held. //Update global read counts here, lrwc.readercount++; owners++; ExitMyLock(); return(true); } } bool retVal = true; int spincount = 0; for (;;) { // We can enter a read lock if there are only read-locks have been given out // and a writer is not trying to get in. if (owners < MAX_READER) { // Good case, there is no contention, we are basically done owners++; // Indicate we have another reader lrwc.readercount++; break; } if (spincount < MaxSpinCount) { ExitMyLock(); if (millisecondsTimeout == 0) { return(false); } spincount++; SpinWait(spincount); EnterMyLock(); //The per-thread structure may have been recycled as the lock is released, load again. if (IsRwHashEntryChanged(lrwc, id)) { lrwc = GetThreadRWCount(id, false); } continue; } // Drat, we need to wait. Mark that we have waiters and wait. if (readEvent == null) // Create the needed event { LazyCreateEvent(ref readEvent, false); if (IsRwHashEntryChanged(lrwc, id)) { lrwc = GetThreadRWCount(id, false); } continue; // since we left the lock, start over. } retVal = WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout); if (!retVal) { return(false); } if (IsRwHashEntryChanged(lrwc, id)) { lrwc = GetThreadRWCount(id, false); } } ExitMyLock(); return(retVal); }