Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / System / Threading / ReaderWriterLockSlim / ReaderWriterLockSlim.cs / 1305376 / ReaderWriterLockSlim.cs
// optimize using System; // for Basic system types using System.IO; // for File, Path using System.Diagnostics; // for TraceInformation ... using System.Threading; using System.Security.Permissions; namespace System.Threading { public enum LockRecursionPolicy { NoRecursion = 0, SupportsRecursion = 1, } internal class RecursiveCounts { public int writercount; public int upgradecount; } //Ideally ReadCount should be part of recursivecount too. //However,to avoid an extra lookup in the common case (readers only) //we maintain the readercount in the common per-thread structure. internal class ReaderWriterCount { public int threadid; public int readercount; public ReaderWriterCount next; public RecursiveCounts rc; public ReaderWriterCount(bool fIsReentrant) { threadid = -1; if (fIsReentrant) rc = new RecursiveCounts(); } } ////// A reader-writer lock implementation that is intended to be simple, yet very /// efficient. In particular only 1 interlocked operation is taken for any lock /// operation (we use spin locks to achieve this). The spin lock is never held /// for more than a few instructions (in particular, we never call event APIs /// or in fact any non-trivial API while holding the spin lock). /// [HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)] [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] public class ReaderWriterLockSlim : IDisposable { //Specifying if locked can be reacquired recursively. bool fIsReentrant; // Lock specifiation for myLock: This lock protects exactly the local fields associted // instance of ReaderWriterLockSlim. It does NOT protect the memory associted with the // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). int myLock; //The variables controlling spinning behaviior of Mylock(which is a spin-lock) const int LockSpinCycles = 20; const int LockSpinCount = 10; const int LockSleep0Count = 5; // These variables allow use to avoid Setting events (which is expensive) if we don't have to. uint numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent uint numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent uint numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). uint numUpgradeWaiters; //Variable used for quick check when there are no waiters. bool fNoWaiters; int upgradeLockOwnerId; int writeLockOwnerId; // conditions we wait on. EventWaitHandle writeEvent; // threads waiting to aquire a write lock go here. EventWaitHandle readEvent; // threads waiting to aquire a read lock go here (will be released in bulk) EventWaitHandle upgradeEvent; // thread waiting to acquire the upgrade lock EventWaitHandle waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one) ReaderWriterCount[] rwc; bool fUpgradeThreadHoldingRead; //Per thread Hash; private const int hashTableSize = 0xff; private const int MaxSpinCount = 20; //The uint, that contains info like if the writer lock is held, num of //readers etc. uint owners; //Various R/W masks //Note: //The Uint is divided as follows: // //Writer-Owned Waiting-Writers Waiting Upgraders Num-REaders // 31 30 29 28.......0 // //Dividing the uint, allows to vastly simplify logic for checking if a //reader should go in etc. Setting the writer bit, will automatically //make the value of the uint much larger than the max num of readers //allowed, thus causing the check for max_readers to fail. private const uint WRITER_HELD = 0x80000000; private const uint WAITING_WRITERS = 0x40000000; private const uint WAITING_UPGRADER = 0x20000000; //The max readers is actually one less then it's theoretical max. //This is done in order to prevent reader count overflows. If the reader //count reaches max, other readers will wait. private const uint MAX_READER = 0x10000000 - 2; private const uint READER_MASK = 0x10000000 - 1; private bool fDisposed; private void InitializeThreadCounts() { rwc = new ReaderWriterCount[hashTableSize+1]; upgradeLockOwnerId = -1; writeLockOwnerId = -1; } public ReaderWriterLockSlim() : this(LockRecursionPolicy.NoRecursion) { } public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy) { if (recursionPolicy == LockRecursionPolicy.SupportsRecursion) { fIsReentrant = true; } InitializeThreadCounts(); } private static bool IsRWEntryEmpty(ReaderWriterCount rwc) { if (rwc.threadid == -1) return true; else if (rwc.readercount == 0 && rwc.rc == null) return true; else if (rwc.readercount == 0 && rwc.rc.writercount == 0 && rwc.rc.upgradecount == 0) return true; else return false; } private static bool IsRwHashEntryChanged(ReaderWriterCount lrwc, int id) { return lrwc.threadid != id; } ////// This routine retrieves/sets the per-thread counts needed to enforce the /// various rules related to acquiring the lock. It's a simple hash table, /// where the first entry is pre-allocated for optimizing the common case. /// After the first element has been allocated, duplicates are kept of in /// linked-list. The entries are never freed, and the max size of the /// table would be bounded by the max number of threads that held the lock /// simultaneously. /// /// DontAllocate is set to true if the caller just wants to get an existing /// entry for this thread, but doesn't want to add one if an existing one /// could not be found. /// private ReaderWriterCount GetThreadRWCount(int id, bool DontAllocate) { int hash = id & hashTableSize; ReaderWriterCount firstfound = null; #if DEBUG Debug.Assert(MyLockHeld); #endif if (null == rwc[hash]) { if (DontAllocate) return null; else rwc[hash] = new ReaderWriterCount(fIsReentrant); } if (rwc[hash].threadid == id) { return rwc[hash]; } if (IsRWEntryEmpty(rwc[hash]) && !DontAllocate) { //No more entries in chain, so no more searching required. if (rwc[hash].next == null) { rwc[hash].threadid = id; return rwc[hash]; } else firstfound = rwc[hash]; } //SlowPath ReaderWriterCount temp = rwc[hash].next; while (temp != null) { if (temp.threadid == id) { return temp; } if (firstfound == null) { if (IsRWEntryEmpty(temp)) firstfound = temp; } temp = temp.next; } if (DontAllocate) return null; if (firstfound == null) { temp = new ReaderWriterCount(fIsReentrant); temp.threadid = id; temp.next = rwc[hash].next; rwc[hash].next = temp; return temp; } else { firstfound.threadid = id; return firstfound; } } public void EnterReadLock() { TryEnterReadLock(-1); } public bool TryEnterReadLock(TimeSpan timeout) { long ltm = (long)timeout.TotalMilliseconds; if (ltm < -1 || ltm > (long) Int32.MaxValue) throw new ArgumentOutOfRangeException("timeout"); int tm = (int)timeout.TotalMilliseconds; return TryEnterReadLock(tm); } public bool TryEnterReadLock(int millisecondsTimeout) { Thread.BeginCriticalRegion(); bool result = false; try { result = TryEnterReadLockCore(millisecondsTimeout); } finally { if (!result) Thread.EndCriticalRegion(); } return result; } 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; } public void EnterWriteLock() { TryEnterWriteLock(-1); } public bool TryEnterWriteLock(TimeSpan timeout) { long ltm = (long)timeout.TotalMilliseconds; if (ltm < -1 || ltm > (long) Int32.MaxValue) throw new ArgumentOutOfRangeException("timeout"); int tm = (int)timeout.TotalMilliseconds; return TryEnterWriteLock(tm); } public bool TryEnterWriteLock(int millisecondsTimeout) { Thread.BeginCriticalRegion(); bool result = false; try { result = TryEnterWriteLockCore(millisecondsTimeout); } finally { if (!result) Thread.EndCriticalRegion(); } return result; } 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; } public void EnterUpgradeableReadLock() { TryEnterUpgradeableReadLock(-1); } public bool TryEnterUpgradeableReadLock(TimeSpan timeout) { long ltm = (long)timeout.TotalMilliseconds; if (ltm < -1 || ltm > (long) Int32.MaxValue) throw new ArgumentOutOfRangeException("timeout"); int tm = (int)timeout.TotalMilliseconds; return TryEnterUpgradeableReadLock(tm); } public bool TryEnterUpgradeableReadLock(int millisecondsTimeout) { Thread.BeginCriticalRegion(); bool result = false; try { result = TryEnterUpgradeableReadLockCore(millisecondsTimeout); } finally { if (!result) Thread.EndCriticalRegion(); } return result; } 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; } public void ExitReadLock() { int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc = null; EnterMyLock(); lrwc = GetThreadRWCount(id, true); if (!fIsReentrant) { if (lrwc == null) { //You have to be holding the read lock to make this call. ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedRead)); } } else { if (lrwc == null || lrwc.readercount < 1) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedRead)); } if (lrwc.readercount > 1) { lrwc.readercount--; ExitMyLock(); Thread.EndCriticalRegion(); return; } if (id == upgradeLockOwnerId) { fUpgradeThreadHoldingRead = false; } } Debug.Assert(owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken"); --owners; Debug.Assert(lrwc.readercount == 1); lrwc.readercount--; ExitAndWakeUpAppropriateWaiters(); Thread.EndCriticalRegion(); } public void ExitWriteLock() { int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; if (!fIsReentrant) { if (id != writeLockOwnerId) { //You have to be holding the write lock to make this call. throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite)); } EnterMyLock(); } else { EnterMyLock(); lrwc = GetThreadRWCount(id, false); if (lrwc == null) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite)); } RecursiveCounts rc = lrwc.rc; if (rc.writercount < 1) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite)); } rc.writercount--; if (rc.writercount > 0) { ExitMyLock(); Thread.EndCriticalRegion(); return; } } Debug.Assert((owners & WRITER_HELD) > 0, "Calling ReleaseWriterLock when no write lock is held"); ClearWriterAcquired(); writeLockOwnerId = -1; ExitAndWakeUpAppropriateWaiters(); Thread.EndCriticalRegion(); } public void ExitUpgradeableReadLock() { int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; if (!fIsReentrant) { if (id != upgradeLockOwnerId) { //You have to be holding the upgrade lock to make this call. throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade)); } EnterMyLock(); } else { EnterMyLock(); lrwc = GetThreadRWCount(id, true); if (lrwc == null) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade)); } RecursiveCounts rc = lrwc.rc; if (rc.upgradecount < 1) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade)); } rc.upgradecount--; if (rc.upgradecount > 0) { ExitMyLock(); Thread.EndCriticalRegion(); return; } fUpgradeThreadHoldingRead = false; } owners--; upgradeLockOwnerId = -1; ExitAndWakeUpAppropriateWaiters(); Thread.EndCriticalRegion(); } ////// A routine for lazily creating a event outside the lock (so if errors /// happen they are outside the lock and that we don't do much work /// while holding a spin lock). If all goes well, reenter the lock and /// set 'waitEvent' /// private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) { #if DEBUG Debug.Assert(MyLockHeld); Debug.Assert(waitEvent == null); #endif ExitMyLock(); EventWaitHandle newEvent; if (makeAutoResetEvent) newEvent = new AutoResetEvent(false); else newEvent = new ManualResetEvent(false); EnterMyLock(); if (waitEvent == null) // maybe someone snuck in. waitEvent = newEvent; else newEvent.Close(); } ////// Waits on 'waitEvent' with a timeout of 'millisceondsTimeout. /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout) { #if DEBUG Debug.Assert(MyLockHeld); #endif waitEvent.Reset(); numWaiters++; fNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (numWriteWaiters == 1) SetWritersWaiting(); if (numWriteUpgradeWaiters == 1) SetUpgraderWaiting(); bool waitSuccessful = false; ExitMyLock(); // Do the wait outside of any lock try { waitSuccessful = waitEvent.WaitOne(millisecondsTimeout, false); } finally { EnterMyLock(); --numWaiters; if (numWriteWaiters == 0 && numWriteUpgradeWaiters == 0 && numUpgradeWaiters == 0 && numReadWaiters == 0) fNoWaiters = true; if (numWriteWaiters == 0) ClearWritersWaiting(); if (numWriteUpgradeWaiters == 0) ClearUpgraderWaiting(); if (!waitSuccessful) // We may also be aboutto throw for some reason. Exit myLock. ExitMyLock(); } return waitSuccessful; } ////// Determines the appropriate events to set, leaves the locks, and sets the events. /// private void ExitAndWakeUpAppropriateWaiters() { #if DEBUG Debug.Assert(MyLockHeld); #endif if (fNoWaiters) { ExitMyLock(); return; } ExitAndWakeUpAppropriateWaitersPreferringWriters(); } private void ExitAndWakeUpAppropriateWaitersPreferringWriters() { bool setUpgradeEvent = false; bool setReadEvent = false; uint readercount = GetNumReaders(); //We need this case for EU->ER->EW case, as the read count will be 2 in //that scenario. if (fIsReentrant) { if (numWriteUpgradeWaiters > 0 && fUpgradeThreadHoldingRead && readercount == 2) { ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). return; } } if (readercount == 1 && numWriteUpgradeWaiters > 0) { //We have to be careful now, as we are droppping the lock. //No new writes should be allowed to sneak in if an upgrade //was pending. ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). } else if (readercount == 0 && numWriteWaiters > 0) { ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) writeEvent.Set(); // release one writer. } else if (readercount >= 0) { if (numReadWaiters != 0 || numUpgradeWaiters != 0) { if (numReadWaiters != 0) setReadEvent = true; if (numUpgradeWaiters != 0 && upgradeLockOwnerId == -1) { setUpgradeEvent = true; } ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) if (setReadEvent) readEvent.Set(); // release all readers. if (setUpgradeEvent) upgradeEvent.Set(); //release one upgrader. } else ExitMyLock(); } else ExitMyLock(); } private bool IsWriterAcquired() { return (owners & ~WAITING_WRITERS) == 0; } private void SetWriterAcquired() { owners |= WRITER_HELD; // indicate we have a writer. } private void ClearWriterAcquired() { owners &= ~WRITER_HELD; } private void SetWritersWaiting() { owners |= WAITING_WRITERS; } private void ClearWritersWaiting() { owners &= ~WAITING_WRITERS; } private void SetUpgraderWaiting() { owners |= WAITING_UPGRADER; } private void ClearUpgraderWaiting() { owners &= ~WAITING_UPGRADER; } private uint GetNumReaders() { return owners & READER_MASK; } private void EnterMyLock() { if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0) EnterMyLockSpin(); } private void EnterMyLockSpin() { int pc = Environment.ProcessorCount; for (int i = 0; ; i++) { if (i < LockSpinCount && pc > 1) { Thread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock. } else if (i < (LockSpinCount + LockSleep0Count)) { Thread.Sleep(0); // Give up my quantum. } else { Thread.Sleep(1); // Give up my quantum. } if (myLock == 0 && Interlocked.CompareExchange(ref myLock, 1, 0) == 0) return; } } private void ExitMyLock() { Debug.Assert(myLock != 0, "Exiting spin lock that is not held"); myLock = 0; } #if DEBUG private bool MyLockHeld { get { return myLock != 0; } } #endif private static void SpinWait(int SpinCount) { //Exponential backoff if ((SpinCount < 5) && (Environment.ProcessorCount > 1)) { Thread.SpinWait(LockSpinCycles * SpinCount); } else if (SpinCount < MaxSpinCount - 3) { Thread.Sleep(0); } else { Thread.Sleep(1); } } public void Dispose() { Dispose(true); } private void Dispose(bool disposing) { if(disposing) { if(fDisposed) throw new ObjectDisposedException(null); if(WaitingReadCount>0 || WaitingUpgradeCount > 0 || WaitingWriteCount > 0) throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_IncorrectDispose)); if(IsReadLockHeld || IsUpgradeableReadLockHeld || IsWriteLockHeld) throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_IncorrectDispose)); if (writeEvent != null) { writeEvent.Close(); writeEvent = null; } if (readEvent != null) { readEvent.Close(); readEvent = null; } if (upgradeEvent != null) { upgradeEvent.Close(); upgradeEvent = null; } if (waitUpgradeEvent != null) { waitUpgradeEvent.Close(); waitUpgradeEvent = null; } fDisposed = true; } } public bool IsReadLockHeld { get { if (RecursiveReadCount > 0) return true; else return false; } } public bool IsUpgradeableReadLockHeld { get { if (RecursiveUpgradeCount > 0) return true; else return false; } } public bool IsWriteLockHeld { get { if (RecursiveWriteCount > 0) return true; else return false; } } public LockRecursionPolicy RecursionPolicy { get { if (fIsReentrant) { return LockRecursionPolicy.SupportsRecursion; } else { return LockRecursionPolicy.NoRecursion; } } } public int CurrentReadCount { get { int numreaders = (int)GetNumReaders(); if (upgradeLockOwnerId != -1) return numreaders - 1; else return numreaders; } } public int RecursiveReadCount { get { ReaderWriterCount lrwc; int id = Thread.CurrentThread.ManagedThreadId; int count = 0; Thread.BeginCriticalRegion(); EnterMyLock(); lrwc = GetThreadRWCount(id, true); if(lrwc != null) count = lrwc.readercount; ExitMyLock(); Thread.EndCriticalRegion(); return count; } } public int RecursiveUpgradeCount { get { int id = Thread.CurrentThread.ManagedThreadId; if (fIsReentrant) { ReaderWriterCount lrwc; int count = 0; Thread.BeginCriticalRegion(); EnterMyLock(); lrwc = GetThreadRWCount(id, true); if(lrwc != null) count = lrwc.rc.upgradecount; ExitMyLock(); Thread.EndCriticalRegion(); return count; } else { if (id == upgradeLockOwnerId) return 1; else return 0; } } } public int RecursiveWriteCount { get { int id = Thread.CurrentThread.ManagedThreadId; int count = 0; if (fIsReentrant) { ReaderWriterCount lrwc; Thread.BeginCriticalRegion(); EnterMyLock(); lrwc = GetThreadRWCount(id, true); if(lrwc != null) count = lrwc.rc.writercount; ExitMyLock(); Thread.EndCriticalRegion(); return count; } else { if (id == writeLockOwnerId) return 1; else return 0; } } } public int WaitingReadCount { get { return (int)numReadWaiters; } } public int WaitingUpgradeCount { get { return (int)numUpgradeWaiters; } } public int WaitingWriteCount { get { return (int)numWriteWaiters; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. // optimize using System; // for Basic system types using System.IO; // for File, Path using System.Diagnostics; // for TraceInformation ... using System.Threading; using System.Security.Permissions; namespace System.Threading { public enum LockRecursionPolicy { NoRecursion = 0, SupportsRecursion = 1, } internal class RecursiveCounts { public int writercount; public int upgradecount; } //Ideally ReadCount should be part of recursivecount too. //However,to avoid an extra lookup in the common case (readers only) //we maintain the readercount in the common per-thread structure. internal class ReaderWriterCount { public int threadid; public int readercount; public ReaderWriterCount next; public RecursiveCounts rc; public ReaderWriterCount(bool fIsReentrant) { threadid = -1; if (fIsReentrant) rc = new RecursiveCounts(); } } ////// A reader-writer lock implementation that is intended to be simple, yet very /// efficient. In particular only 1 interlocked operation is taken for any lock /// operation (we use spin locks to achieve this). The spin lock is never held /// for more than a few instructions (in particular, we never call event APIs /// or in fact any non-trivial API while holding the spin lock). /// [HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)] [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] public class ReaderWriterLockSlim : IDisposable { //Specifying if locked can be reacquired recursively. bool fIsReentrant; // Lock specifiation for myLock: This lock protects exactly the local fields associted // instance of ReaderWriterLockSlim. It does NOT protect the memory associted with the // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). int myLock; //The variables controlling spinning behaviior of Mylock(which is a spin-lock) const int LockSpinCycles = 20; const int LockSpinCount = 10; const int LockSleep0Count = 5; // These variables allow use to avoid Setting events (which is expensive) if we don't have to. uint numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent uint numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent uint numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). uint numUpgradeWaiters; //Variable used for quick check when there are no waiters. bool fNoWaiters; int upgradeLockOwnerId; int writeLockOwnerId; // conditions we wait on. EventWaitHandle writeEvent; // threads waiting to aquire a write lock go here. EventWaitHandle readEvent; // threads waiting to aquire a read lock go here (will be released in bulk) EventWaitHandle upgradeEvent; // thread waiting to acquire the upgrade lock EventWaitHandle waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one) ReaderWriterCount[] rwc; bool fUpgradeThreadHoldingRead; //Per thread Hash; private const int hashTableSize = 0xff; private const int MaxSpinCount = 20; //The uint, that contains info like if the writer lock is held, num of //readers etc. uint owners; //Various R/W masks //Note: //The Uint is divided as follows: // //Writer-Owned Waiting-Writers Waiting Upgraders Num-REaders // 31 30 29 28.......0 // //Dividing the uint, allows to vastly simplify logic for checking if a //reader should go in etc. Setting the writer bit, will automatically //make the value of the uint much larger than the max num of readers //allowed, thus causing the check for max_readers to fail. private const uint WRITER_HELD = 0x80000000; private const uint WAITING_WRITERS = 0x40000000; private const uint WAITING_UPGRADER = 0x20000000; //The max readers is actually one less then it's theoretical max. //This is done in order to prevent reader count overflows. If the reader //count reaches max, other readers will wait. private const uint MAX_READER = 0x10000000 - 2; private const uint READER_MASK = 0x10000000 - 1; private bool fDisposed; private void InitializeThreadCounts() { rwc = new ReaderWriterCount[hashTableSize+1]; upgradeLockOwnerId = -1; writeLockOwnerId = -1; } public ReaderWriterLockSlim() : this(LockRecursionPolicy.NoRecursion) { } public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy) { if (recursionPolicy == LockRecursionPolicy.SupportsRecursion) { fIsReentrant = true; } InitializeThreadCounts(); } private static bool IsRWEntryEmpty(ReaderWriterCount rwc) { if (rwc.threadid == -1) return true; else if (rwc.readercount == 0 && rwc.rc == null) return true; else if (rwc.readercount == 0 && rwc.rc.writercount == 0 && rwc.rc.upgradecount == 0) return true; else return false; } private static bool IsRwHashEntryChanged(ReaderWriterCount lrwc, int id) { return lrwc.threadid != id; } ////// This routine retrieves/sets the per-thread counts needed to enforce the /// various rules related to acquiring the lock. It's a simple hash table, /// where the first entry is pre-allocated for optimizing the common case. /// After the first element has been allocated, duplicates are kept of in /// linked-list. The entries are never freed, and the max size of the /// table would be bounded by the max number of threads that held the lock /// simultaneously. /// /// DontAllocate is set to true if the caller just wants to get an existing /// entry for this thread, but doesn't want to add one if an existing one /// could not be found. /// private ReaderWriterCount GetThreadRWCount(int id, bool DontAllocate) { int hash = id & hashTableSize; ReaderWriterCount firstfound = null; #if DEBUG Debug.Assert(MyLockHeld); #endif if (null == rwc[hash]) { if (DontAllocate) return null; else rwc[hash] = new ReaderWriterCount(fIsReentrant); } if (rwc[hash].threadid == id) { return rwc[hash]; } if (IsRWEntryEmpty(rwc[hash]) && !DontAllocate) { //No more entries in chain, so no more searching required. if (rwc[hash].next == null) { rwc[hash].threadid = id; return rwc[hash]; } else firstfound = rwc[hash]; } //SlowPath ReaderWriterCount temp = rwc[hash].next; while (temp != null) { if (temp.threadid == id) { return temp; } if (firstfound == null) { if (IsRWEntryEmpty(temp)) firstfound = temp; } temp = temp.next; } if (DontAllocate) return null; if (firstfound == null) { temp = new ReaderWriterCount(fIsReentrant); temp.threadid = id; temp.next = rwc[hash].next; rwc[hash].next = temp; return temp; } else { firstfound.threadid = id; return firstfound; } } public void EnterReadLock() { TryEnterReadLock(-1); } public bool TryEnterReadLock(TimeSpan timeout) { long ltm = (long)timeout.TotalMilliseconds; if (ltm < -1 || ltm > (long) Int32.MaxValue) throw new ArgumentOutOfRangeException("timeout"); int tm = (int)timeout.TotalMilliseconds; return TryEnterReadLock(tm); } public bool TryEnterReadLock(int millisecondsTimeout) { Thread.BeginCriticalRegion(); bool result = false; try { result = TryEnterReadLockCore(millisecondsTimeout); } finally { if (!result) Thread.EndCriticalRegion(); } return result; } 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; } public void EnterWriteLock() { TryEnterWriteLock(-1); } public bool TryEnterWriteLock(TimeSpan timeout) { long ltm = (long)timeout.TotalMilliseconds; if (ltm < -1 || ltm > (long) Int32.MaxValue) throw new ArgumentOutOfRangeException("timeout"); int tm = (int)timeout.TotalMilliseconds; return TryEnterWriteLock(tm); } public bool TryEnterWriteLock(int millisecondsTimeout) { Thread.BeginCriticalRegion(); bool result = false; try { result = TryEnterWriteLockCore(millisecondsTimeout); } finally { if (!result) Thread.EndCriticalRegion(); } return result; } 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; } public void EnterUpgradeableReadLock() { TryEnterUpgradeableReadLock(-1); } public bool TryEnterUpgradeableReadLock(TimeSpan timeout) { long ltm = (long)timeout.TotalMilliseconds; if (ltm < -1 || ltm > (long) Int32.MaxValue) throw new ArgumentOutOfRangeException("timeout"); int tm = (int)timeout.TotalMilliseconds; return TryEnterUpgradeableReadLock(tm); } public bool TryEnterUpgradeableReadLock(int millisecondsTimeout) { Thread.BeginCriticalRegion(); bool result = false; try { result = TryEnterUpgradeableReadLockCore(millisecondsTimeout); } finally { if (!result) Thread.EndCriticalRegion(); } return result; } 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; } public void ExitReadLock() { int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc = null; EnterMyLock(); lrwc = GetThreadRWCount(id, true); if (!fIsReentrant) { if (lrwc == null) { //You have to be holding the read lock to make this call. ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedRead)); } } else { if (lrwc == null || lrwc.readercount < 1) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedRead)); } if (lrwc.readercount > 1) { lrwc.readercount--; ExitMyLock(); Thread.EndCriticalRegion(); return; } if (id == upgradeLockOwnerId) { fUpgradeThreadHoldingRead = false; } } Debug.Assert(owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken"); --owners; Debug.Assert(lrwc.readercount == 1); lrwc.readercount--; ExitAndWakeUpAppropriateWaiters(); Thread.EndCriticalRegion(); } public void ExitWriteLock() { int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; if (!fIsReentrant) { if (id != writeLockOwnerId) { //You have to be holding the write lock to make this call. throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite)); } EnterMyLock(); } else { EnterMyLock(); lrwc = GetThreadRWCount(id, false); if (lrwc == null) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite)); } RecursiveCounts rc = lrwc.rc; if (rc.writercount < 1) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite)); } rc.writercount--; if (rc.writercount > 0) { ExitMyLock(); Thread.EndCriticalRegion(); return; } } Debug.Assert((owners & WRITER_HELD) > 0, "Calling ReleaseWriterLock when no write lock is held"); ClearWriterAcquired(); writeLockOwnerId = -1; ExitAndWakeUpAppropriateWaiters(); Thread.EndCriticalRegion(); } public void ExitUpgradeableReadLock() { int id = Thread.CurrentThread.ManagedThreadId; ReaderWriterCount lrwc; if (!fIsReentrant) { if (id != upgradeLockOwnerId) { //You have to be holding the upgrade lock to make this call. throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade)); } EnterMyLock(); } else { EnterMyLock(); lrwc = GetThreadRWCount(id, true); if (lrwc == null) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade)); } RecursiveCounts rc = lrwc.rc; if (rc.upgradecount < 1) { ExitMyLock(); throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade)); } rc.upgradecount--; if (rc.upgradecount > 0) { ExitMyLock(); Thread.EndCriticalRegion(); return; } fUpgradeThreadHoldingRead = false; } owners--; upgradeLockOwnerId = -1; ExitAndWakeUpAppropriateWaiters(); Thread.EndCriticalRegion(); } ////// A routine for lazily creating a event outside the lock (so if errors /// happen they are outside the lock and that we don't do much work /// while holding a spin lock). If all goes well, reenter the lock and /// set 'waitEvent' /// private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) { #if DEBUG Debug.Assert(MyLockHeld); Debug.Assert(waitEvent == null); #endif ExitMyLock(); EventWaitHandle newEvent; if (makeAutoResetEvent) newEvent = new AutoResetEvent(false); else newEvent = new ManualResetEvent(false); EnterMyLock(); if (waitEvent == null) // maybe someone snuck in. waitEvent = newEvent; else newEvent.Close(); } ////// Waits on 'waitEvent' with a timeout of 'millisceondsTimeout. /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. /// private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout) { #if DEBUG Debug.Assert(MyLockHeld); #endif waitEvent.Reset(); numWaiters++; fNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (numWriteWaiters == 1) SetWritersWaiting(); if (numWriteUpgradeWaiters == 1) SetUpgraderWaiting(); bool waitSuccessful = false; ExitMyLock(); // Do the wait outside of any lock try { waitSuccessful = waitEvent.WaitOne(millisecondsTimeout, false); } finally { EnterMyLock(); --numWaiters; if (numWriteWaiters == 0 && numWriteUpgradeWaiters == 0 && numUpgradeWaiters == 0 && numReadWaiters == 0) fNoWaiters = true; if (numWriteWaiters == 0) ClearWritersWaiting(); if (numWriteUpgradeWaiters == 0) ClearUpgraderWaiting(); if (!waitSuccessful) // We may also be aboutto throw for some reason. Exit myLock. ExitMyLock(); } return waitSuccessful; } ////// Determines the appropriate events to set, leaves the locks, and sets the events. /// private void ExitAndWakeUpAppropriateWaiters() { #if DEBUG Debug.Assert(MyLockHeld); #endif if (fNoWaiters) { ExitMyLock(); return; } ExitAndWakeUpAppropriateWaitersPreferringWriters(); } private void ExitAndWakeUpAppropriateWaitersPreferringWriters() { bool setUpgradeEvent = false; bool setReadEvent = false; uint readercount = GetNumReaders(); //We need this case for EU->ER->EW case, as the read count will be 2 in //that scenario. if (fIsReentrant) { if (numWriteUpgradeWaiters > 0 && fUpgradeThreadHoldingRead && readercount == 2) { ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). return; } } if (readercount == 1 && numWriteUpgradeWaiters > 0) { //We have to be careful now, as we are droppping the lock. //No new writes should be allowed to sneak in if an upgrade //was pending. ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). } else if (readercount == 0 && numWriteWaiters > 0) { ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) writeEvent.Set(); // release one writer. } else if (readercount >= 0) { if (numReadWaiters != 0 || numUpgradeWaiters != 0) { if (numReadWaiters != 0) setReadEvent = true; if (numUpgradeWaiters != 0 && upgradeLockOwnerId == -1) { setUpgradeEvent = true; } ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) if (setReadEvent) readEvent.Set(); // release all readers. if (setUpgradeEvent) upgradeEvent.Set(); //release one upgrader. } else ExitMyLock(); } else ExitMyLock(); } private bool IsWriterAcquired() { return (owners & ~WAITING_WRITERS) == 0; } private void SetWriterAcquired() { owners |= WRITER_HELD; // indicate we have a writer. } private void ClearWriterAcquired() { owners &= ~WRITER_HELD; } private void SetWritersWaiting() { owners |= WAITING_WRITERS; } private void ClearWritersWaiting() { owners &= ~WAITING_WRITERS; } private void SetUpgraderWaiting() { owners |= WAITING_UPGRADER; } private void ClearUpgraderWaiting() { owners &= ~WAITING_UPGRADER; } private uint GetNumReaders() { return owners & READER_MASK; } private void EnterMyLock() { if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0) EnterMyLockSpin(); } private void EnterMyLockSpin() { int pc = Environment.ProcessorCount; for (int i = 0; ; i++) { if (i < LockSpinCount && pc > 1) { Thread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock. } else if (i < (LockSpinCount + LockSleep0Count)) { Thread.Sleep(0); // Give up my quantum. } else { Thread.Sleep(1); // Give up my quantum. } if (myLock == 0 && Interlocked.CompareExchange(ref myLock, 1, 0) == 0) return; } } private void ExitMyLock() { Debug.Assert(myLock != 0, "Exiting spin lock that is not held"); myLock = 0; } #if DEBUG private bool MyLockHeld { get { return myLock != 0; } } #endif private static void SpinWait(int SpinCount) { //Exponential backoff if ((SpinCount < 5) && (Environment.ProcessorCount > 1)) { Thread.SpinWait(LockSpinCycles * SpinCount); } else if (SpinCount < MaxSpinCount - 3) { Thread.Sleep(0); } else { Thread.Sleep(1); } } public void Dispose() { Dispose(true); } private void Dispose(bool disposing) { if(disposing) { if(fDisposed) throw new ObjectDisposedException(null); if(WaitingReadCount>0 || WaitingUpgradeCount > 0 || WaitingWriteCount > 0) throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_IncorrectDispose)); if(IsReadLockHeld || IsUpgradeableReadLockHeld || IsWriteLockHeld) throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_IncorrectDispose)); if (writeEvent != null) { writeEvent.Close(); writeEvent = null; } if (readEvent != null) { readEvent.Close(); readEvent = null; } if (upgradeEvent != null) { upgradeEvent.Close(); upgradeEvent = null; } if (waitUpgradeEvent != null) { waitUpgradeEvent.Close(); waitUpgradeEvent = null; } fDisposed = true; } } public bool IsReadLockHeld { get { if (RecursiveReadCount > 0) return true; else return false; } } public bool IsUpgradeableReadLockHeld { get { if (RecursiveUpgradeCount > 0) return true; else return false; } } public bool IsWriteLockHeld { get { if (RecursiveWriteCount > 0) return true; else return false; } } public LockRecursionPolicy RecursionPolicy { get { if (fIsReentrant) { return LockRecursionPolicy.SupportsRecursion; } else { return LockRecursionPolicy.NoRecursion; } } } public int CurrentReadCount { get { int numreaders = (int)GetNumReaders(); if (upgradeLockOwnerId != -1) return numreaders - 1; else return numreaders; } } public int RecursiveReadCount { get { ReaderWriterCount lrwc; int id = Thread.CurrentThread.ManagedThreadId; int count = 0; Thread.BeginCriticalRegion(); EnterMyLock(); lrwc = GetThreadRWCount(id, true); if(lrwc != null) count = lrwc.readercount; ExitMyLock(); Thread.EndCriticalRegion(); return count; } } public int RecursiveUpgradeCount { get { int id = Thread.CurrentThread.ManagedThreadId; if (fIsReentrant) { ReaderWriterCount lrwc; int count = 0; Thread.BeginCriticalRegion(); EnterMyLock(); lrwc = GetThreadRWCount(id, true); if(lrwc != null) count = lrwc.rc.upgradecount; ExitMyLock(); Thread.EndCriticalRegion(); return count; } else { if (id == upgradeLockOwnerId) return 1; else return 0; } } } public int RecursiveWriteCount { get { int id = Thread.CurrentThread.ManagedThreadId; int count = 0; if (fIsReentrant) { ReaderWriterCount lrwc; Thread.BeginCriticalRegion(); EnterMyLock(); lrwc = GetThreadRWCount(id, true); if(lrwc != null) count = lrwc.rc.writercount; ExitMyLock(); Thread.EndCriticalRegion(); return count; } else { if (id == writeLockOwnerId) return 1; else return 0; } } } public int WaitingReadCount { get { return (int)numReadWaiters; } } public int WaitingUpgradeCount { get { return (int)numUpgradeWaiters; } } public int WaitingWriteCount { get { return (int)numWriteWaiters; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- QilStrConcat.cs
- XPathDescendantIterator.cs
- ItemsPanelTemplate.cs
- ChannelPoolSettingsElement.cs
- SafeNativeMethods.cs
- DbRetry.cs
- TCEAdapterGenerator.cs
- XamlTypeMapper.cs
- DataBoundLiteralControl.cs
- DetailsViewPagerRow.cs
- EncodingDataItem.cs
- PersonalizationDictionary.cs
- AttachedPropertyInfo.cs
- GcSettings.cs
- NameTable.cs
- ChtmlFormAdapter.cs
- GraphicsPathIterator.cs
- AutomationIdentifier.cs
- XMLSchema.cs
- PropertyManager.cs
- SqlExpander.cs
- sqlinternaltransaction.cs
- SessionStateUtil.cs
- SessionPageStatePersister.cs
- ITreeGenerator.cs
- LinqDataSourceEditData.cs
- WebControlsSection.cs
- OuterGlowBitmapEffect.cs
- Int32RectValueSerializer.cs
- AssemblyInfo.cs
- EventWaitHandle.cs
- ViewGenResults.cs
- SqlTriggerAttribute.cs
- DataSourceControl.cs
- Transform.cs
- TableHeaderCell.cs
- SafeRightsManagementHandle.cs
- CmsUtils.cs
- DataGridViewImageCell.cs
- CSharpCodeProvider.cs
- CommonProperties.cs
- WebPartChrome.cs
- GPStream.cs
- DropDownList.cs
- Composition.cs
- ThreadStateException.cs
- ETagAttribute.cs
- RubberbandSelector.cs
- SolidBrush.cs
- ProcessModelInfo.cs
- PropertyMappingExceptionEventArgs.cs
- ClaimTypeElement.cs
- SelectedGridItemChangedEvent.cs
- TextServicesCompartment.cs
- ColumnPropertiesGroup.cs
- PackageDigitalSignatureManager.cs
- BamlResourceSerializer.cs
- TrackingProfileDeserializationException.cs
- CustomError.cs
- IntersectQueryOperator.cs
- FrameworkContentElement.cs
- CodeDelegateInvokeExpression.cs
- ConsoleEntryPoint.cs
- ColumnWidthChangingEvent.cs
- TextServicesProperty.cs
- TypeDescriptorFilterService.cs
- TextBlockAutomationPeer.cs
- VSWCFServiceContractGenerator.cs
- cache.cs
- DSASignatureDeformatter.cs
- SerializationFieldInfo.cs
- NetTcpSection.cs
- ProgressBarRenderer.cs
- ActivityContext.cs
- Rotation3DAnimationUsingKeyFrames.cs
- SchemaMerger.cs
- ServiceEndpointElementCollection.cs
- RadioButton.cs
- CredentialCache.cs
- DataTableReaderListener.cs
- GetLedgerEntryForRecipientRequest.cs
- RangeBase.cs
- SevenBitStream.cs
- UnmanagedMarshal.cs
- XamlBrushSerializer.cs
- MarkupWriter.cs
- FixedFlowMap.cs
- ProfileGroupSettings.cs
- TableFieldsEditor.cs
- LinearQuaternionKeyFrame.cs
- RewritingSimplifier.cs
- altserialization.cs
- ComPersistableTypeElementCollection.cs
- XmlObjectSerializerWriteContextComplexJson.cs
- CountAggregationOperator.cs
- CodeMemberField.cs
- altserialization.cs
- ISFTagAndGuidCache.cs
- XmlILOptimizerVisitor.cs
- ComponentEditorPage.cs