diff options
Diffstat (limited to 'java/JACE/Concurrency/RWMutex.java')
-rw-r--r-- | java/JACE/Concurrency/RWMutex.java | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/java/JACE/Concurrency/RWMutex.java b/java/JACE/Concurrency/RWMutex.java new file mode 100644 index 00000000000..abb30ce3bc8 --- /dev/null +++ b/java/JACE/Concurrency/RWMutex.java @@ -0,0 +1,268 @@ +package JACE.Concurrency; + +import java.util.*; +import JACE.ASX.*; + +/** + * A read/write lock allows multiple + * readers or a single writer to access the guarded element. + * <P> + * <EM>This class does not support recursive semantics.</EM> + */ +public class RWMutex extends LockAdapter +{ + public synchronized int tryAcquire () + { + if (referenceCount_ == 0) { + referenceCount_ = -1; + setOwner (); + return AbstractLock.SUCCESS; + } else + return AbstractLock.FAILURE; + } + + public synchronized int tryAcquireRead () + { + if (referenceCount_ > -1 && waiters_.size () == 0) { + referenceCount_++; + setOwner (); + return AbstractLock.SUCCESS; + } else + return AbstractLock.FAILURE; + } + + public int acquire(TimeValue timeout) + throws TimeoutException, InterruptedException + { + return acquireWrite(timeout); + } + + public void waitUntilIsOwner (RWWaitObject waitObj, TimeValue timeout) + throws TimeoutException, InterruptedException + { + boolean exceptionOccured = true; + try { + sleepHook (); + synchronized (waitObj) { + waitObj.timedWait (timeout); + } + exceptionOccured = false; + } finally { + + synchronized (this) { + + if (exceptionOccured) { + if (!waiters_.removeElement (waitObj)) { + setOwner (); + release (); + } + } else + setOwner(); + } + } + } + + public int acquireRead(TimeValue timeout) + throws TimeoutException, InterruptedException + { + RWWaitObject waitObj = null; + + synchronized (this) { + + if (referenceCount_ > -1 && waiters_.size () == 0) { + referenceCount_++; + setOwner (); + return AbstractLock.SUCCESS; + } + + waitObj = new RWWaitObject (true); + + waiters_.addElement (waitObj); + } + + waitUntilIsOwner (waitObj, timeout); + + return AbstractLock.SLEEPHOOK; + } + + public int acquireWrite(TimeValue timeout) + throws TimeoutException, InterruptedException + { + RWWaitObject waitObj = null; + + synchronized (this) { + + if (referenceCount_ == 0) { + referenceCount_ = -1; + setOwner (); + return AbstractLock.SUCCESS; + } + + waitObj = new RWWaitObject (false); + + waiters_.addElement (waitObj); + } + + waitUntilIsOwner (waitObj, timeout); + + // When the writer gets here, it has been cleared to go by + // whatever thread specifically gave control to this writer in + // release. The referenceCount_ and numberOfWaitingWriters_ + // variables are also adjusted by the releasing thread since + // it already has a synchronization lock. Not doing that, + // and then having another synchronized (this) block in here + // could lead to a situation in which another thread sneaks + // in inbetween when this thread leaves timedWait and goes to + // adjust them. + + return AbstractLock.SLEEPHOOK; + } + + + public synchronized int release () + { + if (!isOwner ()) + return AbstractLock.FAILURE; + + clearOwner (); + + // Releasing a reader. + if (referenceCount_ > 0) { + referenceCount_--; + + if (referenceCount_ != 0) + return AbstractLock.SUCCESS; + + } else { + // releasing a writer + referenceCount_ = 0; + } + + if (waiters_.size () == 0) + return AbstractLock.SUCCESS; + + if (releaseFirstReaders () == 0) { + RWWaitObject waitObj = (RWWaitObject)waiters_.firstElement (); + waiters_.removeElementAt (0); + + referenceCount_ = -1; + + waitObj.condition (true); + synchronized (waitObj) { + waitObj.signal (); + } + } + + return AbstractLock.SUCCESS; + } + + // Releases all waiting readers up to the first waiting writer + // or the end of the queue. Returns the number of readers + // released. + protected int releaseFirstReaders () + { + int releasedReaders = 0; + + do { + + RWWaitObject waitObj = (RWWaitObject)waiters_.firstElement (); + if (!waitObj.isReader ()) + break; + + waiters_.removeElementAt (0); + + referenceCount_++; + releasedReaders++; + + waitObj.condition (true); + synchronized (waitObj) { + waitObj.signal (); + } + + } while (waiters_.size () > 0); + + return releasedReaders; + } + + public int renew (int requeuePosition, + JACE.ASX.TimeValue timeout) + throws InterruptedException, + TimeoutException + { + RWWaitObject waitObj = null; + + synchronized (this) { + + if (!isOwner ()) + return AbstractLock.FAILURE; + + if (requeuePosition == 0 || waiters_.size () == 0) + return AbstractLock.SUCCESS; + + waitObj = new RWWaitObject (referenceCount_ > 0); + + if (requeuePosition < 0 || requeuePosition > waiters_.size ()) { + requeuePosition = waiters_.size (); + } + + waiters_.insertElementAt (waitObj, requeuePosition); + + release (); + } + + waitUntilIsOwner (waitObj, timeout); + + // When the writer gets here, it has been cleared to go by + // whatever thread specifically gave control to this writer in + // release. The referenceCount_ and numberOfWaitingWriters_ + // variables are also adjusted by the releasing thread since + // it already has a synchronization lock. Not doing that, + // and then having another synchronized (this) block in here + // could lead to a situation in which another thread sneaks + // in inbetween when this thread leaves timedWait and goes to + // adjust them. + + return AbstractLock.SUCCESS; + } + + static class RWWaitObject extends WaitObject + { + public RWWaitObject (boolean isReader) + { + isReader_ = isReader; + } + + public boolean isReader () + { + return isReader_; + } + + private boolean isReader_ = false; + } + + protected boolean isOwner () + { + return owners_.containsKey (accessorID()); + } + + protected void setOwner () + { + owners_.put (accessorID(), this); + } + + protected void clearOwner () + { + owners_.remove (accessorID()); + } + + private Vector waiters_ = new Vector (); + + private int referenceCount_ = 0; + // Value is -1 if writer has the lock, else this keeps track of the + // number of readers holding the lock. + + private Hashtable owners_ = new Hashtable (); + + private int nestingLevel_ = 0; +} + |