001: /*
002: File: WriterPreferenceReadWriteLock.java
003:
004: Originally written by Doug Lea and released into the public domain.
005: This may be used for any purposes whatsoever without acknowledgment.
006: Thanks for the assistance and support of Sun Microsystems Labs,
007: and everyone contributing, testing, and using this code.
008:
009: History:
010: Date Who What
011: 11Jun1998 dl Create public version
012: 5Aug1998 dl replaced int counters with longs
013: 25aug1998 dl record writer thread
014: 3May1999 dl add notifications on interrupt/timeout
015:
016: */
017:
018: package EDU.oswego.cs.dl.util.concurrent;
019:
020: /**
021: * A ReadWriteLock that prefers waiting writers over
022: * waiting readers when there is contention. This class
023: * is adapted from the versions described in CPJ, improving
024: * on the ones there a bit by segregating reader and writer
025: * wait queues, which is typically more efficient.
026: * <p>
027: * The locks are <em>NOT</em> reentrant. In particular,
028: * even though it may appear to usually work OK,
029: * a thread holding a read lock should not attempt to
030: * re-acquire it. Doing so risks lockouts when there are
031: * also waiting writers.
032: * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
033: **/
034:
035: public class WriterPreferenceReadWriteLock implements ReadWriteLock {
036:
037: protected long activeReaders_ = 0;
038: protected Thread activeWriter_ = null;
039: protected long waitingReaders_ = 0;
040: protected long waitingWriters_ = 0;
041:
042: protected final ReaderLock readerLock_ = new ReaderLock();
043: protected final WriterLock writerLock_ = new WriterLock();
044:
045: public Sync writeLock() {
046: return writerLock_;
047: }
048:
049: public Sync readLock() {
050: return readerLock_;
051: }
052:
053: /*
054: A bunch of small synchronized methods are needed
055: to allow communication from the Lock objects
056: back to this object, that serves as controller
057: */
058:
059: protected synchronized void cancelledWaitingReader() {
060: --waitingReaders_;
061: }
062:
063: protected synchronized void cancelledWaitingWriter() {
064: --waitingWriters_;
065: }
066:
067: /** Override this method to change to reader preference **/
068: protected boolean allowReader() {
069: return activeWriter_ == null && waitingWriters_ == 0;
070: }
071:
072: protected synchronized boolean startRead() {
073: boolean allowRead = allowReader();
074: if (allowRead)
075: ++activeReaders_;
076: return allowRead;
077: }
078:
079: protected synchronized boolean startWrite() {
080:
081: // The allowWrite expression cannot be modified without
082: // also changing startWrite, so is hard-wired
083:
084: boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);
085: if (allowWrite)
086: activeWriter_ = Thread.currentThread();
087: return allowWrite;
088: }
089:
090: /*
091: Each of these variants is needed to maintain atomicity
092: of wait counts during wait loops. They could be
093: made faster by manually inlining each other. We hope that
094: compilers do this for us though.
095: */
096:
097: protected synchronized boolean startReadFromNewReader() {
098: boolean pass = startRead();
099: if (!pass)
100: ++waitingReaders_;
101: return pass;
102: }
103:
104: protected synchronized boolean startWriteFromNewWriter() {
105: boolean pass = startWrite();
106: if (!pass)
107: ++waitingWriters_;
108: return pass;
109: }
110:
111: protected synchronized boolean startReadFromWaitingReader() {
112: boolean pass = startRead();
113: if (pass)
114: --waitingReaders_;
115: return pass;
116: }
117:
118: protected synchronized boolean startWriteFromWaitingWriter() {
119: boolean pass = startWrite();
120: if (pass)
121: --waitingWriters_;
122: return pass;
123: }
124:
125: /**
126: * Called upon termination of a read.
127: * Returns the object to signal to wake up a waiter, or null if no such
128: **/
129: protected synchronized Signaller endRead() {
130: if (--activeReaders_ == 0 && waitingWriters_ > 0)
131: return writerLock_;
132: else
133: return null;
134: }
135:
136: /**
137: * Called upon termination of a write.
138: * Returns the object to signal to wake up a waiter, or null if no such
139: **/
140: protected synchronized Signaller endWrite() {
141: activeWriter_ = null;
142: if (waitingReaders_ > 0 && allowReader())
143: return readerLock_;
144: else if (waitingWriters_ > 0)
145: return writerLock_;
146: else
147: return null;
148: }
149:
150: /**
151: * Reader and Writer requests are maintained in two different
152: * wait sets, by two different objects. These objects do not
153: * know whether the wait sets need notification since they
154: * don't know preference rules. So, each supports a
155: * method that can be selected by main controlling object
156: * to perform the notifications. This base class simplifies mechanics.
157: **/
158:
159: protected abstract class Signaller { // base for ReaderLock and WriterLock
160: abstract void signalWaiters();
161: }
162:
163: protected class ReaderLock extends Signaller implements Sync {
164:
165: public void acquire() throws InterruptedException {
166: if (Thread.interrupted())
167: throw new InterruptedException();
168: InterruptedException ie = null;
169: synchronized (this ) {
170: if (!startReadFromNewReader()) {
171: for (;;) {
172: try {
173: ReaderLock.this .wait();
174: if (startReadFromWaitingReader())
175: return;
176: } catch (InterruptedException ex) {
177: cancelledWaitingReader();
178: ie = ex;
179: break;
180: }
181: }
182: }
183: }
184: if (ie != null) {
185: // fall through outside synch on interrupt.
186: // This notification is not really needed here,
187: // but may be in plausible subclasses
188: writerLock_.signalWaiters();
189: throw ie;
190: }
191: }
192:
193: public void release() {
194: Signaller s = endRead();
195: if (s != null)
196: s.signalWaiters();
197: }
198:
199: synchronized void signalWaiters() {
200: ReaderLock.this .notifyAll();
201: }
202:
203: public boolean attempt(long msecs) throws InterruptedException {
204: if (Thread.interrupted())
205: throw new InterruptedException();
206: InterruptedException ie = null;
207: synchronized (this ) {
208: if (msecs <= 0)
209: return startRead();
210: else if (startReadFromNewReader())
211: return true;
212: else {
213: long waitTime = msecs;
214: long start = System.currentTimeMillis();
215: for (;;) {
216: try {
217: ReaderLock.this .wait(waitTime);
218: } catch (InterruptedException ex) {
219: cancelledWaitingReader();
220: ie = ex;
221: break;
222: }
223: if (startReadFromWaitingReader())
224: return true;
225: else {
226: waitTime = msecs
227: - (System.currentTimeMillis() - start);
228: if (waitTime <= 0) {
229: cancelledWaitingReader();
230: break;
231: }
232: }
233: }
234: }
235: }
236: // safeguard on interrupt or timeout:
237: writerLock_.signalWaiters();
238: if (ie != null)
239: throw ie;
240: else
241: return false; // timed out
242: }
243:
244: }
245:
246: protected class WriterLock extends Signaller implements Sync {
247:
248: public void acquire() throws InterruptedException {
249: if (Thread.interrupted())
250: throw new InterruptedException();
251: InterruptedException ie = null;
252: synchronized (this ) {
253: if (!startWriteFromNewWriter()) {
254: for (;;) {
255: try {
256: WriterLock.this .wait();
257: if (startWriteFromWaitingWriter())
258: return;
259: } catch (InterruptedException ex) {
260: cancelledWaitingWriter();
261: WriterLock.this .notify();
262: ie = ex;
263: break;
264: }
265: }
266: }
267: }
268: if (ie != null) {
269: // Fall through outside synch on interrupt.
270: // On exception, we may need to signal readers.
271: // It is not worth checking here whether it is strictly necessary.
272: readerLock_.signalWaiters();
273: throw ie;
274: }
275: }
276:
277: public void release() {
278: Signaller s = endWrite();
279: if (s != null)
280: s.signalWaiters();
281: }
282:
283: synchronized void signalWaiters() {
284: WriterLock.this .notify();
285: }
286:
287: public boolean attempt(long msecs) throws InterruptedException {
288: if (Thread.interrupted())
289: throw new InterruptedException();
290: InterruptedException ie = null;
291: synchronized (this ) {
292: if (msecs <= 0)
293: return startWrite();
294: else if (startWriteFromNewWriter())
295: return true;
296: else {
297: long waitTime = msecs;
298: long start = System.currentTimeMillis();
299: for (;;) {
300: try {
301: WriterLock.this .wait(waitTime);
302: } catch (InterruptedException ex) {
303: cancelledWaitingWriter();
304: WriterLock.this .notify();
305: ie = ex;
306: break;
307: }
308: if (startWriteFromWaitingWriter())
309: return true;
310: else {
311: waitTime = msecs
312: - (System.currentTimeMillis() - start);
313: if (waitTime <= 0) {
314: cancelledWaitingWriter();
315: WriterLock.this .notify();
316: break;
317: }
318: }
319: }
320: }
321: }
322:
323: readerLock_.signalWaiters();
324: if (ie != null)
325: throw ie;
326: else
327: return false; // timed out
328: }
329:
330: }
331:
332: }
|