2 // System.Threading.ReaderWriterLock.cs
5 // Dick Porter (dick@ximian.com)
6 // Jackson Harper (jackson@ximian.com)
7 // Lluis Sanchez Gual (lluis@ximian.com)
9 // (C) Ximian, Inc. http://www.ximian.com
10 // (C) 2004 Novell, Inc (http://www.novell.com)
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System
.Collections
;
38 namespace System
.Threading
40 public sealed class ReaderWriterLock
42 private int seq_num
= 1;
43 private int state
= 0;
44 private int readers
= 0;
45 private LockQueue writer_queue
;
46 private Hashtable reader_locks
;
47 private int writer_lock_owner
;
48 private int readyWaitingReaders
= 0;
50 public ReaderWriterLock()
52 writer_queue
= new LockQueue (this);
53 reader_locks
= new Hashtable ();
56 public bool IsReaderLockHeld
{
58 lock (this) return reader_locks
.ContainsKey (Thread
.CurrentThreadId
);
62 public bool IsWriterLockHeld
{
64 lock (this) return (state
< 0 && Thread
.CurrentThreadId
== writer_lock_owner
);
68 public int WriterSeqNum
{
70 lock (this) return seq_num
;
74 public void AcquireReaderLock (int millisecondsTimeout
)
76 AcquireReaderLock (millisecondsTimeout
, 1);
79 void AcquireReaderLock (int millisecondsTimeout
, int initialLockCount
)
82 if (HasWriterLock ()) {
83 AcquireWriterLock (millisecondsTimeout
, initialLockCount
);
87 object nlocks
= reader_locks
[Thread
.CurrentThreadId
];
90 // Not currently holding a reader lock
91 // Wait if there is a write lock
94 if (state
< 0 || !writer_queue
.IsEmpty
) {
95 if (!Monitor
.Wait (this, millisecondsTimeout
))
96 throw new ApplicationException ("Timeout expired");
98 readyWaitingReaders
--;
104 reader_locks
[Thread
.CurrentThreadId
] = initialLockCount
;
105 state
+= initialLockCount
;
108 reader_locks
[Thread
.CurrentThreadId
] = ((int)nlocks
) + 1;
114 public void AcquireReaderLock(TimeSpan timeout
)
116 int ms
= CheckTimeout (timeout
);
117 AcquireReaderLock ((int) timeout
.TotalMilliseconds
, 1);
120 public void AcquireWriterLock (int millisecondsTimeout
)
122 AcquireWriterLock (millisecondsTimeout
, 1);
125 void AcquireWriterLock (int millisecondsTimeout
, int initialLockCount
)
128 if (HasWriterLock ()) {
133 // wait while there are reader locks or another writer lock, or
134 // other threads waiting for the writer lock
135 if (state
!= 0 || !writer_queue
.IsEmpty
|| readers
> 0) {
136 if (!writer_queue
.Wait (millisecondsTimeout
))
137 throw new ApplicationException ("Timeout expited");
140 state
= -initialLockCount
;
141 writer_lock_owner
= Thread
.CurrentThreadId
;
146 public void AcquireWriterLock(TimeSpan timeout
) {
147 int ms
= CheckTimeout (timeout
);
148 AcquireWriterLock (ms
, 1);
151 public bool AnyWritersSince(int seqNum
) {
153 return (this.seq_num
> seqNum
);
157 public void DowngradeFromWriterLock(ref LockCookie lockCookie
)
160 if (!HasWriterLock())
161 throw new ApplicationException ("The thread does not have the writer lock.");
163 state
= lockCookie
.ReaderLocks
;
164 reader_locks
[Thread
.CurrentThreadId
] = state
;
166 readyWaitingReaders
= readers
;
167 Monitor
.PulseAll (this);
170 // MSDN: A thread does not block when downgrading from the writer lock,
171 // even if other threads are waiting for the writer lock
175 public LockCookie
ReleaseLock()
179 cookie
= GetLockCookie ();
180 if (cookie
.WriterLocks
!= 0)
181 ReleaseWriterLock (cookie
.WriterLocks
);
182 else if (cookie
.ReaderLocks
!= 0) {
183 ReleaseReaderLock (cookie
.ReaderLocks
, cookie
.ReaderLocks
);
189 public void ReleaseReaderLock()
192 if (HasWriterLock ()) {
193 ReleaseWriterLock ();
196 else if (state
> 0) {
197 object read_lock_count
= reader_locks
[Thread
.CurrentThreadId
];
198 if (read_lock_count
!= null) {
199 ReleaseReaderLock ((int)read_lock_count
, 1);
203 throw new ApplicationException ("The thread does not have any reader or writer locks.");
207 void ReleaseReaderLock (int currentCount
, int releaseCount
)
209 int new_count
= currentCount
- releaseCount
;
212 reader_locks
.Remove (Thread
.CurrentThreadId
);
214 reader_locks
[Thread
.CurrentThreadId
] = new_count
;
216 state
-= releaseCount
;
217 if (state
== 0 && (readers
== 0 || readyWaitingReaders
<= 0) && !writer_queue
.IsEmpty
)
218 writer_queue
.Pulse ();
221 public void ReleaseWriterLock()
224 if (!HasWriterLock())
225 throw new ApplicationException ("The thread does not have the writer lock.");
227 ReleaseWriterLock (1);
231 void ReleaseWriterLock (int releaseCount
)
233 state
+= releaseCount
;
236 readyWaitingReaders
= readers
;
237 Monitor
.PulseAll (this);
239 else if (!writer_queue
.IsEmpty
)
240 writer_queue
.Pulse ();
244 public void RestoreLock(ref LockCookie lockCookie
)
247 if (lockCookie
.WriterLocks
!= 0)
248 AcquireWriterLock (-1, lockCookie
.WriterLocks
);
249 else if (lockCookie
.ReaderLocks
!= 0)
250 AcquireReaderLock (-1, lockCookie
.ReaderLocks
);
254 public LockCookie
UpgradeToWriterLock(int millisecondsTimeout
)
258 cookie
= GetLockCookie ();
259 if (cookie
.WriterLocks
!= 0) {
264 if (cookie
.ReaderLocks
!= 0)
265 ReleaseReaderLock (cookie
.ReaderLocks
, cookie
.ReaderLocks
);
268 // Don't do this inside the lock, since it can cause a deadlock.
269 AcquireWriterLock (millisecondsTimeout
);
273 public LockCookie
UpgradeToWriterLock(TimeSpan timeout
)
275 int ms
= CheckTimeout (timeout
);
276 return UpgradeToWriterLock (ms
);
279 LockCookie
GetLockCookie ()
281 LockCookie cookie
= new LockCookie (Thread
.CurrentThreadId
);
284 cookie
.WriterLocks
= -state
;
286 object locks
= reader_locks
[Thread
.CurrentThreadId
];
287 if (locks
!= null) cookie
.ReaderLocks
= (int)locks
;
292 bool HasWriterLock ()
294 return (state
< 0 && Thread
.CurrentThreadId
== writer_lock_owner
);
297 private int CheckTimeout (TimeSpan timeout
)
299 int ms
= (int) timeout
.TotalMilliseconds
;
302 throw new ArgumentOutOfRangeException ("timeout",
303 "Number must be either non-negative or -1");