1 using System; // for Basic system types
2 using System.IO; // for File, Path
3 using System.Diagnostics; // for TraceInformation ...
4 using System.Threading;
5 using System.Security.Permissions;
6 using System.Runtime.CompilerServices;
8 using System.Core; // for strongly typed resources
11 namespace System.Threading
13 public enum LockRecursionPolicy
16 SupportsRecursion = 1,
20 // ReaderWriterCount tracks how many of each kind of lock is held by each thread.
21 // We keep a linked list for each thread, attached to a ThreadStatic field.
22 // These are reused wherever possible, so that a given thread will only
23 // allocate N of these, where N is the maximum number of locks held simultaneously
26 internal class ReaderWriterCount
28 // Which lock does this object belong to? This is a numeric ID for two reasons:
29 // 1) We don't want this field to keep the lock object alive, and a WeakReference would
31 // 2) Setting the value of a long is faster than setting the value of a reference.
32 // The "hot" paths in ReaderWriterLockSlim are short enough that this actually
36 // How many reader locks does this thread hold on this ReaderWriterLockSlim instance?
37 public int readercount;
39 // Ditto for writer/upgrader counts. These are only used if the lock allows recursion.
40 // But we have to have the fields on every ReaderWriterCount instance, because
41 // we reuse it for different locks.
42 public int writercount;
43 public int upgradecount;
45 // Next RWC in this thread's list.
46 public ReaderWriterCount next;
50 /// A reader-writer lock implementation that is intended to be simple, yet very
51 /// efficient. In particular only 1 interlocked operation is taken for any lock
52 /// operation (we use spin locks to achieve this). The spin lock is never held
53 /// for more than a few instructions (in particular, we never call event APIs
54 /// or in fact any non-trivial API while holding the spin lock).
57 [HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]
59 [HostProtection(MayLeakOnAbort = true)]
60 public class ReaderWriterLockSlim : IDisposable
62 //Specifying if the lock can be reacquired recursively.
65 // Lock specification for myLock: This lock protects exactly the local fields associated
66 // instance of ReaderWriterLockSlim. It does NOT protect the memory associated with the
67 // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent).
70 //The variables controlling spinning behavior of Mylock(which is a spin-lock)
72 const int LockSpinCycles = 20;
73 const int LockSpinCount = 10;
74 const int LockSleep0Count = 5;
76 // These variables allow use to avoid Setting events (which is expensive) if we don't have to.
77 uint numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent
78 uint numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent
79 uint numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1).
80 uint numUpgradeWaiters;
82 //Variable used for quick check when there are no waiters.
85 int upgradeLockOwnerId;
88 // conditions we wait on.
89 EventWaitHandle writeEvent; // threads waiting to acquire a write lock go here.
90 EventWaitHandle readEvent; // threads waiting to acquire a read lock go here (will be released in bulk)
91 EventWaitHandle upgradeEvent; // thread waiting to acquire the upgrade lock
92 EventWaitHandle waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one)
94 // Every lock instance has a unique ID, which is used by ReaderWriterCount to associate itself with the lock
95 // without holding a reference to it.
96 static long s_nextLockID;
99 // See comments on ReaderWriterCount.
101 static ReaderWriterCount t_rwc;
103 bool fUpgradeThreadHoldingRead;
105 private const int MaxSpinCount = 20;
107 //The uint, that contains info like if the writer lock is held, num of
113 //The Uint is divided as follows:
115 //Writer-Owned Waiting-Writers Waiting Upgraders Num-Readers
116 // 31 30 29 28.......0
118 //Dividing the uint, allows to vastly simplify logic for checking if a
119 //reader should go in etc. Setting the writer bit, will automatically
120 //make the value of the uint much larger than the max num of readers
121 //allowed, thus causing the check for max_readers to fail.
123 private const uint WRITER_HELD = 0x80000000;
124 private const uint WAITING_WRITERS = 0x40000000;
125 private const uint WAITING_UPGRADER = 0x20000000;
127 //The max readers is actually one less than it's theoretical max.
128 //This is done in order to prevent reader count overflows. If the reader
129 //count reaches max, other readers will wait.
130 private const uint MAX_READER = 0x10000000 - 2;
132 private const uint READER_MASK = 0x10000000 - 1;
134 private bool fDisposed;
136 private void InitializeThreadCounts()
138 upgradeLockOwnerId = -1;
139 writeLockOwnerId = -1;
142 public ReaderWriterLockSlim()
143 : this(LockRecursionPolicy.NoRecursion)
147 public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy)
149 if (recursionPolicy == LockRecursionPolicy.SupportsRecursion)
153 InitializeThreadCounts();
155 lockID = Interlocked.Increment(ref s_nextLockID);
159 [MethodImpl(MethodImplOptions.AggressiveInlining)]
161 private static bool IsRWEntryEmpty(ReaderWriterCount rwc)
165 else if (rwc.readercount == 0 && rwc.writercount == 0 && rwc.upgradecount == 0)
171 private bool IsRwHashEntryChanged(ReaderWriterCount lrwc)
173 return lrwc.lockID != this.lockID;
177 /// This routine retrieves/sets the per-thread counts needed to enforce the
178 /// various rules related to acquiring the lock.
180 /// DontAllocate is set to true if the caller just wants to get an existing
181 /// entry for this thread, but doesn't want to add one if an existing one
182 /// could not be found.
185 [MethodImpl(MethodImplOptions.AggressiveInlining)]
187 private ReaderWriterCount GetThreadRWCount(bool dontAllocate)
189 ReaderWriterCount rwc = t_rwc;
190 ReaderWriterCount empty = null;
193 if (rwc.lockID == this.lockID)
196 if (!dontAllocate && empty == null && IsRWEntryEmpty(rwc))
207 empty = new ReaderWriterCount();
212 empty.lockID = this.lockID;
216 public void EnterReadLock()
218 TryEnterReadLock(-1);
222 // Common timeout support
224 private struct TimeoutTracker
229 public TimeoutTracker(TimeSpan timeout)
231 long ltm = (long)timeout.TotalMilliseconds;
232 if (ltm < -1 || ltm > (long)Int32.MaxValue)
233 throw new ArgumentOutOfRangeException("timeout");
235 if (m_total != -1 && m_total != 0)
236 m_start = Environment.TickCount;
241 public TimeoutTracker(int millisecondsTimeout)
243 if (millisecondsTimeout < -1)
244 throw new ArgumentOutOfRangeException("millisecondsTimeout");
245 m_total = millisecondsTimeout;
246 if (m_total != -1 && m_total != 0)
247 m_start = Environment.TickCount;
252 public int RemainingMilliseconds
256 if (m_total == -1 || m_total == 0)
259 int elapsed = Environment.TickCount - m_start;
260 // elapsed may be negative if TickCount has overflowed by 2^31 milliseconds.
261 if (elapsed < 0 || elapsed >= m_total)
264 return m_total - elapsed;
268 public bool IsExpired
272 return RemainingMilliseconds == 0;
277 public bool TryEnterReadLock(TimeSpan timeout)
279 return TryEnterReadLock(new TimeoutTracker(timeout));
282 public bool TryEnterReadLock(int millisecondsTimeout)
284 return TryEnterReadLock(new TimeoutTracker(millisecondsTimeout));
287 private bool TryEnterReadLock(TimeoutTracker timeout)
290 Thread.BeginCriticalRegion();
291 #endif // !FEATURE_NETCORE
295 result = TryEnterReadLockCore(timeout);
301 Thread.EndCriticalRegion();
302 #endif // !FEATURE_NETCORE
307 private bool TryEnterReadLockCore(TimeoutTracker timeout)
310 throw new ObjectDisposedException(null);
312 ReaderWriterCount lrwc = null;
313 int id = Thread.CurrentThread.ManagedThreadId;
318 if (id == writeLockOwnerId)
321 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_ReadAfterWriteNotAllowed));
326 lrwc = GetThreadRWCount(false);
328 //Check if the reader lock is already acquired. Note, we could
329 //check the presence of a reader by not allocating rwc (But that
330 //would lead to two lookups in the common case. It's better to keep
331 //a count in the struucture).
332 if (lrwc.readercount > 0)
335 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveReadNotAllowed));
337 else if (id == upgradeLockOwnerId)
339 //The upgrade lock is already held.
340 //Update the global read counts and exit.
351 lrwc = GetThreadRWCount(false);
352 if (lrwc.readercount > 0)
358 else if (id == upgradeLockOwnerId)
360 //The upgrade lock is already held.
361 //Update the global read counts and exit.
365 fUpgradeThreadHoldingRead = true;
368 else if (id == writeLockOwnerId)
370 //The write lock is already held.
371 //Update global read counts here,
385 // We can enter a read lock if there are only read-locks have been given out
386 // and a writer is not trying to get in.
388 if (owners < MAX_READER)
390 // Good case, there is no contention, we are basically done
391 owners++; // Indicate we have another reader
396 if (spincount < MaxSpinCount)
399 if (timeout.IsExpired)
404 //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again.
405 if(IsRwHashEntryChanged(lrwc))
406 lrwc = GetThreadRWCount(false);
410 // Drat, we need to wait. Mark that we have waiters and wait.
411 if (readEvent == null) // Create the needed event
413 LazyCreateEvent(ref readEvent, false);
414 if (IsRwHashEntryChanged(lrwc))
415 lrwc = GetThreadRWCount(false);
416 continue; // since we left the lock, start over.
419 retVal = WaitOnEvent(readEvent, ref numReadWaiters, timeout);
424 if (IsRwHashEntryChanged(lrwc))
425 lrwc = GetThreadRWCount(false);
432 public void EnterWriteLock()
434 TryEnterWriteLock(-1);
437 public bool TryEnterWriteLock(TimeSpan timeout)
439 return TryEnterWriteLock(new TimeoutTracker(timeout));
442 public bool TryEnterWriteLock(int millisecondsTimeout)
444 return TryEnterWriteLock(new TimeoutTracker(millisecondsTimeout));
447 private bool TryEnterWriteLock(TimeoutTracker timeout)
450 Thread.BeginCriticalRegion();
451 #endif // !FEATURE_NETCORE
456 result = TryEnterWriteLockCore(timeout);
462 Thread.EndCriticalRegion();
463 #endif // !FEATURE_NETCORE
468 private bool TryEnterWriteLockCore(TimeoutTracker timeout)
471 throw new ObjectDisposedException(null);
473 int id = Thread.CurrentThread.ManagedThreadId;
474 ReaderWriterCount lrwc;
475 bool upgradingToWrite = false;
479 if (id == writeLockOwnerId)
482 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveWriteNotAllowed));
484 else if (id == upgradeLockOwnerId)
486 //AU->AW case is allowed once.
487 upgradingToWrite = true;
491 lrwc = GetThreadRWCount(true);
493 //Can't acquire write lock with reader lock held.
494 if (lrwc != null && lrwc.readercount > 0)
497 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed));
503 lrwc = GetThreadRWCount(false);
505 if (id == writeLockOwnerId)
511 else if (id == upgradeLockOwnerId)
513 upgradingToWrite = true;
515 else if (lrwc.readercount > 0)
517 //Write locks may not be acquired if only read locks have been
520 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed));
529 if (IsWriterAcquired())
531 // Good case, there is no contention, we are basically done
536 //Check if there is just one upgrader, and no readers.
537 //Assumption: Only one thread can have the upgrade lock, so the
538 //following check will fail for all other threads that may sneak in
539 //when the upgrading thread is waiting.
541 if (upgradingToWrite)
543 uint readercount = GetNumReaders();
545 if (readercount == 1)
547 //Good case again, there is just one upgrader, and no readers.
548 SetWriterAcquired(); // indicate we have a writer.
551 else if (readercount == 2)
555 if (IsRwHashEntryChanged(lrwc))
556 lrwc = GetThreadRWCount(false);
558 if (lrwc.readercount > 0)
560 //This check is needed for EU->ER->EW case, as the owner count will be two.
561 Debug.Assert(fIsReentrant);
562 Debug.Assert(fUpgradeThreadHoldingRead);
564 //Good case again, there is just one upgrader, and no readers.
565 SetWriterAcquired(); // indicate we have a writer.
572 if (spincount < MaxSpinCount)
575 if (timeout.IsExpired)
583 if (upgradingToWrite)
585 if (waitUpgradeEvent == null) // Create the needed event
587 LazyCreateEvent(ref waitUpgradeEvent, true);
588 continue; // since we left the lock, start over.
591 Debug.Assert(numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held.");
593 retVal = WaitOnEvent(waitUpgradeEvent, ref numWriteUpgradeWaiters, timeout);
595 //The lock is not held in case of failure.
601 // Drat, we need to wait. Mark that we have waiters and wait.
602 if (writeEvent == null) // create the needed event.
604 LazyCreateEvent(ref writeEvent, true);
605 continue; // since we left the lock, start over.
608 retVal = WaitOnEvent(writeEvent, ref numWriteWaiters, timeout);
609 //The lock is not held in case of failure.
615 Debug.Assert((owners & WRITER_HELD) > 0);
619 if (IsRwHashEntryChanged(lrwc))
620 lrwc = GetThreadRWCount(false);
626 writeLockOwnerId = id;
631 public void EnterUpgradeableReadLock()
633 TryEnterUpgradeableReadLock(-1);
636 public bool TryEnterUpgradeableReadLock(TimeSpan timeout)
638 return TryEnterUpgradeableReadLock(new TimeoutTracker(timeout));
641 public bool TryEnterUpgradeableReadLock(int millisecondsTimeout)
643 return TryEnterUpgradeableReadLock(new TimeoutTracker(millisecondsTimeout));
646 private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout)
649 Thread.BeginCriticalRegion();
650 #endif // !FEATURE_NETCORE
654 result = TryEnterUpgradeableReadLockCore(timeout);
660 Thread.EndCriticalRegion();
661 #endif // !FEATURE_NETCORE
666 private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout)
669 throw new ObjectDisposedException(null);
671 int id = Thread.CurrentThread.ManagedThreadId;
672 ReaderWriterCount lrwc;
676 if (id == upgradeLockOwnerId)
679 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveUpgradeNotAllowed));
681 else if (id == writeLockOwnerId)
684 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterWriteNotAllowed));
688 lrwc = GetThreadRWCount(true);
689 //Can't acquire upgrade lock with reader lock held.
690 if (lrwc != null && lrwc.readercount > 0)
693 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed));
699 lrwc = GetThreadRWCount(false);
701 if (id == upgradeLockOwnerId)
707 else if (id == writeLockOwnerId)
709 //Write lock is already held, Just update the global state
710 //to show presence of upgrader.
711 Debug.Assert((owners & WRITER_HELD) > 0);
713 upgradeLockOwnerId = id;
715 if (lrwc.readercount > 0)
716 fUpgradeThreadHoldingRead = true;
720 else if (lrwc.readercount > 0)
722 //Upgrade locks may not be acquired if only read locks have been
725 throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed));
735 //Once an upgrade lock is taken, it's like having a reader lock held
736 //until upgrade or downgrade operations are performed.
738 if ((upgradeLockOwnerId == -1) && (owners < MAX_READER))
741 upgradeLockOwnerId = id;
745 if (spincount < MaxSpinCount)
748 if (timeout.IsExpired)
756 // Drat, we need to wait. Mark that we have waiters and wait.
757 if (upgradeEvent == null) // Create the needed event
759 LazyCreateEvent(ref upgradeEvent, true);
760 continue; // since we left the lock, start over.
763 //Only one thread with the upgrade lock held can proceed.
764 retVal = WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, timeout);
771 //The lock may have been dropped getting here, so make a quick check to see whether some other
772 //thread did not grab the entry.
773 if (IsRwHashEntryChanged(lrwc))
774 lrwc = GetThreadRWCount(false);
783 public void ExitReadLock()
785 ReaderWriterCount lrwc = null;
789 lrwc = GetThreadRWCount(true);
791 if (lrwc == null || lrwc.readercount < 1)
793 //You have to be holding the read lock to make this call.
795 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedRead));
800 if (lrwc.readercount > 1)
805 Thread.EndCriticalRegion();
806 #endif // !FEATURE_NETCORE
810 if (Thread.CurrentThread.ManagedThreadId == upgradeLockOwnerId)
812 fUpgradeThreadHoldingRead = false;
816 Debug.Assert(owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken");
820 Debug.Assert(lrwc.readercount == 1);
823 ExitAndWakeUpAppropriateWaiters();
825 Thread.EndCriticalRegion();
826 #endif // !FEATURE_NETCORE
829 public void ExitWriteLock()
831 ReaderWriterCount lrwc;
834 if (Thread.CurrentThread.ManagedThreadId != writeLockOwnerId)
836 //You have to be holding the write lock to make this call.
837 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite));
844 lrwc = GetThreadRWCount(false);
849 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite));
852 if (lrwc.writercount < 1)
855 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite));
860 if (lrwc.writercount > 0)
864 Thread.EndCriticalRegion();
865 #endif // !FEATURE_NETCORE
870 Debug.Assert((owners & WRITER_HELD) > 0, "Calling ReleaseWriterLock when no write lock is held");
872 ClearWriterAcquired();
874 writeLockOwnerId = -1;
876 ExitAndWakeUpAppropriateWaiters();
878 Thread.EndCriticalRegion();
879 #endif // !FEATURE_NETCORE
882 public void ExitUpgradeableReadLock()
884 ReaderWriterCount lrwc;
887 if (Thread.CurrentThread.ManagedThreadId != upgradeLockOwnerId)
889 //You have to be holding the upgrade lock to make this call.
890 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade));
897 lrwc = GetThreadRWCount(true);
902 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade));
905 if (lrwc.upgradecount < 1)
908 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade));
913 if (lrwc.upgradecount > 0)
917 Thread.EndCriticalRegion();
918 #endif // !FEATURE_NETCORE
922 fUpgradeThreadHoldingRead = false;
926 upgradeLockOwnerId = -1;
928 ExitAndWakeUpAppropriateWaiters();
930 Thread.EndCriticalRegion();
931 #endif // !FEATURE_NETCORE
935 /// A routine for lazily creating a event outside the lock (so if errors
936 /// happen they are outside the lock and that we don't do much work
937 /// while holding a spin lock). If all goes well, reenter the lock and
940 private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent)
943 Debug.Assert(MyLockHeld);
944 Debug.Assert(waitEvent == null);
947 EventWaitHandle newEvent;
948 if (makeAutoResetEvent)
949 newEvent = new AutoResetEvent(false);
951 newEvent = new ManualResetEvent(false);
953 if (waitEvent == null) // maybe someone snuck in.
954 waitEvent = newEvent;
960 /// Waits on 'waitEvent' with a timeout
961 /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine.
963 private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout)
966 Debug.Assert(MyLockHeld);
972 //Setting these bits will prevent new readers from getting in.
973 if (numWriteWaiters == 1)
975 if (numWriteUpgradeWaiters == 1)
976 SetUpgraderWaiting();
978 bool waitSuccessful = false;
979 ExitMyLock(); // Do the wait outside of any lock
983 waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds);
990 if (numWriteWaiters == 0 && numWriteUpgradeWaiters == 0 && numUpgradeWaiters == 0 && numReadWaiters == 0)
993 if (numWriteWaiters == 0)
994 ClearWritersWaiting();
995 if (numWriteUpgradeWaiters == 0)
996 ClearUpgraderWaiting();
998 if (!waitSuccessful) // We may also be aboutto throw for some reason. Exit myLock.
1001 return waitSuccessful;
1005 /// Determines the appropriate events to set, leaves the locks, and sets the events.
1007 private void ExitAndWakeUpAppropriateWaiters()
1010 Debug.Assert(MyLockHeld);
1018 ExitAndWakeUpAppropriateWaitersPreferringWriters();
1021 private void ExitAndWakeUpAppropriateWaitersPreferringWriters()
1023 bool setUpgradeEvent = false;
1024 bool setReadEvent = false;
1025 uint readercount = GetNumReaders();
1027 //We need this case for EU->ER->EW case, as the read count will be 2 in
1031 if (numWriteUpgradeWaiters > 0 && fUpgradeThreadHoldingRead && readercount == 2)
1033 ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock)
1034 waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one).
1039 if (readercount == 1 && numWriteUpgradeWaiters > 0)
1041 //We have to be careful now, as we are droppping the lock.
1042 //No new writes should be allowed to sneak in if an upgrade
1045 ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock)
1046 waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one).
1048 else if (readercount == 0 && numWriteWaiters > 0)
1050 ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock)
1051 writeEvent.Set(); // release one writer.
1053 else if (readercount >= 0)
1055 if (numReadWaiters != 0 || numUpgradeWaiters != 0)
1057 if (numReadWaiters != 0)
1058 setReadEvent = true;
1060 if (numUpgradeWaiters != 0 && upgradeLockOwnerId == -1)
1062 setUpgradeEvent = true;
1065 ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock)
1068 readEvent.Set(); // release all readers.
1070 if (setUpgradeEvent)
1071 upgradeEvent.Set(); //release one upgrader.
1080 private bool IsWriterAcquired()
1082 return (owners & ~WAITING_WRITERS) == 0;
1085 private void SetWriterAcquired()
1087 owners |= WRITER_HELD; // indicate we have a writer.
1090 private void ClearWriterAcquired()
1092 owners &= ~WRITER_HELD;
1095 private void SetWritersWaiting()
1097 owners |= WAITING_WRITERS;
1100 private void ClearWritersWaiting()
1102 owners &= ~WAITING_WRITERS;
1105 private void SetUpgraderWaiting()
1107 owners |= WAITING_UPGRADER;
1110 private void ClearUpgraderWaiting()
1112 owners &= ~WAITING_UPGRADER;
1115 private uint GetNumReaders()
1117 return owners & READER_MASK;
1121 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1123 private void EnterMyLock()
1125 if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0)
1129 private void EnterMyLockSpin()
1131 int pc = Environment.ProcessorCount;
1132 for (int i = 0; ; i++)
1134 if (i < LockSpinCount && pc > 1)
1136 Thread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock.
1138 else if (i < (LockSpinCount + LockSleep0Count))
1140 Thread.Sleep(0); // Give up my quantum.
1144 Thread.Sleep(1); // Give up my quantum.
1147 if (myLock == 0 && Interlocked.CompareExchange(ref myLock, 1, 0) == 0)
1152 private void ExitMyLock()
1154 Debug.Assert(myLock != 0, "Exiting spin lock that is not held");
1156 Volatile.Write(ref myLock, 0);
1158 Thread.VolatileWrite(ref myLock, 0);
1163 private bool MyLockHeld { get { return myLock != 0; } }
1166 private static void SpinWait(int SpinCount)
1168 //Exponential backoff
1169 if ((SpinCount < 5) && (Environment.ProcessorCount > 1))
1171 Thread.SpinWait(LockSpinCycles * SpinCount);
1173 else if (SpinCount < MaxSpinCount - 3)
1183 public void Dispose()
1189 private void Dispose(bool disposing)
1191 if(disposing && !fDisposed)
1193 if(WaitingReadCount>0 || WaitingUpgradeCount > 0 || WaitingWriteCount > 0)
1194 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_IncorrectDispose));
1196 if(IsReadLockHeld || IsUpgradeableReadLockHeld || IsWriteLockHeld)
1197 throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_IncorrectDispose));
1199 if (writeEvent != null)
1205 if (readEvent != null)
1211 if (upgradeEvent != null)
1213 upgradeEvent.Close();
1214 upgradeEvent = null;
1217 if (waitUpgradeEvent != null)
1219 waitUpgradeEvent.Close();
1220 waitUpgradeEvent = null;
1227 public bool IsReadLockHeld
1231 if (RecursiveReadCount > 0)
1238 public bool IsUpgradeableReadLockHeld
1242 if (RecursiveUpgradeCount > 0)
1249 public bool IsWriteLockHeld
1253 if (RecursiveWriteCount > 0)
1260 public LockRecursionPolicy RecursionPolicy
1266 return LockRecursionPolicy.SupportsRecursion;
1270 return LockRecursionPolicy.NoRecursion;
1276 public int CurrentReadCount
1280 int numreaders = (int)GetNumReaders();
1282 if (upgradeLockOwnerId != -1)
1283 return numreaders - 1;
1290 public int RecursiveReadCount
1295 ReaderWriterCount lrwc = GetThreadRWCount(true);
1297 count = lrwc.readercount;
1303 public int RecursiveUpgradeCount
1311 ReaderWriterCount lrwc = GetThreadRWCount(true);
1313 count = lrwc.upgradecount;
1319 if (Thread.CurrentThread.ManagedThreadId == upgradeLockOwnerId)
1327 public int RecursiveWriteCount
1336 ReaderWriterCount lrwc = GetThreadRWCount(true);
1338 count = lrwc.writercount;
1344 if (Thread.CurrentThread.ManagedThreadId == writeLockOwnerId)
1352 public int WaitingReadCount
1356 return (int)numReadWaiters;
1360 public int WaitingUpgradeCount
1364 return (int)numUpgradeWaiters;
1368 public int WaitingWriteCount
1372 return (int)numWriteWaiters;