diff src/EDU/oswego/cs/dl/util/concurrent/WriterPreferenceReadWriteLock.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EDU/oswego/cs/dl/util/concurrent/WriterPreferenceReadWriteLock.java	Sat Mar 21 11:00:06 2009 +0000
@@ -0,0 +1,312 @@
+/*
+  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 EDU.oswego.cs.dl.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_;
+    else
+      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;
+            else {
+              waitTime = msecs - (System.currentTimeMillis() - start);
+              if (waitTime <= 0) {
+                cancelledWaitingReader();
+                break;
+              }
+            }
+          }
+        }
+      }
+      // safeguard on interrupt or timeout:
+      writerLock_.signalWaiters();
+      if (ie != null) throw ie;
+      else 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;
+            else {
+              waitTime = msecs - (System.currentTimeMillis() - start);
+              if (waitTime <= 0) {
+                cancelledWaitingWriter();
+                WriterLock.this.notify();
+                break;
+              }
+            }
+          }
+        }
+      }
+      
+      readerLock_.signalWaiters();
+      if (ie != null) throw ie;
+      else return false; // timed out
+    }
+
+  }
+
+
+
+}
+