/*
* File: ReentrantWriterPreferenceReadWriteLock.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 26aug1998 dl Create public version 7sep2000 dl
* Readers are now also reentrant 19jan2001 dl Allow read->write upgrades if the
* only reader 10dec2002 dl Throw IllegalStateException on extra release
*/
package org.castor.core.util.concurrent;
import java.util.HashMap;
/**
* A writer-preference ReadWriteLock that allows both readers and writers to
* reacquire read or write locks in the style of a ReentrantLock. Readers are
* not allowed until all write locks held by the writing thread have been
* released. Among other applications, reentrancy can be useful when write locks
* are held during calls or callbacks to methods that perform reads under read
* locks.
* <p>
* <b>Sample usage </b>. Here is a code sketch showing how to exploit reentrancy
* to perform lock downgrading after updating a cache:
*
* <pre>
*
* class CachedData {
* Object data;
* volatile boolean cacheValid;
* ReentrantWriterPreferenceReadWriteLock rwl = ...
*
* void processCachedData() {
* rwl.readLock().acquire();
* if (!cacheValid) {
*
* // upgrade lock:
* rwl.readLock().release(); // must release first to obtain writelock
* rwl.writeLock().acquire();
* if (!cacheValid) { // recheck
* data = ...
* cacheValid = true;
* }
* // downgrade lock
* rwl.readLock().acquire(); // reacquire read without giving up lock
* rwl.writeLock().release(); // release write, still hold read
* }
*
* use(data);
* rwl.readLock().release();
* }
* }
*
* </pre>
*
* <p>[ <a
* href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
* Introduction to this package. </a>]
*
* @see "java.util.concurrent.locks.ReentrantLock in Java 5 or later JavaDoc"
*/
public class ReentrantWriterPreferenceReadWriteLock
extends WriterPreferenceReadWriteLock {
/** Number of acquires on write lock by activeWriter_ thread * */
protected long writeHolds_ = 0;
/** Number of acquires on read lock by any reader thread * */
protected HashMap readers_ = new HashMap();
/** cache/reuse the special Integer value one to speed up readlocks * */
protected static final Integer IONE = new Integer(1);
protected boolean allowReader() {
return (activeWriter_ == null && waitingWriters_ == 0)
|| activeWriter_ == Thread.currentThread();
}
protected synchronized boolean startRead() {
Thread t = Thread.currentThread();
Object c = readers_.get(t);
if(c != null) { // already held -- just increment hold count
readers_.put(t, new Integer(((Integer)(c)).intValue() + 1));
++activeReaders_;
return true;
} else if(allowReader()) {
readers_.put(t, IONE);
++activeReaders_;
return true;
} else
return false;
}
protected synchronized boolean startWrite() {
if(activeWriter_ == Thread.currentThread()) { // already held;
// re-acquire
++writeHolds_;
return true;
} else if(writeHolds_ == 0) {
if(activeReaders_ == 0
|| (readers_.size() == 1 && readers_.get(Thread
.currentThread()) != null)) {
activeWriter_ = Thread.currentThread();
writeHolds_ = 1;
return true;
}
return false;
} else
return false;
}
protected synchronized Signaller endRead() {
Thread t = Thread.currentThread();
Object c = readers_.get(t);
if(c == null) throw new IllegalStateException();
--activeReaders_;
if(c != IONE) { // more than one hold; decrement count
int h = ((Integer)(c)).intValue() - 1;
Integer ih = (h == 1) ? IONE : new Integer(h);
readers_.put(t, ih);
return null;
}
readers_.remove(t);
if(writeHolds_ > 0) // a write lock is still held by current thread
return null;
else if(activeReaders_ == 0 && waitingWriters_ > 0)
return writerLock_;
else
return null;
}
protected synchronized Signaller endWrite() {
--writeHolds_;
if(writeHolds_ > 0) // still being held
return null;
activeWriter_ = null;
if(waitingReaders_ > 0 && allowReader())
return readerLock_;
else if(waitingWriters_ > 0)
return writerLock_;
else
return null;
}
}