ReaderWriterLock.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //
  2. // System.Threading.ReaderWriterLock.cs
  3. //
  4. // Author:
  5. // Dick Porter ([email protected])
  6. // Jackson Harper ([email protected])
  7. // Lluis Sanchez Gual ([email protected])
  8. //
  9. // (C) Ximian, Inc. http://www.ximian.com
  10. // (C) 2004 Novell, Inc (http://www.novell.com)
  11. //
  12. using System.Collections;
  13. namespace System.Threading
  14. {
  15. public sealed class ReaderWriterLock
  16. {
  17. private int seq_num = 1;
  18. private int state = 0;
  19. private int readers = 0;
  20. private LockQueue writer_queue;
  21. private Hashtable reader_locks;
  22. private int writer_lock_owner;
  23. private int readyWaitingReaders = 0;
  24. public ReaderWriterLock()
  25. {
  26. writer_queue = new LockQueue (this);
  27. reader_locks = new Hashtable ();
  28. }
  29. public bool IsReaderLockHeld {
  30. get {
  31. lock (this) return reader_locks.ContainsKey (Thread.CurrentThreadId);
  32. }
  33. }
  34. public bool IsWriterLockHeld {
  35. get {
  36. lock (this) return (state < 0 && Thread.CurrentThreadId == writer_lock_owner);
  37. }
  38. }
  39. public int WriterSeqNum {
  40. get {
  41. lock (this) return seq_num;
  42. }
  43. }
  44. public void AcquireReaderLock (int millisecondsTimeout)
  45. {
  46. AcquireReaderLock (millisecondsTimeout, 1);
  47. }
  48. void AcquireReaderLock (int millisecondsTimeout, int initialLockCount)
  49. {
  50. lock (this) {
  51. if (HasWriterLock ()) {
  52. AcquireWriterLock (millisecondsTimeout, initialLockCount);
  53. return;
  54. }
  55. object nlocks = reader_locks [Thread.CurrentThreadId];
  56. if (nlocks == null)
  57. {
  58. // Not currently holding a reader lock
  59. // Wait if there is a write lock
  60. readers++;
  61. try {
  62. if (state < 0 || !writer_queue.IsEmpty) {
  63. if (!Monitor.Wait (this, millisecondsTimeout))
  64. throw new ApplicationException ("Timeout expired");
  65. }
  66. readyWaitingReaders--;
  67. }
  68. finally {
  69. readers--;
  70. }
  71. reader_locks [Thread.CurrentThreadId] = initialLockCount;
  72. state += initialLockCount;
  73. }
  74. else {
  75. reader_locks [Thread.CurrentThreadId] = ((int)nlocks) + 1;
  76. state++;
  77. }
  78. }
  79. }
  80. public void AcquireReaderLock(TimeSpan timeout)
  81. {
  82. int ms = CheckTimeout (timeout);
  83. AcquireReaderLock ((int) timeout.TotalMilliseconds, 1);
  84. }
  85. public void AcquireWriterLock (int millisecondsTimeout)
  86. {
  87. AcquireWriterLock (millisecondsTimeout, 1);
  88. }
  89. void AcquireWriterLock (int millisecondsTimeout, int initialLockCount)
  90. {
  91. lock (this) {
  92. if (HasWriterLock ()) {
  93. state--;
  94. return;
  95. }
  96. // wait while there are reader locks or another writer lock, or
  97. // other threads waiting for the writer lock
  98. if (state != 0 || !writer_queue.IsEmpty || readers > 0) {
  99. if (!writer_queue.Wait (millisecondsTimeout))
  100. throw new ApplicationException ("Timeout expited");
  101. }
  102. state = -initialLockCount;
  103. writer_lock_owner = Thread.CurrentThreadId;
  104. seq_num++;
  105. }
  106. }
  107. public void AcquireWriterLock(TimeSpan timeout) {
  108. int ms = CheckTimeout (timeout);
  109. AcquireWriterLock (ms, 1);
  110. }
  111. public bool AnyWritersSince(int seqNum) {
  112. lock (this) {
  113. return (this.seq_num > seqNum);
  114. }
  115. }
  116. public void DowngradeFromWriterLock(ref LockCookie lockCookie)
  117. {
  118. lock (this) {
  119. if (!HasWriterLock())
  120. throw new ApplicationException ("The thread does not have the writer lock.");
  121. state = lockCookie.ReaderLocks;
  122. reader_locks [Thread.CurrentThreadId] = state;
  123. if (readers > 0) {
  124. readyWaitingReaders = readers;
  125. Monitor.PulseAll (this);
  126. }
  127. // MSDN: A thread does not block when downgrading from the writer lock,
  128. // even if other threads are waiting for the writer lock
  129. }
  130. }
  131. public LockCookie ReleaseLock()
  132. {
  133. LockCookie cookie;
  134. lock (this) {
  135. cookie = GetLockCookie ();
  136. if (cookie.WriterLocks != 0)
  137. ReleaseWriterLock (cookie.WriterLocks);
  138. else if (cookie.ReaderLocks != 0) {
  139. ReleaseReaderLock (cookie.ReaderLocks, cookie.ReaderLocks);
  140. }
  141. }
  142. return cookie;
  143. }
  144. public void ReleaseReaderLock()
  145. {
  146. lock (this) {
  147. if (HasWriterLock ()) {
  148. ReleaseWriterLock ();
  149. return;
  150. }
  151. else if (state > 0) {
  152. object read_lock_count = reader_locks [Thread.CurrentThreadId];
  153. if (read_lock_count != null) {
  154. ReleaseReaderLock ((int)read_lock_count, 1);
  155. return;
  156. }
  157. }
  158. throw new ApplicationException ("The thread does not have any reader or writer locks.");
  159. }
  160. }
  161. void ReleaseReaderLock (int currentCount, int releaseCount)
  162. {
  163. int new_count = currentCount - releaseCount;
  164. if (new_count == 0)
  165. reader_locks.Remove (Thread.CurrentThreadId);
  166. else
  167. reader_locks [Thread.CurrentThreadId] = new_count;
  168. state -= releaseCount;
  169. if (state == 0 && (readers == 0 || readyWaitingReaders <= 0) && !writer_queue.IsEmpty)
  170. writer_queue.Pulse ();
  171. }
  172. public void ReleaseWriterLock()
  173. {
  174. lock (this) {
  175. if (!HasWriterLock())
  176. throw new ApplicationException ("The thread does not have the writer lock.");
  177. ReleaseWriterLock (1);
  178. }
  179. }
  180. void ReleaseWriterLock (int releaseCount)
  181. {
  182. state += releaseCount;
  183. if (state == 0) {
  184. if (readers > 0) {
  185. readyWaitingReaders = readers;
  186. Monitor.PulseAll (this);
  187. }
  188. else if (!writer_queue.IsEmpty)
  189. writer_queue.Pulse ();
  190. }
  191. }
  192. public void RestoreLock(ref LockCookie lockCookie)
  193. {
  194. lock (this) {
  195. if (lockCookie.WriterLocks != 0)
  196. AcquireWriterLock (-1, lockCookie.WriterLocks);
  197. else if (lockCookie.ReaderLocks != 0)
  198. AcquireReaderLock (-1, lockCookie.ReaderLocks);
  199. }
  200. }
  201. public LockCookie UpgradeToWriterLock(int millisecondsTimeout)
  202. {
  203. LockCookie cookie;
  204. lock (this) {
  205. cookie = GetLockCookie ();
  206. if (cookie.WriterLocks != 0) {
  207. state--;
  208. return cookie;
  209. }
  210. if (cookie.ReaderLocks != 0)
  211. ReleaseReaderLock (cookie.ReaderLocks, cookie.ReaderLocks);
  212. }
  213. // Don't do this inside the lock, since it can cause a deadlock.
  214. AcquireWriterLock (millisecondsTimeout);
  215. return cookie;
  216. }
  217. public LockCookie UpgradeToWriterLock(TimeSpan timeout)
  218. {
  219. int ms = CheckTimeout (timeout);
  220. return UpgradeToWriterLock (ms);
  221. }
  222. LockCookie GetLockCookie ()
  223. {
  224. LockCookie cookie = new LockCookie (Thread.CurrentThreadId);
  225. if (HasWriterLock())
  226. cookie.WriterLocks = -state;
  227. else {
  228. object locks = reader_locks [Thread.CurrentThreadId];
  229. if (locks != null) cookie.ReaderLocks = (int)locks;
  230. }
  231. return cookie;
  232. }
  233. bool HasWriterLock ()
  234. {
  235. return (state < 0 && Thread.CurrentThreadId == writer_lock_owner);
  236. }
  237. private int CheckTimeout (TimeSpan timeout)
  238. {
  239. int ms = (int) timeout.TotalMilliseconds;
  240. if (ms < -1)
  241. throw new ArgumentOutOfRangeException ("timeout",
  242. "Number must be either non-negative or -1");
  243. return ms;
  244. }
  245. }
  246. }