for TARGET_J2EE only:
[mono.git] / mcs / class / corlib / System.Threading / ReaderWriterLock.cs
1 //
2 // System.Threading.ReaderWriterLock.cs
3 //
4 // Author:
5 //   Dick Porter (dick@ximian.com)
6 //   Jackson Harper (jackson@ximian.com)
7 //   Lluis Sanchez Gual (lluis@ximian.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 // (C) 2004 Novell, Inc (http://www.novell.com)
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
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:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
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.
34 //
35
36 using System.Collections;
37
38 #if NET_2_0
39 using System.Runtime.InteropServices;
40 using System.Runtime.ConstrainedExecution;
41 #endif
42
43 namespace System.Threading
44 {
45 #if NET_2_0
46         [ComVisible (true)]
47         public sealed class ReaderWriterLock: CriticalFinalizerObject
48 #else
49         public sealed class ReaderWriterLock
50 #endif
51         {
52                 private int seq_num = 1;
53                 private int state;
54                 private int readers;
55                 private LockQueue writer_queue;
56                 private Hashtable reader_locks;
57                 private int writer_lock_owner;
58
59                 public ReaderWriterLock()
60                 {
61                         writer_queue = new LockQueue (this);
62                         reader_locks = new Hashtable ();
63
64 #if NET_2_0
65                         GC.SuppressFinalize (this);
66 #endif
67                 }
68
69 #if NET_2_0
70                 [MonoTODO]
71                 ~ReaderWriterLock ()
72                 {
73                 }
74 #endif
75
76                 public bool IsReaderLockHeld {
77 #if NET_2_0
78                         [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
79 #endif
80                         get {
81                                 lock (this) return reader_locks.ContainsKey (Thread.CurrentThreadId);
82                         }
83                 }
84
85                 public bool IsWriterLockHeld {
86 #if NET_2_0
87                         [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
88 #endif
89                         get {
90                                 lock (this) return (state < 0 && Thread.CurrentThreadId == writer_lock_owner);
91                         }
92                 }
93
94                 public int WriterSeqNum {
95                         get {
96                                 lock (this) return seq_num;
97                         }
98                 }
99
100                 public void AcquireReaderLock (int millisecondsTimeout) 
101                 {
102                         AcquireReaderLock (millisecondsTimeout, 1);
103                 }
104                 
105                 void AcquireReaderLock (int millisecondsTimeout, int initialLockCount) 
106                 {
107                         lock (this) {
108                                 if (HasWriterLock ()) {
109                                         AcquireWriterLock (millisecondsTimeout, initialLockCount);
110                                         return;
111                                 }
112                                 
113                                 object nlocks = reader_locks [Thread.CurrentThreadId];
114                                 if (nlocks == null)
115                                 {
116                                         // Not currently holding a reader lock
117                                         // Wait if there is a write lock
118                                         readers++;
119                                         try {
120                                                 if (state < 0 || !writer_queue.IsEmpty) {
121                                                         do {
122                                                                 if (!Monitor.Wait (this, millisecondsTimeout))
123                                                                         throw new ApplicationException ("Timeout expired");
124                                                         } while (state < 0);
125                                                 }
126                                         }
127                                         finally {
128                                                 readers--;
129                                         }
130                                         
131                                         reader_locks [Thread.CurrentThreadId] = initialLockCount;
132                                         state += initialLockCount;
133                                 }
134                                 else {
135                                         reader_locks [Thread.CurrentThreadId] = ((int)nlocks) + 1;
136                                         state++;
137                                 }
138                         }
139                 }
140
141                 public void AcquireReaderLock(TimeSpan timeout)
142                 {
143                         int ms = CheckTimeout (timeout);
144                         AcquireReaderLock (ms, 1);
145                 }
146
147                 public void AcquireWriterLock (int millisecondsTimeout)
148                 {
149                         AcquireWriterLock (millisecondsTimeout, 1);
150                 }
151                 
152                 void AcquireWriterLock (int millisecondsTimeout, int initialLockCount)
153                 {
154                         lock (this) {
155                                 if (HasWriterLock ()) {
156                                         state--;
157                                         return;
158                                 }
159                                 
160                                 // wait while there are reader locks or another writer lock, or
161                                 // other threads waiting for the writer lock
162                                 if (state != 0 || !writer_queue.IsEmpty) {
163                                         do {
164                                                 if (!writer_queue.Wait (millisecondsTimeout))
165                                                         throw new ApplicationException ("Timeout expired");
166                                         } while (state != 0);
167                                 }
168
169                                 state = -initialLockCount;
170                                 writer_lock_owner = Thread.CurrentThreadId;
171                                 seq_num++;
172                         }
173                 }
174
175                 public void AcquireWriterLock(TimeSpan timeout) {
176                         int ms = CheckTimeout (timeout);
177                         AcquireWriterLock (ms, 1);
178                 }
179
180                 public bool AnyWritersSince(int seqNum) {
181                         lock (this) {
182                                 return (this.seq_num > seqNum);
183                         }
184                 }
185
186                 public void DowngradeFromWriterLock(ref LockCookie lockCookie)
187                 {
188                         lock (this) {
189                                 if (!HasWriterLock())
190                                         throw new ApplicationException ("The thread does not have the writer lock.");
191                                 
192                                 state = lockCookie.ReaderLocks;
193                                 reader_locks [Thread.CurrentThreadId] = state;
194                                 if (readers > 0) {
195                                         Monitor.PulseAll (this);
196                                 }
197                                 
198                                 // MSDN: A thread does not block when downgrading from the writer lock, 
199                                 // even if other threads are waiting for the writer lock
200                         }
201                 }
202
203                 public LockCookie ReleaseLock()
204                 {
205                         LockCookie cookie;
206                         lock (this) {
207                                 cookie = GetLockCookie ();
208                                 if (cookie.WriterLocks != 0)
209                                         ReleaseWriterLock (cookie.WriterLocks);
210                                 else if (cookie.ReaderLocks != 0) {
211                                         ReleaseReaderLock (cookie.ReaderLocks, cookie.ReaderLocks);
212                                 }
213                         }
214                         return cookie;
215                 }
216                 
217 #if NET_2_0
218                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
219 #endif
220                 public void ReleaseReaderLock()
221                 {
222                         lock (this) {
223                                 if (HasWriterLock ()) {
224                                         ReleaseWriterLock ();
225                                         return;
226                                 }
227                                 else if (state > 0) {
228                                         object read_lock_count = reader_locks [Thread.CurrentThreadId];
229                                         if (read_lock_count != null) {
230                                                 ReleaseReaderLock ((int)read_lock_count, 1);
231                                                 return;
232                                         }
233                                 }
234
235                                 throw new ApplicationException ("The thread does not have any reader or writer locks.");
236                         }
237                 }
238
239                 void ReleaseReaderLock (int currentCount, int releaseCount)
240                 {
241                         int new_count = currentCount - releaseCount;
242                         
243                         if (new_count == 0)
244                                 reader_locks.Remove (Thread.CurrentThreadId);
245                         else
246                                 reader_locks [Thread.CurrentThreadId] = new_count;
247                                 
248                         state -= releaseCount;
249                         if (state == 0 && !writer_queue.IsEmpty)
250                                 writer_queue.Pulse ();
251                 }
252
253 #if NET_2_0
254                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
255 #endif
256                 public void ReleaseWriterLock()
257                 {
258                         lock (this) {
259                                 if (!HasWriterLock())
260                                         throw new ApplicationException ("The thread does not have the writer lock.");
261                                 
262                                 ReleaseWriterLock (1);
263                         }
264                 }
265
266                 void ReleaseWriterLock (int releaseCount)
267                 {
268                         state += releaseCount;
269                         if (state == 0) {
270                                 if (readers > 0) {
271                                         Monitor.PulseAll (this);
272                                 }
273                                 else if (!writer_queue.IsEmpty)
274                                         writer_queue.Pulse ();
275                         }
276                 }
277                 
278                 public void RestoreLock(ref LockCookie lockCookie)
279                 {
280                         lock (this) {
281                                 if (lockCookie.WriterLocks != 0)
282                                         AcquireWriterLock (-1, lockCookie.WriterLocks);
283                                 else if (lockCookie.ReaderLocks != 0)
284                                         AcquireReaderLock (-1, lockCookie.ReaderLocks);
285                         }
286                 }
287
288                 public LockCookie UpgradeToWriterLock(int millisecondsTimeout)
289                 {
290                         LockCookie cookie;
291                         lock (this) {
292                                 cookie = GetLockCookie ();
293                                 if (cookie.WriterLocks != 0) {
294                                         state--;
295                                         return cookie;
296                                 }
297                                 
298                                 if (cookie.ReaderLocks != 0)
299                                         ReleaseReaderLock (cookie.ReaderLocks, cookie.ReaderLocks);
300                         }
301                         
302                         // Don't do this inside the lock, since it can cause a deadlock.
303                         AcquireWriterLock (millisecondsTimeout);
304                         return cookie;
305                 }
306                 
307                 public LockCookie UpgradeToWriterLock(TimeSpan timeout)
308                 {
309                         int ms = CheckTimeout (timeout);
310                         return UpgradeToWriterLock (ms);
311                 }
312                 
313                 LockCookie GetLockCookie ()
314                 {
315                         LockCookie cookie = new LockCookie (Thread.CurrentThreadId);
316                         
317                         if (HasWriterLock())
318                                 cookie.WriterLocks = -state;
319                         else {
320                                 object locks = reader_locks [Thread.CurrentThreadId];
321                                 if (locks != null) cookie.ReaderLocks = (int)locks;
322                         }
323                         return cookie;
324                 }
325
326                 bool HasWriterLock ()
327                 {
328                         return (state < 0 && Thread.CurrentThreadId == writer_lock_owner);
329                 }
330                 
331                 private int CheckTimeout (TimeSpan timeout)
332                 {
333                         int ms = (int) timeout.TotalMilliseconds;
334
335                         if (ms < -1)
336                                 throw new ArgumentOutOfRangeException ("timeout",
337                                                 "Number must be either non-negative or -1");
338                         return ms;
339                 }
340         }
341 }
342