Index: openide/src/org/openide/util/Mutex.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/Mutex.java,v retrieving revision 1.56 diff -u -r1.56 Mutex.java --- openide/src/org/openide/util/Mutex.java 27 Feb 2003 23:41:08 -0000 1.56 +++ openide/src/org/openide/util/Mutex.java 27 Mar 2003 23:03:57 -0000 @@ -24,16 +24,10 @@ * can be read by several readers at once but only written by one writer. *
* It is guaranteed that if you are a writer you can also enter the -* mutex as a reader. Conversely, if you are the only reader you -* are allowed to enter the mutex as a writer. +* mutex as a reader. But you cannot enter the write mutex if you hold +* the read mutex, since that can cause deadlocks. *
-* If the mutex is used only by one thread, the thread can repeatedly -* enter it as a writer or reader. So one thread can never deadlock itself, -* whichever order operations are performed in. -*
-* There is no strategy to prevent starvation. -* Even if there is a writer waiting to enter, another reader might enter -* the section instead. + * This implementation will not starve a writer or reader indefinitely. *
* Examples of use:
*
@@ -61,52 +55,14 @@
* }
*
*
-* @author Ales Novak
+* @author Ales Novak, Jesse Glick
*/
public final class Mutex extends Object {
+
/** Mutex that allows code to be synchronized with the AWT event dispatch thread. */
public static final Mutex EVENT = new Mutex ();
-
- // lock mode constants
- /** Lock free */
- private static final int NONE = 0x0;
- /** Enqueue all requests */
- private static final int CHAIN = 0x1;
- /** eXclusive */
- private static final int X = 0x2;
- /** Shared */
- private static final int S = 0x3;
-
- /** number of modes */
- private static final int MODE_COUNT = 0x4;
-
- /** compatibility matrix */ // [requested][granted]
- private static final boolean[][] cmatrix = {null, null, // NONE, CHAIN
- /* NONE */ /* CHAIN */ /* X */ /* S */ // granted
- /*r X */ {true, false, false, false},
- /*e S */ {true, false, false, true}
- /*q */
- //uested
- };
-
- /** Decides whether two locks are compatible.?
- * @param granted?
- * @param requested?
- * @return true iff they are compatible?
- */
- private static boolean compatibleLocks(int granted, int requested) {
- return cmatrix[requested][granted];
- }
- /** granted mode */
- private int grantedMode = NONE;
- /** protects internal data structures */
- private /*final*/ Object LOCK;
- /** threads that - owns or waits for this mutex */
- private /*final*/ Map registeredThreads;
- /** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") */ // NOI18N
- private int readersNo = 0;
- /** a queue of waiting threads for this mutex */
- private List waiters;
+
+ private static final RequestProcessor LATER = new RequestProcessor("Mutex"); // NOI18N
/** Enhanced constructor that permits specifying an object to use as a lock.
* The lock is used on entry and exit to {@link #readAccess} and during the
@@ -117,57 +73,41 @@
* @param lock lock to use
*/
public Mutex (Object lock) {
- init(lock);
+ this.lock = lock;
}
/** Default constructor.
*/
public Mutex() {
- init(new InternalLock());
+ this(new InternalLock());
}
/** @param privileged can enter privileged states of this Mutex
* This helps avoid creating of custom Runnables.
*/
public Mutex(Privileged privileged) {
+ this();
if (privileged == null) {
throw new IllegalArgumentException("privileged == null"); //NOI18N
} else {
- init(new InternalLock());
privileged.setParent(this);
}
}
- /** Initiates this Mutex */
- private void init(Object lock) {
- this.LOCK = lock;
- this.registeredThreads = new HashMap(7);
- this.waiters = new LinkedList();
- }
-
/** Run an action only with read access.
* See class description re. entering for write access within the dynamic scope.
* @param action the action to perform
* @return the object returned from {@link Mutex.Action#run}
*/
public Object readAccess (Action action) {
-
if (this == EVENT) {
- try {
- return doEventAccess (action);
- } catch (MutexException e) {
- InternalError err = new InternalError("Exception from non-Exception Action"); // NOI18N
- ErrorManager.getDefault().annotate(err, e.getException());
- throw err;
- }
+ return eventAccess(action);
}
-
- Thread t = Thread.currentThread();
- readEnter(t);
+ enterReadAccess();
try {
return action.run();
} finally {
- leave(t);
+ exitReadAccess();
}
}
@@ -196,49 +136,55 @@
* @see #readAccess(Mutex.Action)
*/
public Object readAccess (ExceptionAction action) throws MutexException {
-
if (this == EVENT) {
- return doEventAccess (action);
+ return eventAccess(action);
}
-
- Thread t = Thread.currentThread();
- readEnter(t);
+ enterReadAccess();
try {
return action.run();
- } catch (RuntimeException e) {
- throw e;
} catch (Exception e) {
throw new MutexException(e);
- } catch (LinkageError e) {
- // #20467
- throw new MutexException(new InvocationTargetException(e));
- } catch (StackOverflowError e) {
- // #20467
- throw new MutexException(new InvocationTargetException(e));
} finally {
- leave(t);
+ exitReadAccess();
}
}
/** Run an action with read access, returning no result.
- * It may be run asynchronously.
- *
+ * It may be run asynchronously (or not) at the discretion of the implementation.
+ * This implementation tries to run if synchronously if it will not have to wait
+ * for a lock, else runs it at some later point in the future.
* @param action the action to perform
* @see #readAccess(Mutex.Action)
*/
public void readAccess (final Runnable action) {
-
if (this == EVENT) {
- doEvent (action);
+ eventAccess(action, true);
return;
}
-
- Thread t = Thread.currentThread();
- readEnter(t);
- try {
- action.run();
- } finally {
- leave(t);
+ boolean synch;
+ synchronized (lock) {
+ synch = semaphore >= 0;
+ if (synch) {
+ enterReadAccess();
+ }
+ }
+ if (synch) {
+ try {
+ action.run();
+ } finally {
+ exitReadAccess();
+ }
+ } else {
+ LATER.post(new Runnable() {
+ public void run() {
+ enterReadAccess();
+ try {
+ action.run();
+ } finally {
+ exitReadAccess();
+ }
+ }
+ });
}
}
@@ -250,23 +196,14 @@
* @return the result of {@link Mutex.Action#run}
*/
public Object writeAccess (Action action) {
-
if (this == EVENT) {
- try {
- return doEventAccess (action);
- } catch (MutexException e) {
- InternalError err = new InternalError("Exception from non-Exception Action"); // NOI18N
- ErrorManager.getDefault().annotate(err, e.getException());
- throw err;
- }
+ return eventAccess(action);
}
-
- Thread t = Thread.currentThread();
- writeEnter(t);
+ enterWriteAccess();
try {
return action.run();
} finally {
- leave(t);
+ exitWriteAccess();
}
}
@@ -292,50 +229,56 @@
* @see #readAccess(Mutex.ExceptionAction)
*/
public Object writeAccess (ExceptionAction action) throws MutexException {
-
if (this == EVENT) {
- return doEventAccess (action);
+ return eventAccess(action);
}
-
- Thread t = Thread.currentThread();
- writeEnter(t);
+ enterWriteAccess();
try {
return action.run();
- } catch (RuntimeException e) {
- throw e;
} catch (Exception e) {
throw new MutexException(e);
- } catch (LinkageError e) {
- // #20467
- throw new MutexException(new InvocationTargetException(e));
- } catch (StackOverflowError e) {
- // #20467
- throw new MutexException(new InvocationTargetException(e));
} finally {
- leave(t);
+ exitWriteAccess();
}
}
/** Run an action with write access and return no result.
- * It may be run asynchronously.
- *
+ * It may be run asynchronously (or not) at the discretion of the implementation.
+ * This implementation tries to run if synchronously if it will not have to wait
+ * for a lock, else runs it at some later point in the future.
* @param action the action to perform
* @see #writeAccess(Mutex.Action)
* @see #readAccess(Runnable)
*/
public void writeAccess (final Runnable action) {
-
if (this == EVENT) {
- doEvent (action);
+ eventAccess(action, true);
return;
}
-
- Thread t = Thread.currentThread();
- writeEnter(t);
- try {
- action.run();
- } finally {
- leave(t);
+ boolean synch;
+ synchronized (lock) {
+ synch = semaphore == 0 || canWrite();
+ if (synch) {
+ enterWriteAccess();
+ }
+ }
+ if (synch) {
+ try {
+ action.run();
+ } finally {
+ exitWriteAccess();
+ }
+ } else {
+ LATER.post(new Runnable() {
+ public void run() {
+ enterWriteAccess();
+ try {
+ action.run();
+ } finally {
+ exitWriteAccess();
+ }
+ }
+ });
}
}
@@ -355,7 +298,28 @@
* @param run runnable to run
*/
public void postReadRequest (final Runnable run) {
- postRequest(S, run);
+ if (this == EVENT) {
+ eventAccess(run, false);
+ return;
+ }
+ if (canWrite()) {
+ synchronized (lock) {
+ ThreadInfo ti = currentThreadInfo();
+ if (ti == null) throw new IllegalStateException();
+ if (!ti.writer) throw new IllegalStateException();
+ if (ti.lateReadActions == null) {
+ ti.lateReadActions = new LinkedList();
+ }
+ ti.lateReadActions.add(run);
+ }
+ } else {
+ enterReadAccess();
+ try {
+ run.run();
+ } finally {
+ exitReadAccess();
+ }
+ }
}
/** Posts a write request. This request runs immediately iff
@@ -372,613 +336,349 @@
* @param run runnable to run
*/
public void postWriteRequest (Runnable run) {
- postRequest(X, run);
- }
-
- /** toString */
- public String toString() {
if (this == EVENT) {
- return "Mutex.EVENT"; // NOI18N
- }
-
- String newline = System.getProperty("line.separator");
- StringBuffer sbuff = new StringBuffer(512);
- synchronized(LOCK){
- sbuff.append("threads: ").append(registeredThreads).append(newline); // NOI18N
- sbuff.append("readersNo: ").append(readersNo).append(newline); // NOI18N
- sbuff.append("waiters: ").append(waiters).append(newline); // NOI18N
- sbuff.append("grantedMode: ").append(grantedMode).append(newline); // NOI18N
+ eventAccess(run, false);
+ return;
}
- return sbuff.toString();
- }
-
- // priv methods -----------------------------------------
-
- /** enters this mutex for writing */
- private void writeEnter(Thread t) {
- enter(X, t, true);
- }
- /** enters this mutex for reading */
- private void readEnter(Thread t) {
- enter(S, t, true);
- }
-
- /** enters this mutex with given mode
- * @param requested one of S, X
- * @param t
- */
- private boolean enter(int requested, Thread t, boolean block) {
-
- QueueCell cell = null;
- int loopc = 0;
-
- for (;;) {
- loopc++;
- synchronized (LOCK) {
- // does the thread reenter this mutex?
- ThreadInfo info = getThreadInfo(t);
-
- if (info != null) {
- if (grantedMode == NONE) {
- // defensive
- throw new IllegalStateException();
+ if (canWrite()) {
+ boolean synch;
+ synchronized (lock) {
+ ThreadInfo ti = currentThreadInfo();
+ if (ti == null) throw new IllegalStateException();
+ if (!ti.writer) throw new IllegalStateException();
+ synch = ti.extraReads == 0;
+ if (!synch) {
+ if (ti.lateWriteActions == null) {
+ ti.lateWriteActions = new LinkedList();
}
- // reenters
- // requested == S -> always succeeds
- // info.mode == X -> always succeeds
- if (((info.mode == S) && (grantedMode == X)) ||
- ((info.mode == X) && (grantedMode == S))) {
- // defensive
- throw new IllegalStateException();
- }
- if ((info.mode == X) ||
- (info.mode == requested)) { // X - X, X - S, S - S
- if (info.forced) {
- info.forced = false;
- } else {
- info.counts[requested]++;
- if ((requested == S) && (info.counts[requested] == 1)) {
- readersNo++;
- info.stamp = readersNo;
- }
- }
- return true;
- } else if (canUpgrade(info.mode, requested)) { // S - X and no holders
- info.mode = X;
- info.counts[requested]++;
- info.rsnapshot = info.counts[S];
- if (grantedMode == S) {
- grantedMode = X;
- } else if (grantedMode == X) {
- // defensive
- throw new IllegalStateException();
- } // else if grantedMode == CHAIN - let it be
-
- return true;
- } else { // S - X and holders
- if (Boolean.getBoolean("netbeans.debug.threads")) { // NOI18N
- System.err.println("WARNING: Going from readAccess to writeAccess");
- Thread.dumpStack();
- }
- // chain follows
- }
- } else { // first acquisition
- if (isCompatible(requested)) { // NONE -> S,X or S -> S
- grantedMode = requested;
- registeredThreads.put(t, info = new ThreadInfo(t, requested));
- if (requested == S) {
- readersNo++;
- info.stamp = readersNo;
- }
- return true;
- } // else {
- // granted is S and requested is X
- // granted is X and requested is S or X
- //}
- }
-
- if (! block) {
- return false;
+ ti.lateWriteActions.add(run);
}
- grantedMode = CHAIN;
- cell = chain(requested, t, 0);
- } // sync
- cell.sleep();
- } // for
- }
-
- /** privilegedEnter serves for processing posted requests */
- private boolean reenter(Thread t, int mode, int stamp) {
-
- // from leaveX -> grantedMode is NONE or S
- if (mode == S) {
- if (grantedMode != NONE && grantedMode != S) {
- throw new IllegalStateException(this.toString());
}
- enter(mode, t, true);
- return false;
- }
-
- // assert (mode == X)
-
- ThreadInfo tinfo = getThreadInfo(t);
- boolean chainFromLeaveX = (grantedMode == CHAIN && tinfo != null && tinfo.counts[X] > 0);
-
- // process grantedMode == X or CHAIN from leaveX OR grantedMode == NONE from leaveS
- if (grantedMode == X || grantedMode == NONE || chainFromLeaveX) {
- enter(mode, t, true);
- return false;
- } else { // remains grantedMode == CHAIN or S from leaveS, so it will be CHAIN
- if (readersNo == 0) {
- throw new IllegalStateException(this.toString());
- }
- ThreadInfo info = new ThreadInfo(t, mode);
- registeredThreads.put(t, info);
- // prevent from grantedMode == NONE (another thread - leaveS)
- readersNo += 2;
- info.stamp = stamp;
- // prevent from new readers
- grantedMode = CHAIN;
- return true;
- } // else X means ERROR!!!
- }
-
- /** @param t holds S (one entry) and wants X, grantedMode != NONE && grantedMode != X */
- private void privilegedEnter(Thread t, int mode) {
- boolean decrease = true;
- ThreadInfo info;
-
- synchronized (LOCK) {
- info = getThreadInfo(t);
- }
-
- for (;;) {
- QueueCell cell;
- synchronized (LOCK) {
-
- if (decrease) {
- decrease = false;
- readersNo -= 2;
- }
-
- // always chain this thread
- // since there can be another one
- // in the queue with higher priority
- grantedMode = CHAIN;
- cell = chain(mode, t, Integer.MAX_VALUE - info.stamp);
-
- if (readersNo == 0) { // seems I may enter
- // no one has higher prio?
- if (waiters.get(0) == cell) {
- waiters.remove(0);
- return;
- } else {
- grantedMode = NONE;
- wakeUpOthers();
- }
- }
- } // synchronized (LOCK)
- cell.sleep();
- // cell already removed from waiters here
- }
- }
-
- /** Leaves this mutex */
- private void leave(Thread t) {
- ThreadInfo info;
- int postedMode = NONE;
- boolean needLock = false;
-
- synchronized (LOCK) {
- info = getThreadInfo(t);
-
- switch (grantedMode) {
- case NONE:
- throw new IllegalStateException();
-
- case CHAIN:
- if (info.counts[X] > 0) {
- // it matters that X is handled first - see ThreadInfo.rsnapshot
- postedMode = leaveX(info);
- } else if (info.counts[S] > 0) {
- postedMode = leaveS(info);
- } else {
- throw new IllegalStateException();
- }
- break;
-
- case X:
- postedMode = leaveX(info);
- break;
-
- case S:
- postedMode = leaveS(info);
- break;
- } // switch
-
- // do not give up LOCK until queued runnables are run
- if (postedMode != NONE) {
- int runsize = info.getRunnableCount(postedMode);
- if (runsize != 0) {
- needLock = reenter(t, postedMode, info.stamp); // grab lock
- }
+ if (synch) {
+ run.run();
}
- } // sync
-
- // check posted requests
- if (postedMode != NONE && info.getRunnableCount(postedMode) > 0) {
+ } else if (canRead()) {
+ throw new IllegalStateException("Cannot call postWriteRequest while holding a read mutex; consider using writeAccess(Runnable)"); // NOI18N
+ } else {
+ enterWriteAccess();
try {
- if (needLock) { // go from S to X or CHAIN
- privilegedEnter(t, postedMode);
- }
- // holds postedMode lock here
- List runnables = info.dequeue(postedMode);
- final int size = runnables.size();
- for (int i = 0; i < size; i++) {
- try {
- Runnable r = (Runnable) runnables.get(i);
- r.run();
- } catch (Exception e) {
- ErrorManager.getDefault().notify(e);
- } catch (LinkageError e) {
- // #20467
- ErrorManager.getDefault().notify(e);
- } catch (StackOverflowError e) {
- // #20467
- ErrorManager.getDefault().notify(e);
- } // try
- } // for
- // help gc
- runnables = null;
+ run.run();
} finally {
- leave(t); // release lock grabbed - shared
+ exitWriteAccess();
}
- } // mode
- }
-
- /** Leaves the lock supposing that info.counts[X] is greater than zero */
- private int leaveX(ThreadInfo info) {
-
- if ((info.counts[X] <= 0) ||
- (info.rsnapshot > info.counts[S])) {
- // defensive
- throw new IllegalStateException();
}
+ }
- if (info.rsnapshot == info.counts[S]) {
- info.counts[X]--;
- if (info.counts[X] == 0) {
- info.rsnapshot = 0;
- // downgrade the lock
- if (info.counts[S] > 0) {
- info.mode = grantedMode = S;
- } else {
- info.mode = grantedMode = NONE;
- registeredThreads.remove(info.t);
- }
-
- if (info.getRunnableCount(S) > 0) {
- return S;
- }
-
- // mode has changed
- wakeUpOthers();
- }
- } else {
- // rsnapshot < counts[S]
-
- if (info.counts[S] <= 0) {
- // defensive
- throw new IllegalStateException();
- }
-
- if (--info.counts[S] == 0) {
- if (readersNo <= 0) {
- throw new IllegalStateException();
+ /**
+ * Enter the read mutex.
+ * @see Mutex.Privileged#enterReadAccess
+ */
+ void enterReadAccess() {
+ synchronized (lock) {
+ ThreadInfo ti = currentThreadInfo();
+ if (ti != null) {
+ // Already in read or write mutex, can certainly enter read.
+ ti.extraReads++;
+ } else {
+ // ti == null, entering fresh.
+ // Wait for any writers to exit.
+ while (semaphore < 0) {
+ try {
+ lock.wait();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e.toString());
+ }
}
- readersNo--;
- return X;
+ // Uncontended or shared, go ahead.
+ new ThreadInfo(false).register();
+ semaphore++;
}
}
-
- return NONE;
}
- /** Leaves the lock supposing that info.counts[S] is greater than zero */
- private int leaveS(ThreadInfo info) {
- if ((info.counts[S] <= 0) ||
- (info.counts[X] > 0)) {
- // defensive
- throw new IllegalStateException();
- }
-
- info.counts[S]--;
- if (info.counts[S] == 0) {
-
- // remove the thread
- info.mode = NONE;
- registeredThreads.remove(info.t);
-
- // downsize readersNo
- if (readersNo <= 0) {
- throw new IllegalStateException();
- }
- readersNo--;
-
- if (readersNo == 0) {
- // set grantedMode to NONE
- // and then wakeUp others - either immediately
- // or in privelegedEnter()
- grantedMode = NONE;
-
- if (info.getRunnableCount(X) > 0) {
- return X;
- }
-
- wakeUpOthers();
- } else if (info.getRunnableCount(X) > 0) {
- return X;
- } else if ((grantedMode == CHAIN) &&
- (readersNo == 1)) {
- // can be the mode advanced from CHAIN? Examine first item of waiters!
-
- for (int i = 0; i < waiters.size(); i++) {
- QueueCell qc = (QueueCell) waiters.get(i);
- synchronized (qc) {
- if (qc.isGotOut()) {
- waiters.remove(i--);
- continue;
- }
-
- ThreadInfo tinfo = getThreadInfo(qc.t);
-
- if (tinfo != null) {
- if (tinfo.mode == S) {
- if (qc.mode != X) {
- // defensive
- throw new IllegalStateException();
- }
-
- if (waiters.size() == 1) {
- grantedMode = X;
- } // else let CHAIN
- tinfo.mode = X;
- waiters.remove(i);
- qc.wakeMeUp();
- }
- } // else first request is a first X request of some thread
- break;
- } // sync (qc)
- } // for
- } // else
- } // count[S] == 0
-
- return NONE;
- }
-
- /** Adds this thread to the queue of waiting threads
- * @warning LOCK must be held
- */
- private QueueCell chain(final int requested, final Thread t, final int priority) {
-
- //long timeout = 0;
-
- /*
- if (killDeadlocksOn) {
- checkDeadlock(requested, t);
- timeout = (isDispatchThread() || checkAwtTreeLock() ? TIMEOUT : 0);
- }
- */
-
- QueueCell qc = new QueueCell(requested, t);
- //qc.timeout = timeout;
- qc.priority2 = priority;
-
- final int size = waiters.size();
- if (size == 0) {
- waiters.add(qc);
- } else {
- QueueCell cursor;
- int i = 0;
- do {
- cursor = (QueueCell) waiters.get(i);
- if (cursor.getPriority() < qc.getPriority()) {
- waiters.add(i, qc);
- break;
+ /**
+ * Exit the read mutex.
+ * @see Mutex.Privileged#exitReadAccess
+ */
+ void exitReadAccess() {
+ List lateWriteActions = null;
+ synchronized (lock) {
+ ThreadInfo ti = currentThreadInfo();
+ if (ti == null) throw new IllegalStateException();
+ if (ti.extraReads > 0) {
+ // Just mark them off.
+ ti.extraReads--;
+ if (ti.extraReads == 0 && ti.writer && ti.lateWriteActions != null) {
+ // We are a writer who has just finished nested reads and now can
+ // run some late write actions.
+ if (semaphore != -1) throw new IllegalStateException();
+ lateWriteActions = ti.lateWriteActions;
+ ti.lateWriteActions = null;
}
- i++;
- } while (i < size);
- if (i == size) {
- waiters.add(qc);
+ } else {
+ // Really exiting.
+ if (ti.writer) throw new IllegalStateException();
+ if (semaphore <= 0) throw new IllegalStateException();
+ if (ti.extraWrites > 0) throw new IllegalStateException();
+ ti.unregister();
+ semaphore--;
+ lock.notifyAll();
+ if (semaphore == 0 && !threads.isEmpty()) throw new IllegalStateException(threads.toString());
+ }
+ }
+ if (lateWriteActions != null) {
+ // We exited the lock, but since are a writer no one else can enter
+ // anything, so that is safe.
+ Iterator it = lateWriteActions.iterator();
+ while (it.hasNext()) {
+ Runnable r = (Runnable)it.next();
+ r.run();
}
}
- return qc;
}
- /** Scans through waiters and wakes up them */
- private void wakeUpOthers() {
-
- if ((grantedMode == X) ||
- (grantedMode == CHAIN)) {
- // defensive
- throw new IllegalStateException();
- }
-
- if (waiters.size() == 0) {
- return;
- }
-
- for (int i = 0; i < waiters.size(); i++) {
- QueueCell qc = (QueueCell) waiters.get(i);
-
- synchronized (qc) {
- if (qc.isGotOut()) {
- // bogus waiter
- waiters.remove(i--);
- continue;
+ /**
+ * Enter the write mutex.
+ * @see Mutex.Privileged#enterWriteAccess
+ */
+ void enterWriteAccess() {
+ synchronized (lock) {
+ ThreadInfo ti = currentThreadInfo();
+ if (ti != null) {
+ if (ti.writer) {
+ // Already in write mutex, can reenter freely.
+ ti.extraWrites++;
+ } else {
+ throw new IllegalStateException("Illegal mutex upgrade from read to write"); // NOI18N
}
-
- if (compatibleLocks(grantedMode, qc.mode)) { // woken S -> should I wake X? -> no
- waiters.remove(i--);
- qc.wakeMeUp();
- grantedMode = qc.mode;
- if (getThreadInfo(qc.t) == null) {
- // force to have a record since recorded threads
- // do not use isCompatible call
- ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
- ti.forced = true;
- if (qc.mode == S) {
- readersNo++;
- }
- registeredThreads.put(qc.t, ti);
+ } else {
+ // ti == null, entering fresh.
+ // Wait for any readers or writers to exit.
+ while (semaphore != 0) {
+ try {
+ lock.wait();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e.toString());
}
- } else {
- grantedMode = CHAIN;
- break;
}
- } // sync (qc)
+ // Uncontended, go ahead.
+ new ThreadInfo(true).register();
+ semaphore = -1;
+ }
}
}
- /** Posts new request for current thread
- * @param mutexMode mutex mode for which the action is rquested
- * @param run the action
- */
- private void postRequest(int mutexMode, Runnable run) {
-
- if (this == EVENT) {
- doEventRequest(run);
- return;
- }
-
- Thread t = Thread.currentThread();
- ThreadInfo info;
-
- synchronized (LOCK) {
- info = getThreadInfo(t);
- if (info != null) {
- // the same mode and mutex is not entered in the other mode
- // assert (mutexMode == S || mutexMode == X)
- if (mutexMode == info.mode && info.counts[S + X - mutexMode] == 0) {
- enter(mutexMode, t, true);
- } else { // the mutex is held but can not be entered in X mode
- info.enqueue(mutexMode, run);
- return;
- }
+ /**
+ * Exit the write mutex.
+ * @see Mutex.Privileged#exitWriteAccess
+ */
+ void exitWriteAccess() {
+ List lateReadActions = null;
+ ThreadInfo ti;
+ synchronized (lock) {
+ ti = currentThreadInfo();
+ if (ti == null) throw new IllegalStateException();
+ if (!ti.writer) throw new IllegalStateException();
+ if (semaphore != -1) throw new IllegalStateException();
+ if (ti.extraWrites > 0) {
+ // Just mark them off.
+ ti.extraWrites--;
+ } else if (ti.lateReadActions != null) {
+ if (ti.extraReads > 0) throw new IllegalStateException();
+ // Will exit after running these.
+ lateReadActions = ti.lateReadActions;
+ ti.lateReadActions = null;
+ } else {
+ // Really exiting.
+ if (ti.extraReads > 0) throw new IllegalStateException();
+ ti.unregister();
+ semaphore = 0;
+ lock.notifyAll();
+ if (!threads.isEmpty()) throw new IllegalStateException(threads.toString());
+ }
+ }
+ if (lateReadActions != null) {
+ // No other threads can enter before this because we have not yet
+ // released the semaphore.
+ synchronized (lock) {
+ semaphore = 1;
+ ti.writer = false;
+ lock.notifyAll();
+ // Now the semaphore is released, we are in plain read access.
+ if (threads.size() != 1) throw new IllegalStateException(threads.toString());
}
- }
-
- // this mutex is not held
- if (info == null) {
- enter(mutexMode, t, true);
try {
- run.run();
+ Iterator it = lateReadActions.iterator();
+ while (it.hasNext()) {
+ Runnable r = (Runnable)it.next();
+ r.run();
+ }
} finally {
- leave(t);
+ exitReadAccess();
}
- return;
- }
-
- // run it immediately
- // info != null so enter(...) succeeded
- try {
- run.run();
- } finally {
- leave(t);
}
}
-
- /** @param requested is requested mode of locking
- * @return true if and only if current mode and requested mode are compatible
- */
- private boolean isCompatible(int requested) {
- return compatibleLocks(grantedMode, requested);
- }
-
- private ThreadInfo getThreadInfo(Thread t) {
- return (ThreadInfo) registeredThreads.get(t);
- }
-
- private boolean canUpgrade(int threadGranted, int requested) {
- return (threadGranted == S) && (requested == X) && (readersNo == 1);
+
+ /**
+ * Check if the current thread is holding a read or write mutex.
+ * @since XXX
+ */
+ public boolean canRead() {
+ if (this == EVENT) {
+ return isDispatchThread();
+ }
+ synchronized (lock) {
+ if (semaphore == 0) {
+ // Uncontended, obviously not. Only a shortcut,
+ // next clause would catch it anyway.
+ return false;
+ }
+ return currentThreadInfo() != null;
+ }
}
-
- // ------------------------------- EVENT METHODS ----------------------------
- /** Runs the runnable in event queue, either immediatelly,
- * or it posts it into the queue.
- */
- private static void doEvent (Runnable run) {
- if (EventQueue.isDispatchThread ()) {
- run.run ();
- } else {
- EventQueue.invokeLater (run);
+
+ /**
+ * Check if the current thread is holding the write mutex.
+ * @since XXX
+ */
+ public boolean canWrite() {
+ if (this == EVENT) {
+ return isDispatchThread();
+ }
+ synchronized (lock) {
+ if (semaphore != -1) {
+ // Not in write mode, obviously not. Only a shortcut,
+ // next clause would catch it anyway.
+ return false;
+ }
+ ThreadInfo ti = currentThreadInfo();
+ if (ti != null) {
+ if (!ti.writer) throw new IllegalStateException();
+ return true;
+ } else {
+ return false;
+ }
}
}
- /** Methods for access to event queue.
- * @param run runabble to post later
- */
- private static void doEventRequest (Runnable run) {
- EventQueue.invokeLater (run);
+ /** toString */
+ public String toString() {
+ if (this == EVENT) {
+ return "Mutex.EVENT"; // NOI18N
+ }
+ return "Mutex