alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.util;

import java.awt.EventQueue;
import java.util.*;
import java.lang.reflect.InvocationTargetException;

import org.openide.ErrorManager;

/** Read-many/write-one lock.
* Allows control over resources that
* 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 * can enter the mutex as a writer, but you'll be warned because it is very * deadlock prone (two readers trying to get write access concurently). *

* 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. *

* Examples of use: * *

* Mutex m = new Mutex ();
* 
* // Grant write access, compute an integer and return it:
* return (Integer)m.writeAccess (new Mutex.Action () {
*   public Object run () {
*     return new Integer (1);
*   }
* });
* 
* // Obtain read access, do some computation, possibly throw an IOException:
* try {
*   m.readAccess (new Mutex.ExceptionAction () {
*     public Object run () throws IOException {
*       if (...) throw new IOException ();
* 
*       return null;
*     }
*   });
* } catch (MutexException ex) {
*   throw (IOException)ex.getException ();
* }
* 
* * @author Ales Novak */ 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; /** 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 * whole execution of {@link #writeAccess}. The ability to specify locks * allows several Mutexes to synchronize on one object or to synchronize * a mutex with another critical section. * * @param lock lock to use */ public Mutex (Object lock) { init(lock); } /** Default constructor. */ public Mutex() { init(new InternalLock()); } /** @param privileged can enter privileged states of this Mutex * This helps avoid creating of custom Runnables. */ public Mutex(Privileged privileged) { 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; } } Thread t = Thread.currentThread(); readEnter(t); try { return action.run(); } finally { leave(t); } } /** Run an action with read access and possibly throw a checked exception. * The exception if thrown is then encapsulated * in a MutexException and thrown from this method. One is encouraged * to catch MutexException, obtain the inner exception, and rethrow it. * Here is an example: *

    * try {
    *   mutex.readAccess (new ExceptionAction () {
    *     public void run () throws IOException {
    *       throw new IOException ();
    *     }
    *   });
    *  } catch (MutexException ex) {
    *    throw (IOException) ex.getException ();
    *  }
    * 
* Note that runtime exceptions are always passed through, and neither * require this invocation style, nor are encapsulated. * @param action the action to execute * @return the object returned from {@link Mutex.ExceptionAction#run} * @exception MutexException encapsulates a user exception * @exception RuntimeException if any runtime exception is thrown from the run method * @see #readAccess(Mutex.Action) */ public Object readAccess (ExceptionAction action) throws MutexException { if (this == EVENT) { return doEventAccess (action); } Thread t = Thread.currentThread(); readEnter(t); try { return action.run(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new MutexException(e); } finally { leave(t); } } /** Run an action with read access, returning no result. * It may be run asynchronously. * * @param action the action to perform * @see #readAccess(Mutex.Action) */ public void readAccess (final Runnable action) { if (this == EVENT) { doEvent (action); return; } Thread t = Thread.currentThread(); readEnter(t); try { action.run(); } finally { leave(t); } } /** Run an action with write access. * The same thread may meanwhile reenter the mutex; see the class description for details. * * @param action the action to perform * @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; } } Thread t = Thread.currentThread(); writeEnter(t); try { return action.run(); } finally { leave(t); } } /** Run an action with write access and possibly throw an exception. * Here is an example: *

    * try {
    *   mutex.writeAccess (new ExceptionAction () {
    *     public void run () throws IOException {
    *       throw new IOException ();
    *     }
    *   });
    *  } catch (MutexException ex) {
    *    throw (IOException) ex.getException ();
    *  }
    * 
* * @param action the action to execute * @return the result of {@link Mutex.ExceptionAction#run} * @exception MutexException an encapsulated checked exception, if any * @exception RuntimeException if a runtime exception is thrown in the action * @see #writeAccess(Mutex.Action) * @see #readAccess(Mutex.ExceptionAction) */ public Object writeAccess (ExceptionAction action) throws MutexException { if (this == EVENT) { return doEventAccess (action); } Thread t = Thread.currentThread(); writeEnter(t); try { return action.run(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new MutexException(e); } finally { leave(t); } } /** Run an action with write access and return no result. * It may be run asynchronously. * * @param action the action to perform * @see #writeAccess(Mutex.Action) * @see #readAccess(Runnable) */ public void writeAccess (final Runnable action) { if (this == EVENT) { doEvent (action); return; } Thread t = Thread.currentThread(); writeEnter(t); try { action.run(); } finally { leave(t); } } /** Posts a read request. This request runs immediately iff * this Mutex is in the shared mode or this Mutex is not contended * at all. * * This request is delayed if this Mutex is in the exclusive * mode and is held by this thread, until the exclusive is left. * * Finally, this request blocks, if this Mutex is in the exclusive * mode and is held by another thread. * *

Warning: this method blocks.

* * @param run runnable to run */ public void postReadRequest (final Runnable run) { postRequest(S, run); } /** Posts a write request. This request runs immediately iff * this Mutex is in the "pure" exclusive mode, i.e. this Mutex * is not reentered in shared mode after the exclusive mode * was acquired. Otherwise it is delayed until all read requests * are executed. * * This request runs immediately if this Mutex is not contended at all. * * This request blocks if this Mutex is in the shared mode. * *

Warning: this method blocks.

* @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 } 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(); } // 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++; } } return true; } else if (canUpgrade(info.mode, requested)) { // S - X and no holders IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 "); ErrorManager.getDefault().notify(e); 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 IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess through queue, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 "); ErrorManager.getDefault().notify(e); // 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++; } return true; } // else { // granted is S and requested is X // granted is X and requested is S or X //} } if (! block) { return false; } grantedMode = CHAIN; cell = chain(requested, t, 0); } // sync cell.sleep(); } // for } /** privilegedEnter serves for processing posted requests */ private boolean reenter(Thread t, int mode) { // 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; // 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); 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); // grab lock } } } // sync // check posted requests if (postedMode != NONE && info.getRunnableCount(postedMode) > 0) { 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) { // Try as hard as possible to get a real stack trace e.printStackTrace(); // #20467 ErrorManager.getDefault().notify(e); } // try } // for // help gc runnables = null; } finally { leave(t); // release lock grabbed - shared } } // 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(); } readersNo--; return X; } } 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 if (qc.getPriority() == Integer.MAX_VALUE) { waiters.add(0, qc); } else { QueueCell cursor; int i = 0; do { cursor = (QueueCell) waiters.get(i); if (cursor.getPriority() < qc.getPriority()) { waiters.add(i, qc); break; } i++; } while (i < size); if (i == size) { waiters.add(qc); } } 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; } 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 { grantedMode = CHAIN; break; } } // sync (qc) } } /** 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; } } } // this mutex is not held if (info == null) { enter(mutexMode, t, true); try { run.run(); } finally { leave(t); } 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); } // ------------------------------- 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); } } /** Methods for access to event queue. * @param run runabble to post later */ private static void doEventRequest (Runnable run) { EventQueue.invokeLater (run); } /** Methods for access to event queue and waiting for result. * @param run runabble to post later */ private static Object doEventAccess (final ExceptionAction run) throws MutexException { if (isDispatchThread()) { try { return run.run (); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new MutexException (e); } } final Throwable[] arr = new Throwable[1]; try { final Object[] res = new Object[1]; EventQueue.invokeAndWait (new Runnable () { public void run () { try { res[0] = run.run (); } catch (Exception e) { arr[0] = e; } catch (LinkageError e) { // #20467 arr[0] = e; } catch (StackOverflowError e) { // #20467 arr[0] = e; } } }); if (arr[0] == null) { return res[0]; } } catch (InterruptedException e) { arr[0] = e; } catch (InvocationTargetException e) { arr[0] = e; } if(arr[0] instanceof RuntimeException) { throw (RuntimeException)arr[0]; } throw notifyException(ErrorManager.EXCEPTION, arr[0]); } /** @return true iff current thread is EventDispatchThread */ static boolean isDispatchThread() { boolean dispatch = EventQueue.isDispatchThread (); if (!dispatch && Utilities.getOperatingSystem () == Utilities.OS_SOLARIS) { // on solaris the event queue is not always recognized correctly // => try to guess by name dispatch = (Thread.currentThread().getClass().getName().indexOf("EventDispatchThread") >= 0); // NOI18N } return dispatch; } /** Notify exception and returns new MutexException */ private static final MutexException notifyException(int severity, Throwable t) { if (t instanceof InvocationTargetException) { t = unfoldInvocationTargetException((InvocationTargetException) t); } if (t instanceof Error) { annotateEventStack(t); throw (Error) t; } if (t instanceof RuntimeException) { annotateEventStack(t); throw (RuntimeException) t; } MutexException exc = new MutexException((Exception) t); ErrorManager.getDefault().annotate(exc, t); return exc; } private static final void annotateEventStack(Throwable t) { ErrorManager.getDefault().annotate(t, new Exception("Caught here in mutex")); // NOI18N } private static final Throwable unfoldInvocationTargetException(InvocationTargetException e) { Throwable ret; do { ret = e.getTargetException(); if (ret instanceof InvocationTargetException) { e = (InvocationTargetException) ret; } else { e = null; } } while (e != null); return ret; } // --------------------------------------------- END OF EVENT METHODS ------------------------------ /** Action to be executed in a mutex without throwing any checked exceptions. * Unchecked exceptions will be propagated to calling code. */ public static interface Action extends ExceptionAction { /** Execute the action. * @return any object, then returned from {@link Mutex#readAccess(Mutex.Action)} or {@link Mutex#writeAccess(Mutex.Action)} */ public Object run (); } /** Action to be executed in a mutex, possibly throwing checked exceptions. * May throw a checked exception, in which case calling * code should catch the encapsulating exception and rethrow the * real one. * Unchecked exceptions will be propagated to calling code without encapsulation. */ public static interface ExceptionAction { /** Execute the action. * Can throw an exception. * @return any object, then returned from {@link Mutex#readAccess(Mutex.ExceptionAction)} or {@link Mutex#writeAccess(Mutex.ExceptionAction)} * @exception Exception any exception the body needs to throw */ public Object run () throws Exception; } private static final class ThreadInfo { /** t is forcibly sent from waiters to enter() by wakeUpOthers() */ boolean forced; /** ThreadInfo for this Thread */ final Thread t; /** granted mode */ int mode; // 0 - NONE, 1 - CHAIN, 2 - X, 3 - S /** enter counter */ int[] counts; /** queue of runnable rquests that are to be executed (in X mode) right after S mode is left * deadlock avoidance technique */ List[] queues; /** value of counts[S] when the mode was upgraded * rsnapshot works as follows: * if a thread holds the mutex in the S mode and it reenters the mutex * and requests X and the mode can be granted (no other readers) then this * variable is set to counts[S]. This is used in the leave method in the X branch. * (X mode is granted by other words) * If rsnapshot is less than counts[S] then the counter is decremented etc. If the rsnapshot is * equal to count[S] then count[X] is decremented. If the X counter is zeroed then * rsnapshot is zeroed as well and current mode is downgraded to S mode. * rsnapshot gets less than counts[S] if current mode is X and the mutex is reentered * with S request. */ int rsnapshot; public ThreadInfo(Thread t, int mode) { this.t = t; this.mode = mode; this.counts = new int[MODE_COUNT]; this.queues = new List[MODE_COUNT]; counts[mode] = 1; } public String toString() { return super.toString() + " thread: " + t + " mode: " + mode + " X: " + counts[2] + " S: " + counts[3]; // NOI18N } /** Adds the Runnable into the queue of waiting requests */ public void enqueue(int mode, Runnable run) { if (queues[mode] == null) { queues[mode] = new ArrayList(13); } queues[mode].add(run); } /** @return a List of enqueued Runnables - may be null */ public List dequeue(int mode) { List ret = queues[mode]; queues[mode] = null; return ret; } public int getRunnableCount(int mode) { return (queues[mode] == null ? 0 : queues[mode].size()); } } /** This class is defined only for better understanding of thread dumps where are informations like * java.lang.Object@xxxxxxxx owner thread_x * wait for enter thread_y */ private static final class InternalLock { InternalLock() {} } private static final class QueueCell { int mode; Thread t; boolean signal; boolean left; /** priority of the cell */ int priority2; public QueueCell(int mode, Thread t) { this.mode = mode; this.t = t; this.left = false; this.priority2 = 0; } public String toString() { return super.toString() + " mode: " + mode + " thread: " + t; // NOI18N } /** @return priority of this cell */ public long getPriority() { return (priority2 == 0 ? t.getPriority() : priority2); } /** @return true iff the thread left sleep */ public boolean isGotOut() { return left; } /** current thread will sleep until wakeMeUp is called * if wakeMeUp was already called then the thread will not sleep */ public synchronized void sleep() { try { while (!signal) { try { wait(); return; } catch (InterruptedException e) { ErrorManager.getDefault().notify(e); } } } finally { left = true; } } /** sends signal to a sleeper - to a thread that is in the sleep() */ public void wakeMeUp() { signal = true; notifyAll(); } } /** Provides access to Mutex's internal methods. * * This class can be used when one wants to avoid creating a * bunch of Runnables. Instead, *
     * try {
     *     enterXAccess ();
     *     yourCustomMethod ();
     * } finally {
     *     exitXAccess ();
     * }
     * 
* can be used. * * You must, however, control the related Mutex, i.e. you must be creator of * the Mutex. * * @since 1.17 */ public static final class Privileged { private Mutex parent; final void setParent(Mutex parent) { this.parent = parent; } public void enterReadAccess() { parent.readEnter(Thread.currentThread()); } public void enterWriteAccess() { parent.writeEnter(Thread.currentThread()); } public void exitReadAccess() { parent.leave(Thread.currentThread()); } public void exitWriteAccess() { parent.leave(Thread.currentThread()); } } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.