/*
* File: WriterPreferenceReadWriteLock.java Originally written by Doug Lea and
* released into the public domain. This may be used for any purposes whatsoever
* without acknowledgment. Thanks for the assistance and support of Sun
* Microsystems Labs, and everyone contributing, testing, and using this code.
* History: Date Who What 11Jun1998 dl Create public version 5Aug1998 dl
* replaced int counters with longs 25aug1998 dl record writer thread 3May1999
* dl add notifications on interrupt/timeout
*/
package org.castor.core.util.concurrent;
/**
* A ReadWriteLock that prefers waiting writers over waiting readers when there
* is contention. This class is adapted from the versions described in CPJ,
* improving on the ones there a bit by segregating reader and writer wait
* queues, which is typically more efficient.
* <p>
* The locks are <em>NOT</em> reentrant. In particular, even though it may
* appear to usually work OK, a thread holding a read lock should not attempt to
* re-acquire it. Doing so risks lockouts when there are also waiting writers.
* <p>[ <a
* href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
* Introduction to this package. </a>]
*/
public class WriterPreferenceReadWriteLock implements ReadWriteLock {
protected long activeReaders_ = 0;
protected Thread activeWriter_ = null;
protected long waitingReaders_ = 0;
protected long waitingWriters_ = 0;
protected final ReaderLock readerLock_ = new ReaderLock();
protected final WriterLock writerLock_ = new WriterLock();
public Sync writeLock() {
return writerLock_;
}
public Sync readLock() {
return readerLock_;
}
/*
* A bunch of small synchronized methods are needed to allow communication
* from the Lock objects back to this object, that serves as controller
*/
protected synchronized void cancelledWaitingReader() {
--waitingReaders_;
}
protected synchronized void cancelledWaitingWriter() {
--waitingWriters_;
}
/** Override this method to change to reader preference * */
protected boolean allowReader() {
return activeWriter_ == null && waitingWriters_ == 0;
}
protected synchronized boolean startRead() {
boolean allowRead = allowReader();
if(allowRead) ++activeReaders_;
return allowRead;
}
protected synchronized boolean startWrite() {
// The allowWrite expression cannot be modified without
// also changing startWrite, so is hard-wired
boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);
if(allowWrite) activeWriter_ = Thread.currentThread();
return allowWrite;
}
/*
* Each of these variants is needed to maintain atomicity of wait counts
* during wait loops. They could be made faster by manually inlining each
* other. We hope that compilers do this for us though.
*/
protected synchronized boolean startReadFromNewReader() {
boolean pass = startRead();
if( !pass) ++waitingReaders_;
return pass;
}
protected synchronized boolean startWriteFromNewWriter() {
boolean pass = startWrite();
if( !pass) ++waitingWriters_;
return pass;
}
protected synchronized boolean startReadFromWaitingReader() {
boolean pass = startRead();
if(pass) --waitingReaders_;
return pass;
}
protected synchronized boolean startWriteFromWaitingWriter() {
boolean pass = startWrite();
if(pass) --waitingWriters_;
return pass;
}
/**
* Called upon termination of a read. Returns the object to signal to wake
* up a waiter, or null if no such
*/
protected synchronized Signaller endRead() {
if( --activeReaders_ == 0 && waitingWriters_ > 0)
return writerLock_;
return null;
}
/**
* Called upon termination of a write. Returns the object to signal to wake
* up a waiter, or null if no such
*/
protected synchronized Signaller endWrite() {
activeWriter_ = null;
if(waitingReaders_ > 0 && allowReader())
return readerLock_;
else if(waitingWriters_ > 0)
return writerLock_;
else
return null;
}
/**
* Reader and Writer requests are maintained in two different wait sets, by
* two different objects. These objects do not know whether the wait sets
* need notification since they don't know preference rules. So, each
* supports a method that can be selected by main controlling object to
* perform the notifications. This base class simplifies mechanics.
*/
protected abstract class Signaller { // base for ReaderLock and WriterLock
abstract void signalWaiters();
}
protected class ReaderLock extends Signaller implements Sync {
public void acquire() throws InterruptedException {
if(Thread.interrupted()) throw new InterruptedException();
InterruptedException ie = null;
synchronized(this) {
if( !startReadFromNewReader()) {
for(;;) {
try {
ReaderLock.this.wait();
if(startReadFromWaitingReader()) return;
} catch(InterruptedException ex) {
cancelledWaitingReader();
ie = ex;
break;
}
}
}
}
if(ie != null) {
// fall through outside synch on interrupt.
// This notification is not really needed here,
// but may be in plausible subclasses
writerLock_.signalWaiters();
throw ie;
}
}
public void release() {
Signaller s = endRead();
if(s != null) s.signalWaiters();
}
synchronized void signalWaiters() {
ReaderLock.this.notifyAll();
}
public boolean attempt(long msecs) throws InterruptedException {
if(Thread.interrupted()) throw new InterruptedException();
InterruptedException ie = null;
synchronized(this) {
if(msecs <= 0)
return startRead();
else if(startReadFromNewReader())
return true;
else {
long waitTime = msecs;
long start = System.currentTimeMillis();
for(;;) {
try {
ReaderLock.this.wait(waitTime);
} catch(InterruptedException ex) {
cancelledWaitingReader();
ie = ex;
break;
}
if(startReadFromWaitingReader())
return true;
waitTime = msecs
- (System.currentTimeMillis() - start);
if(waitTime <= 0) {
cancelledWaitingReader();
break;
}
}
}
}
// safeguard on interrupt or timeout:
writerLock_.signalWaiters();
if(ie != null)
throw ie;
return false; // timed out
}
}
protected class WriterLock extends Signaller implements Sync {
public void acquire() throws InterruptedException {
if(Thread.interrupted()) throw new InterruptedException();
InterruptedException ie = null;
synchronized(this) {
if( !startWriteFromNewWriter()) {
for(;;) {
try {
WriterLock.this.wait();
if(startWriteFromWaitingWriter()) return;
} catch(InterruptedException ex) {
cancelledWaitingWriter();
WriterLock.this.notify();
ie = ex;
break;
}
}
}
}
if(ie != null) {
// Fall through outside synch on interrupt.
// On exception, we may need to signal readers.
// It is not worth checking here whether it is strictly
// necessary.
readerLock_.signalWaiters();
throw ie;
}
}
public void release() {
Signaller s = endWrite();
if(s != null) s.signalWaiters();
}
synchronized void signalWaiters() {
WriterLock.this.notify();
}
public boolean attempt(long msecs) throws InterruptedException {
if(Thread.interrupted()) throw new InterruptedException();
InterruptedException ie = null;
synchronized(this) {
if(msecs <= 0)
return startWrite();
else if(startWriteFromNewWriter())
return true;
else {
long waitTime = msecs;
long start = System.currentTimeMillis();
for(;;) {
try {
WriterLock.this.wait(waitTime);
} catch(InterruptedException ex) {
cancelledWaitingWriter();
WriterLock.this.notify();
ie = ex;
break;
}
if(startWriteFromWaitingWriter())
return true;
waitTime = msecs
- (System.currentTimeMillis() - start);
if(waitTime <= 0) {
cancelledWaitingWriter();
WriterLock.this.notify();
break;
}
}
}
}
readerLock_.signalWaiters();
if(ie != null)
throw ie;
return false; // timed out
}
}
}