Code - Class EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock


1 /*
2   File: WriterPreferenceReadWriteLock.java
3
4   Originally written by Doug Lea and released into the public domain.
5   This may be used for any purposes whatsoever without acknowledgment.
6   Thanks for the assistance and support of Sun Microsystems Labs,
7   and everyone contributing, testing, and using this code.
8
9   History:
10   Date Who What
11   11Jun1998 dl Create public version
12    5Aug1998 dl replaced int counters with longs
13   25aug1998 dl record writer thread
14    3May1999 dl add notifications on interrupt/timeout
15
16 */

17
18 package EDU.oswego.cs.dl.util.concurrent;
19
20 /**
21  * A ReadWriteLock that prefers waiting writers over
22  * waiting readers when there is contention. This class
23  * is adapted from the versions described in CPJ, improving
24  * on the ones there a bit by segregating reader and writer
25  * wait queues, which is typically more efficient.
26  * <p>
27  * The locks are <em>NOT</em> reentrant. In particular,
28  * even though it may appear to usually work OK,
29  * a thread holding a read lock should not attempt to
30  * re-acquire it. Doing so risks lockouts when there are
31  * also waiting writers.
32  * <p>[<a HREF="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
33  **/

34
35 public class WriterPreferenceReadWriteLock implements ReadWriteLock {
36
37   protected long activeReaders_ = 0;
38   protected Thread activeWriter_ = null;
39   protected long waitingReaders_ = 0;
40   protected long waitingWriters_ = 0;
41
42
43   protected final ReaderLock readerLock_ = new ReaderLock();
44   protected final WriterLock writerLock_ = new WriterLock();
45
46   public Sync writeLock() { return writerLock_; }
47   public Sync readLock() { return readerLock_; }
48
49   /*
50     A bunch of small synchronized methods are needed
51     to allow communication from the Lock objects
52     back to this object, that serves as controller
53   */

54
55
56   protected synchronized void cancelledWaitingReader() { --waitingReaders_; }
57   protected synchronized void cancelledWaitingWriter() { --waitingWriters_; }
58
59
60   /** Override this method to change to reader preference **/
61   protected boolean allowReader() {
62     return activeWriter_ == null && waitingWriters_ == 0;
63   }
64
65
66   protected synchronized boolean startRead() {
67     boolean allowRead = allowReader();
68     if (allowRead) ++activeReaders_;
69     return allowRead;
70   }
71
72   protected synchronized boolean startWrite() {
73
74     // The allowWrite expression cannot be modified without
75
// also changing startWrite, so is hard-wired
76

77     boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);
78     if (allowWrite) activeWriter_ = Thread.currentThread();
79     return allowWrite;
80    }
81
82
83   /*
84      Each of these variants is needed to maintain atomicity
85      of wait counts during wait loops. They could be
86      made faster by manually inlining each other. We hope that
87      compilers do this for us though.
88   */

89
90   protected synchronized boolean startReadFromNewReader() {
91     boolean pass = startRead();
92     if (!pass) ++waitingReaders_;
93     return pass;
94   }
95
96   protected synchronized boolean startWriteFromNewWriter() {
97     boolean pass = startWrite();
98     if (!pass) ++waitingWriters_;
99     return pass;
100   }
101
102   protected synchronized boolean startReadFromWaitingReader() {
103     boolean pass = startRead();
104     if (pass) --waitingReaders_;
105     return pass;
106   }
107
108   protected synchronized boolean startWriteFromWaitingWriter() {
109     boolean pass = startWrite();
110     if (pass) --waitingWriters_;
111     return pass;
112   }
113
114   /**
115    * Called upon termination of a read.
116    * Returns the object to signal to wake up a waiter, or null if no such
117    **/

118   protected synchronized Signaller endRead() {
119     if (--activeReaders_ == 0 && waitingWriters_ > 0)
120       return writerLock_;
121     else
122       return null;
123   }
124
125   
126   /**
127    * Called upon termination of a write.
128    * Returns the object to signal to wake up a waiter, or null if no such
129    **/

130   protected synchronized Signaller endWrite() {
131     activeWriter_ = null;
132     if (waitingReaders_ > 0 && allowReader())
133       return readerLock_;
134     else if (waitingWriters_ > 0)
135       return writerLock_;
136     else
137       return null;
138   }
139
140
141   /**
142    * Reader and Writer requests are maintained in two different
143    * wait sets, by two different objects. These objects do not
144    * know whether the wait sets need notification since they
145    * don't know preference rules. So, each supports a
146    * method that can be selected by main controlling object
147    * to perform the notifications. This base class simplifies mechanics.
148    **/

