ReaderWriterLock.cs 6.2 KB

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