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