149
150   protected abstract class Signaller { // base for ReaderLock and WriterLock
151
abstract void signalWaiters();
152   }
153
154   protected class ReaderLock extends Signaller implements Sync {
155
156     public void acquire() throws InterruptedException {
157       if (Thread.interrupted()) throw new InterruptedException();
158       InterruptedException ie = null;
159       synchronized(this) {
160         if (!startReadFromNewReader()) {
161           for (;;) {
162             try {
163               ReaderLock.this.wait();
164               if (startReadFromWaitingReader())
165                 return;
166             }
167             catch(InterruptedException ex){
168               cancelledWaitingReader();
169               ie = ex;
170               break;
171             }
172           }
173         }
174       }
175       if (ie != null) {
176         // fall through outside synch on interrupt.
177
// This notification is not really needed here,
178
// but may be in plausible subclasses
179
writerLock_.signalWaiters();
180         throw ie;
181       }
182     }
183
184
185     public void release() {
186       Signaller s = endRead();
187       if (s != null) s.signalWaiters();
188     }
189
190
191     synchronized void signalWaiters() { ReaderLock.this.notifyAll(); }
192
193     public boolean attempt(long msecs) throws InterruptedException {
194       if (Thread.interrupted()) throw new InterruptedException();
195       InterruptedException ie = null;
196       synchronized(this) {
197         if (msecs <= 0)
198           return startRead();
199         else if (startReadFromNewReader())
200           return true;
201         else {
202           long waitTime = msecs;
203           long start = System.currentTimeMillis();
204           for (;;) {
205             try { ReaderLock.this.wait(waitTime); }
206             catch(InterruptedException ex){
207               cancelledWaitingReader();
208               ie = ex;
209               break;
210             }
211             if (startReadFromWaitingReader())
212               return true;
213             else {
214               waitTime = msecs - (System.currentTimeMillis() - start);
215               if (waitTime <= 0) {
216                 cancelledWaitingReader();
217                 break;
218               }
219             }
220           }
221         }
222       }
223       // safeguard on interrupt or timeout:
224
writerLock_.signalWaiters();
225       if (ie != null) throw ie;
226       else return false; // timed out
227
}
228
229   }
230
231   protected class WriterLock extends Signaller implements Sync {
232
233     public void acquire() throws InterruptedException {
234       if (Thread.interrupted()) throw new InterruptedException();
235       InterruptedException ie = null;
236       synchronized(this) {
237         if (!startWriteFromNewWriter()) {
238           for (;;) {
239             try {
240               WriterLock.this.wait();
241               if (startWriteFromWaitingWriter())
242                 return;
243             }
244             catch(InterruptedException ex){
245               cancelledWaitingWriter();
246               WriterLock.this.notify();
247               ie = ex;
248               break;
249             }
250           }
251         }
252       }
253       if (ie != null) {
254         // Fall through outside synch on interrupt.
255
// On exception, we may need to signal readers.
256
// It is not worth checking here whether it is strictly necessary.
257
readerLock_.signalWaiters();
258         throw ie;
259       }
260     }
261
262     public void release(){
263       Signaller s = endWrite();
264       if (s != null) s.signalWaiters();
265     }
266
267     synchronized void signalWaiters() { WriterLock.this.notify(); }
268
269     public boolean attempt(long msecs) throws InterruptedException {
270       if (Thread.interrupted()) throw new InterruptedException();
271       InterruptedException ie = null;
272       synchronized(this) {
273         if (msecs <= 0)
274           return startWrite();
275         else if (startWriteFromNewWriter())
276           return true;
277         else {
278           long waitTime = msecs;
279           long start = System.currentTimeMillis();
280           for (;;) {
281             try { WriterLock.this.wait(waitTime); }
282             catch(InterruptedException ex){
283               cancelledWaitingWriter();
284               WriterLock.this.notify();
285               ie = ex;
286               break;
287             }
288             if (startWriteFromWaitingWriter())
289               return true;
290             else {
291               waitTime = msecs - (System.currentTimeMillis() - start);
292               if (waitTime <= 0) {
293                 cancelledWaitingWriter();
294                 WriterLock.this.notify();
295                 break;
296               }
297             }
298           }
299         }
300       }
301       
302       readerLock_.signalWaiters();
303       if (ie != null) throw ie;
304       else return false; // timed out
305
}
306
307   }
308
309
310
311 }
312
313

Java API By Example, From Geeks To Geeks. | Conditions of Use | About Us © 2002 - 2005, KickJava.com, or its affiliates