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