0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.openide.util;
0043:
0044: import java.awt.EventQueue;
0045: import java.lang.reflect.InvocationTargetException;
0046: import java.util.ArrayList;
0047: import java.util.HashMap;
0048: import java.util.LinkedList;
0049: import java.util.List;
0050: import java.util.Map;
0051: import java.util.concurrent.Executor;
0052: import java.util.logging.Level;
0053: import java.util.logging.Logger;
0054:
0055: /** Read-many/write-one lock.
0056: * Allows control over resources that
0057: * can be read by several readers at once but only written by one writer.
0058: * <P>
0059: * It is guaranteed that if you are a writer you can also enter the
0060: * mutex as a reader. Conversely, if you are the <em>only</em> reader you
0061: * can enter the mutex as a writer, but you'll be warned because it is very
0062: * deadlock prone (two readers trying to get write access concurently).
0063: * <P>
0064: * If the mutex is used only by one thread, the thread can repeatedly
0065: * enter it as a writer or reader. So one thread can never deadlock itself,
0066: * whichever order operations are performed in.
0067: * <P>
0068: * There is no strategy to prevent starvation.
0069: * Even if there is a writer waiting to enter, another reader might enter
0070: * the section instead.
0071: * <P>
0072: * Examples of use:
0073: *
0074: * <p><code><PRE>
0075: * Mutex m = new Mutex ();
0076: *
0077: * // Grant write access, compute an integer and return it:
0078: * return (Integer)m.writeAccess (new Mutex.Action () {
0079: * public Object run () {
0080: * return new Integer (1);
0081: * }
0082: * });
0083: *
0084: * // Obtain read access, do some computation, possibly throw an IOException:
0085: * try {
0086: * m.readAccess (new Mutex.ExceptionAction () {
0087: * public Object run () throws IOException {
0088: * if (...) throw new IOException ();
0089: *
0090: * return null;
0091: * }
0092: * });
0093: * } catch (MutexException ex) {
0094: * throw (IOException)ex.getException ();
0095: * }
0096: *
0097: * // check whether you are already in read access
0098: * if (m.isReadAccess ()) {
0099: * // do your work
0100: * }
0101: * </PRE></code>
0102: *
0103: * @author Ales Novak
0104: */
0105: public final class Mutex extends Object {
0106: /** counter of created mutexes */
0107: private static int counter;
0108:
0109: /** logger for things that happen in mutex */
0110: private static Logger LOG = Logger.getLogger(Mutex.class.getName());
0111:
0112: /** Mutex that allows code to be synchronized with the AWT event dispatch thread.
0113: * <P>
0114: * When the Mutex methods are invoked on this mutex, the methods' semantics
0115: * change as follows:
0116: * <UL>
0117: * <LI>The {@link #isReadAccess} and {@link #isWriteAccess} methods
0118: * return <code>true</code> if the current thread is the event dispatch thread
0119: * and false otherwise.
0120: * <LI>The {@link #postReadRequest} and {@link #postWriteRequest} methods
0121: * asynchronously execute the {@link java.lang.Runnable} passed in their
0122: * <code>run</code> parameter on the event dispatch thead.
0123: * <LI>The {@link #readAccess(java.lang.Runnable)} and
0124: * {@link #writeAccess(java.lang.Runnable)} methods asynchronously execute the
0125: * {@link java.lang.Runnable} passed in their <code>run</code> parameter
0126: * on the event dispatch thread, unless the current thread is
0127: * the event dispatch thread, in which case
0128: * <code>run.run()</code> is immediately executed.
0129: * <LI>The {@link #readAccess(Mutex.Action)},
0130: * {@link #readAccess(Mutex.ExceptionAction action)},
0131: * {@link #writeAccess(Mutex.Action action)} and
0132: * {@link #writeAccess(Mutex.ExceptionAction action)}
0133: * methods synchronously execute the {@link Mutex.ExceptionAction}
0134: * passed in their <code>action</code> parameter on the event dispatch thread,
0135: * unless the current thread is the event dispatch thread, in which case
0136: * <code>action.run()</code> is immediately executed.
0137: * </UL>
0138: */
0139: public static final Mutex EVENT = new Mutex();
0140:
0141: /** this is used from tests to prevent upgrade from readAccess to writeAccess
0142: * by strictly throwing exception. Otherwise we just notify that using ErrorManager.
0143: */
0144: static boolean beStrict;
0145:
0146: // lock mode constants
0147:
0148: /** Lock free */
0149: private static final int NONE = 0x0;
0150:
0151: /** Enqueue all requests */
0152: private static final int CHAIN = 0x1;
0153:
0154: /** eXclusive */
0155: private static final int X = 0x2;
0156:
0157: /** Shared */
0158: private static final int S = 0x3;
0159:
0160: /** number of modes */
0161: private static final int MODE_COUNT = 0x4;
0162:
0163: /** compatibility matrix */
0164:
0165: // [requested][granted]
0166: private static final boolean[][] cmatrix = { null, null, // NONE, CHAIN
0167: { true, false, false, false }, { true, false, false, true } };
0168:
0169: /** granted mode */
0170: private int grantedMode = NONE;
0171:
0172: /** The mode the mutex was in before it started chaining */
0173: private int origMode;
0174:
0175: /** protects internal data structures */
0176: private/*final*/Object LOCK;
0177:
0178: /** wrapper, if any */
0179: private final Executor wrapper;
0180:
0181: /** threads that - owns or waits for this mutex */
0182: private/*final*/Map<Thread, ThreadInfo> registeredThreads;
0183:
0184: /** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") */
0185:
0186: // NOI18N
0187: private int readersNo = 0;
0188:
0189: /** a queue of waiting threads for this mutex */
0190: private List<QueueCell> waiters;
0191:
0192: /** identification of the mutex */
0193: private int cnt;
0194:
0195: /** Enhanced constructor that permits specifying an object to use as a lock.
0196: * The lock is used on entry and exit to {@link #readAccess} and during the
0197: * whole execution of {@link #writeAccess}. The ability to specify locks
0198: * allows several <code>Mutex</code>es to synchronize on one object or to synchronize
0199: * a mutex with another critical section.
0200: *
0201: * @param lock lock to use
0202: */
0203: public Mutex(Object lock) {
0204: init(lock);
0205: this .wrapper = null;
0206: }
0207:
0208: /** Default constructor.
0209: */
0210: public Mutex() {
0211: init(new InternalLock());
0212: this .wrapper = null;
0213: }
0214:
0215: /** @param privileged can enter privileged states of this Mutex
0216: * This helps avoid creating of custom Runnables.
0217: */
0218: public Mutex(Privileged privileged) {
0219: if (privileged == null) {
0220: throw new IllegalArgumentException("privileged == null"); //NOI18N
0221: } else {
0222: init(new InternalLock());
0223: privileged.setParent(this );
0224: }
0225: this .wrapper = null;
0226: }
0227:
0228: /** Constructor for those who wish to do some custom additional tasks
0229: * whenever an action or runnable is executed in the {@link Mutex}. This
0230: * may be useful for wrapping all the actions with custom {@link ThreadLocal}
0231: * value, etc. Just implement the {@link Executor}'s <code>execute(Runnable)</code>
0232: * method and do pre and post initialization tasks before running the runnable.
0233: * <p>
0234: * The {@link Executor#execute} method shall return only when the passed in
0235: * {@link Runnable} is finished, otherwise methods like {@link Mutex#readAccess(Action)} and co.
0236: * might not return proper result.
0237: *
0238: * @param privileged can enter privileged states of this Mutex
0239: * @param executor allows to wrap the work of the mutex with a custom code
0240: * @since 7.12
0241: */
0242: public Mutex(Privileged privileged, Executor executor) {
0243: LOCK = new Mutex(privileged);
0244: this .wrapper = executor;
0245: }
0246:
0247: /** Initiates this Mutex */
0248: private void init(Object lock) {
0249: this .LOCK = lock;
0250: this .registeredThreads = new HashMap<Thread, ThreadInfo>(7);
0251: this .waiters = new LinkedList<QueueCell>();
0252: this .cnt = counter++;
0253: if (LOG.isLoggable(Level.FINE)) {
0254: LOG.log(Level.FINE, "[" + cnt + "] created here",
0255: new Exception());
0256: }
0257: }
0258:
0259: /** Run an action only with read access.
0260: * See class description re. entering for write access within the dynamic scope.
0261: * @param action the action to perform
0262: * @return the object returned from {@link Mutex.Action#run}
0263: */
0264: public <T> T readAccess(final Action<T> action) {
0265: if (this == EVENT) {
0266: try {
0267: return doEventAccess(action);
0268: } catch (MutexException e) {
0269: throw (InternalError) new InternalError(
0270: "Exception from non-Exception Action")
0271: .initCause(e.getException()); // NOI18N
0272: }
0273: }
0274: if (wrapper != null) {
0275: try {
0276: return doWrapperAccess(action, null, true);
0277: } catch (MutexException e) {
0278: throw (InternalError) new InternalError(
0279: "Exception from non-Exception Action")
0280: .initCause(e.getException()); // NOI18N
0281: }
0282: }
0283:
0284: Thread t = Thread.currentThread();
0285: readEnter(t);
0286:
0287: try {
0288: return action.run();
0289: } finally {
0290: leave(t);
0291: }
0292: }
0293:
0294: /** Run an action with read access and possibly throw a checked exception.
0295: * The exception if thrown is then encapsulated
0296: * in a <code>MutexException</code> and thrown from this method. One is encouraged
0297: * to catch <code>MutexException</code>, obtain the inner exception, and rethrow it.
0298: * Here is an example:
0299: * <p><code><PRE>
0300: * try {
0301: * mutex.readAccess (new ExceptionAction () {
0302: * public void run () throws IOException {
0303: * throw new IOException ();
0304: * }
0305: * });
0306: * } catch (MutexException ex) {
0307: * throw (IOException) ex.getException ();
0308: * }
0309: * </PRE></code>
0310: * Note that <em>runtime exceptions</em> are always passed through, and neither
0311: * require this invocation style, nor are encapsulated.
0312: * @param action the action to execute
0313: * @return the object returned from {@link Mutex.ExceptionAction#run}
0314: * @exception MutexException encapsulates a user exception
0315: * @exception RuntimeException if any runtime exception is thrown from the run method
0316: * @see #readAccess(Mutex.Action)
0317: */
0318: public <T> T readAccess(final ExceptionAction<T> action)
0319: throws MutexException {
0320: if (this == EVENT) {
0321: return doEventAccess(action);
0322: }
0323: if (wrapper != null) {
0324: return doWrapperAccess(action, null, true);
0325: }
0326:
0327: Thread t = Thread.currentThread();
0328: readEnter(t);
0329:
0330: try {
0331: return action.run();
0332: } catch (RuntimeException e) {
0333: throw e;
0334: } catch (Exception e) {
0335: throw new MutexException(e);
0336: } finally {
0337: leave(t);
0338: }
0339: }
0340:
0341: /** Run an action with read access, returning no result.
0342: * It may be run asynchronously.
0343: *
0344: * @param action the action to perform
0345: * @see #readAccess(Mutex.Action)
0346: */
0347: public void readAccess(final Runnable action) {
0348: if (this == EVENT) {
0349: doEvent(action);
0350:
0351: return;
0352: }
0353: if (wrapper != null) {
0354: try {
0355: doWrapperAccess(null, action, true);
0356: return;
0357: } catch (MutexException ex) {
0358: throw (IllegalStateException) new IllegalStateException()
0359: .initCause(ex);
0360: }
0361: }
0362:
0363: Thread t = Thread.currentThread();
0364: readEnter(t);
0365:
0366: try {
0367: action.run();
0368: } finally {
0369: leave(t);
0370: }
0371: }
0372:
0373: /** Run an action with write access.
0374: * The same thread may meanwhile reenter the mutex; see the class description for details.
0375: *
0376: * @param action the action to perform
0377: * @return the result of {@link Mutex.Action#run}
0378: */
0379: public <T> T writeAccess(Action<T> action) {
0380: if (this == EVENT) {
0381: try {
0382: return doEventAccess(action);
0383: } catch (MutexException e) {
0384: throw (InternalError) new InternalError(
0385: "Exception from non-Exception Action")
0386: .initCause(e.getException()); // NOI18N
0387: }
0388: }
0389: if (wrapper != null) {
0390: try {
0391: return doWrapperAccess(action, null, false);
0392: } catch (MutexException e) {
0393: throw (InternalError) new InternalError(
0394: "Exception from non-Exception Action")
0395: .initCause(e.getException()); // NOI18N
0396: }
0397: }
0398:
0399: Thread t = Thread.currentThread();
0400: writeEnter(t);
0401:
0402: try {
0403: return action.run();
0404: } finally {
0405: leave(t);
0406: }
0407: }
0408:
0409: /** Run an action with write access and possibly throw an exception.
0410: * Here is an example:
0411: * <p><code><PRE>
0412: * try {
0413: * mutex.writeAccess (new ExceptionAction () {
0414: * public void run () throws IOException {
0415: * throw new IOException ();
0416: * }
0417: * });
0418: * } catch (MutexException ex) {
0419: * throw (IOException) ex.getException ();
0420: * }
0421: * </PRE></code>
0422: *
0423: * @param action the action to execute
0424: * @return the result of {@link Mutex.ExceptionAction#run}
0425: * @exception MutexException an encapsulated checked exception, if any
0426: * @exception RuntimeException if a runtime exception is thrown in the action
0427: * @see #writeAccess(Mutex.Action)
0428: * @see #readAccess(Mutex.ExceptionAction)
0429: */
0430: public <T> T writeAccess(ExceptionAction<T> action)
0431: throws MutexException {
0432: if (this == EVENT) {
0433: return doEventAccess(action);
0434: }
0435: if (wrapper != null) {
0436: return doWrapperAccess(action, null, false);
0437: }
0438:
0439: Thread t = Thread.currentThread();
0440: writeEnter(t);
0441:
0442: try {
0443: return action.run();
0444: } catch (RuntimeException e) {
0445: throw e;
0446: } catch (Exception e) {
0447: throw new MutexException(e);
0448: } finally {
0449: leave(t);
0450: }
0451: }
0452:
0453: /** Run an action with write access and return no result.
0454: * It may be run asynchronously.
0455: *
0456: * @param action the action to perform
0457: * @see #writeAccess(Mutex.Action)
0458: * @see #readAccess(Runnable)
0459: */
0460: public void writeAccess(final Runnable action) {
0461: if (this == EVENT) {
0462: doEvent(action);
0463:
0464: return;
0465: }
0466: if (wrapper != null) {
0467: try {
0468: doWrapperAccess(null, action, false);
0469: } catch (MutexException ex) {
0470: throw (IllegalStateException) new IllegalStateException()
0471: .initCause(ex);
0472: }
0473: return;
0474: }
0475:
0476: Thread t = Thread.currentThread();
0477: writeEnter(t);
0478:
0479: try {
0480: action.run();
0481: } finally {
0482: leave(t);
0483: }
0484: }
0485:
0486: /** Tests whether this thread has already entered the mutex in read access.
0487: * If it returns true, calling <code>readAccess</code>
0488: * will be executed immediatelly
0489: * without any blocking.
0490: * Calling <code>postWriteAccess</code> will delay the execution
0491: * of its <code>Runnable</code> until a readAccess section is over
0492: * and calling <code>writeAccess</code> is strongly prohibited and will
0493: * result in a warning as a deadlock prone behaviour.
0494: * <p><strong>Warning:</strong> since a thread with write access automatically
0495: * has effective read access as well (whether or not explicitly requested), if
0496: * you want to check whether a thread can read some data, you should check for
0497: * either kind of access, e.g.:
0498: * <pre>assert myMutex.isReadAccess() || myMutex.isWriteAccess();</pre>
0499: *
0500: * @return true if the thread is in read access section
0501: * @since 4.48
0502: */
0503: public boolean isReadAccess() {
0504: if (this == EVENT) {
0505: return javax.swing.SwingUtilities.isEventDispatchThread();
0506: }
0507: if (wrapper != null) {
0508: Mutex m = (Mutex) LOCK;
0509: return m.isReadAccess();
0510: }
0511:
0512: Thread t = Thread.currentThread();
0513: ThreadInfo info;
0514:
0515: synchronized (LOCK) {
0516: info = getThreadInfo(t);
0517:
0518: if (info != null) {
0519: if (info.counts[S] > 0) {
0520: return true;
0521: }
0522: }
0523: }
0524:
0525: return false;
0526: }
0527:
0528: /** Tests whether this thread has already entered the mutex in write access.
0529: * If it returns true, calling <code>writeAccess</code> will be executed
0530: * immediatelly without any other blocking. <code>postReadAccess</code>
0531: * will be delayed until a write access runnable is over.
0532: *
0533: * @return true if the thread is in write access section
0534: * @since 4.48
0535: */
0536: public boolean isWriteAccess() {
0537: if (this == EVENT) {
0538: return javax.swing.SwingUtilities.isEventDispatchThread();
0539: }
0540: if (wrapper != null) {
0541: Mutex m = (Mutex) LOCK;
0542: return m.isWriteAccess();
0543: }
0544:
0545: Thread t = Thread.currentThread();
0546: ThreadInfo info;
0547:
0548: synchronized (LOCK) {
0549: info = getThreadInfo(t);
0550:
0551: if (info != null) {
0552: if (info.counts[X] > 0) {
0553: return true;
0554: }
0555: }
0556: }
0557:
0558: return false;
0559: }
0560:
0561: /** Posts a read request. This request runs immediately iff
0562: * this Mutex is in the shared mode or this Mutex is not contended
0563: * at all.
0564: *
0565: * This request is delayed if this Mutex is in the exclusive
0566: * mode and is held by this thread, until the exclusive is left.
0567: *
0568: * Finally, this request blocks, if this Mutex is in the exclusive
0569: * mode and is held by another thread.
0570: *
0571: * <p><strong>Warning:</strong> this method blocks.</p>
0572: *
0573: * @param run runnable to run
0574: */
0575: public void postReadRequest(final Runnable run) {
0576: postRequest(S, run, null);
0577: }
0578:
0579: /** Posts a write request. This request runs immediately iff
0580: * this Mutex is in the "pure" exclusive mode, i.e. this Mutex
0581: * is not reentered in shared mode after the exclusive mode
0582: * was acquired. Otherwise it is delayed until all read requests
0583: * are executed.
0584: *
0585: * This request runs immediately if this Mutex is not contended at all.
0586: *
0587: * This request blocks if this Mutex is in the shared mode.
0588: *
0589: * <p><strong>Warning:</strong> this method blocks.</p>
0590: * @param run runnable to run
0591: */
0592: public void postWriteRequest(Runnable run) {
0593: postRequest(X, run, null);
0594: }
0595:
0596: /** toString */
0597: public String toString() {
0598: if (this == EVENT) {
0599: return "Mutex.EVENT"; // NOI18N
0600: }
0601:
0602: String newline = System.getProperty("line.separator");
0603: StringBuffer sbuff = new StringBuffer(512);
0604:
0605: synchronized (LOCK) {
0606: sbuff.append("threads: ").append(registeredThreads).append(
0607: newline); // NOI18N
0608: sbuff.append("readersNo: ").append(readersNo).append(
0609: newline); // NOI18N
0610: sbuff.append("waiters: ").append(waiters).append(newline); // NOI18N
0611: sbuff.append("grantedMode: ").append(grantedMode).append(
0612: newline); // NOI18N
0613: }
0614:
0615: return sbuff.toString();
0616: }
0617:
0618: // priv methods -----------------------------------------
0619:
0620: /** enters this mutex for writing */
0621: private void writeEnter(Thread t) {
0622: enter(X, t, true);
0623: }
0624:
0625: /** enters this mutex for reading */
0626: private void readEnter(Thread t) {
0627: enter(S, t, true);
0628: }
0629:
0630: private void doLog(String action, Object... params) {
0631: String tid = Integer.toHexString(Thread.currentThread()
0632: .hashCode());
0633: LOG.log(Level.FINE, "[#" + cnt + "@" + tid + "] " + action,
0634: params);
0635: }
0636:
0637: /** enters this mutex with given mode
0638: * @param requested one of S, X
0639: * @param t
0640: */
0641: private boolean enter(int requested, Thread t, boolean block) {
0642: boolean log = LOG.isLoggable(Level.FINE);
0643:
0644: if (log)
0645: doLog("Entering {0}, {1}", requested, block); // NOI18N
0646:
0647: boolean ret = enterImpl(requested, t, block);
0648:
0649: if (log)
0650: doLog("Entering exit: {0}", ret); // NOI18N
0651:
0652: return ret;
0653: }
0654:
0655: private boolean enterImpl(int requested, Thread t, boolean block) {
0656: QueueCell cell = null;
0657: int loopc = 0;
0658:
0659: for (;;) {
0660: loopc++;
0661: synchronized (LOCK) {
0662: // does the thread reenter this mutex?
0663: ThreadInfo info = getThreadInfo(t);
0664:
0665: if (info != null) {
0666: if (grantedMode == NONE) {
0667: // defensive
0668: throw new IllegalStateException();
0669: }
0670: // reenters
0671: // requested == S -> always succeeds
0672: // info.mode == X -> always succeeds
0673: if (((info.mode == S) && (grantedMode == X))
0674: || ((info.mode == X) && (grantedMode == S))) {
0675: // defensive
0676: throw new IllegalStateException();
0677: }
0678: if ((info.mode == X) || (info.mode == requested)) {
0679: if (info.forced) {
0680: info.forced = false;
0681: } else {
0682: if ((requested == X)
0683: && (info.counts[S] > 0)) {
0684: IllegalStateException e = new IllegalStateException(
0685: "WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
0686:
0687: if (beStrict) {
0688: throw e;
0689: }
0690: Exceptions.printStackTrace(e);
0691: }
0692: info.counts[requested]++;
0693: if ((requested == S)
0694: && (info.counts[requested] == 1)) {
0695: readersNo++;
0696: }
0697: }
0698: return true;
0699: } else if (canUpgrade(info.mode, requested)) {
0700: IllegalStateException e = new IllegalStateException(
0701: "WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
0702:
0703: if (beStrict) {
0704: throw e;
0705: }
0706: Exceptions.printStackTrace(e);
0707: info.mode = X;
0708: info.counts[requested]++;
0709: info.rsnapshot = info.counts[S];
0710: if (grantedMode == S) {
0711: setGrantedMode(X);
0712: } else if (grantedMode == X) {
0713: // defensive
0714: throw new IllegalStateException();
0715: }
0716: // else if grantedMode == CHAIN - let it be
0717: return true;
0718: } else {
0719: IllegalStateException e = new IllegalStateException(
0720: "WARNING: Going from readAccess to writeAccess through queue, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
0721:
0722: if (beStrict) {
0723: throw e;
0724: }
0725: Exceptions.printStackTrace(e);
0726: }
0727: } else {
0728: if (isCompatible(requested)) {
0729: setGrantedMode(requested);
0730: registeredThreads.put(t, info = new ThreadInfo(
0731: t, requested));
0732: if (requested == S) {
0733: readersNo++;
0734: }
0735: return true;
0736: }
0737: }
0738: if (!block) {
0739: return false;
0740: }
0741: setGrantedMode(CHAIN);
0742: cell = chain(requested, t, 0);
0743: }
0744: // sync
0745: cell.sleep();
0746: }
0747: // for
0748: }
0749:
0750: /** privilegedEnter serves for processing posted requests */
0751: private boolean reenter(Thread t, int mode) {
0752: boolean log = LOG.isLoggable(Level.FINE);
0753:
0754: if (log)
0755: doLog("Re-Entering {0}", mode); // NOI18N
0756:
0757: boolean ret = reenterImpl(t, mode);
0758:
0759: if (log)
0760: doLog("Re-Entering exit: {0}", ret); // NOI18N
0761:
0762: return ret;
0763: }
0764:
0765: private boolean reenterImpl(Thread t, int mode) {
0766: // from leaveX -> grantedMode is NONE or S
0767: if (mode == S) {
0768: if ((grantedMode != NONE) && (grantedMode != S)) {
0769: throw new IllegalStateException(this .toString());
0770: }
0771:
0772: enter(mode, t, true);
0773:
0774: return false;
0775: }
0776:
0777: // assert (mode == X)
0778: ThreadInfo tinfo = getThreadInfo(t);
0779: boolean chainFromLeaveX = ((grantedMode == CHAIN)
0780: && (tinfo != null) && (tinfo.counts[X] > 0));
0781:
0782: // process grantedMode == X or CHAIN from leaveX OR grantedMode == NONE from leaveS
0783: if ((grantedMode == X) || (grantedMode == NONE)
0784: || chainFromLeaveX) {
0785: enter(mode, t, true);
0786:
0787: return false;
0788: } else { // remains grantedMode == CHAIN or S from leaveS, so it will be CHAIN
0789:
0790: if (readersNo == 0) {
0791: throw new IllegalStateException(this .toString());
0792: }
0793:
0794: ThreadInfo info = new ThreadInfo(t, mode);
0795: registeredThreads.put(t, info);
0796:
0797: // prevent from grantedMode == NONE (another thread - leaveS)
0798: readersNo += 2;
0799:
0800: // prevent from new readers
0801: setGrantedMode(CHAIN);
0802:
0803: return true;
0804: }
0805: // else X means ERROR!!!
0806: }
0807:
0808: /** @param t holds S (one entry) and wants X, grantedMode != NONE && grantedMode != X */
0809: private void privilegedEnter(Thread t, int mode) {
0810: boolean decrease = true;
0811:
0812: synchronized (LOCK) {
0813: getThreadInfo(t);
0814: }
0815:
0816: for (;;) {
0817: QueueCell cell;
0818:
0819: synchronized (LOCK) {
0820: if (decrease) {
0821: decrease = false;
0822: readersNo -= 2;
0823: }
0824:
0825: // always chain this thread
0826: // since there can be another one
0827: // in the queue with higher priority
0828: setGrantedMode(CHAIN);
0829: cell = chain(mode, t, Integer.MAX_VALUE);
0830:
0831: if (readersNo == 0) { // seems I may enter
0832:
0833: // no one has higher prio?
0834: if (waiters.get(0) == cell) {
0835: waiters.remove(0);
0836:
0837: setGrantedMode(mode);
0838:
0839: return;
0840: } else {
0841: setGrantedMode(NONE);
0842: wakeUpOthers();
0843: }
0844: }
0845: }
0846: // synchronized (LOCK)
0847:
0848: cell.sleep();
0849:
0850: // cell already removed from waiters here
0851: }
0852: }
0853:
0854: /** Leaves this mutex */
0855: private void leave(Thread t) {
0856: boolean log = LOG.isLoggable(Level.FINE);
0857:
0858: if (log)
0859: doLog("Leaving {0}", grantedMode); // NOI18N
0860:
0861: leaveImpl(t);
0862:
0863: if (log)
0864: doLog("Leaving exit: {0}", grantedMode); // NOI18N
0865: }
0866:
0867: private void leaveImpl(Thread t) {
0868: ThreadInfo info;
0869: int postedMode = NONE;
0870: boolean needLock = false;
0871:
0872: synchronized (LOCK) {
0873: info = getThreadInfo(t);
0874:
0875: switch (grantedMode) {
0876: case NONE:
0877: throw new IllegalStateException();
0878:
0879: case CHAIN:
0880:
0881: if (info.counts[X] > 0) {
0882: // it matters that X is handled first - see ThreadInfo.rsnapshot
0883: postedMode = leaveX(info);
0884: } else if (info.counts[S] > 0) {
0885: postedMode = leaveS(info);
0886: } else {
0887: throw new IllegalStateException();
0888: }
0889:
0890: break;
0891:
0892: case X:
0893: postedMode = leaveX(info);
0894:
0895: break;
0896:
0897: case S:
0898: postedMode = leaveS(info);
0899:
0900: break;
0901: } // switch
0902:
0903: // do not give up LOCK until queued runnables are run
0904: if (postedMode != NONE) {
0905: int runsize = info.getRunnableCount(postedMode);
0906:
0907: if (runsize != 0) {
0908: needLock = reenter(t, postedMode); // grab lock
0909: }
0910: }
0911: } // sync
0912:
0913: // check posted requests
0914: if ((postedMode != NONE)
0915: && (info.getRunnableCount(postedMode) > 0)) {
0916: doLog("Processing posted requests: {0}", postedMode); // NOI18N
0917: try {
0918: if (needLock) { // go from S to X or CHAIN
0919: privilegedEnter(t, postedMode);
0920: }
0921:
0922: // holds postedMode lock here
0923: List runnables = info.dequeue(postedMode);
0924: final int size = runnables.size();
0925:
0926: for (int i = 0; i < size; i++) {
0927: try {
0928: Runnable r = (Runnable) runnables.get(i);
0929:
0930: r.run();
0931: } catch (Exception e) {
0932: Exceptions.printStackTrace(e);
0933: } catch (StackOverflowError e) {
0934: // Try as hard as possible to get a real stack trace
0935: e.printStackTrace();
0936: Exceptions.printStackTrace(e);
0937: } catch (ThreadDeath td) {
0938: throw td;
0939: } catch (Error e) {
0940: Exceptions.printStackTrace(e);
0941: }
0942: }
0943: // for
0944:
0945: // help gc
0946: runnables = null;
0947: } finally {
0948: leave(t); // release lock grabbed - shared
0949: }
0950: }
0951: // mode
0952: }
0953:
0954: /** Leaves the lock supposing that info.counts[X] is greater than zero */
0955: private int leaveX(ThreadInfo info) {
0956: if ((info.counts[X] <= 0) || (info.rsnapshot > info.counts[S])) {
0957: // defensive
0958: throw new IllegalStateException();
0959: }
0960:
0961: if (info.rsnapshot == info.counts[S]) {
0962: info.counts[X]--;
0963:
0964: if (info.counts[X] == 0) {
0965: info.rsnapshot = 0;
0966:
0967: // downgrade the lock
0968: if (info.counts[S] > 0) {
0969: info.mode = S;
0970: setGrantedMode(S);
0971: } else {
0972: info.mode = NONE;
0973: setGrantedMode(NONE);
0974: registeredThreads.remove(info.t);
0975: }
0976:
0977: if (info.getRunnableCount(S) > 0) {
0978: // wake up other readers of this mutex
0979: wakeUpReaders();
0980:
0981: return S;
0982: }
0983:
0984: // mode has changed
0985: wakeUpOthers();
0986: }
0987: } else {
0988: // rsnapshot < counts[S]
0989: if (info.counts[S] <= 0) {
0990: // defensive
0991: throw new IllegalStateException();
0992: }
0993:
0994: if (--info.counts[S] == 0) {
0995: if (readersNo <= 0) {
0996: throw new IllegalStateException();
0997: }
0998:
0999: readersNo--;
1000:
1001: return X;
1002: }
1003: }
1004:
1005: return NONE;
1006: }
1007:
1008: /** Leaves the lock supposing that info.counts[S] is greater than zero */
1009: private int leaveS(ThreadInfo info) {
1010: if ((info.counts[S] <= 0) || (info.counts[X] > 0)) {
1011: // defensive
1012: throw new IllegalStateException();
1013: }
1014:
1015: info.counts[S]--;
1016:
1017: if (info.counts[S] == 0) {
1018: // remove the thread
1019: info.mode = NONE;
1020: registeredThreads.remove(info.t);
1021:
1022: // downsize readersNo
1023: if (readersNo <= 0) {
1024: throw new IllegalStateException();
1025: }
1026:
1027: readersNo--;
1028:
1029: if (readersNo == 0) {
1030: // set grantedMode to NONE
1031: // and then wakeUp others - either immediately
1032: // or in privelegedEnter()
1033: setGrantedMode(NONE);
1034:
1035: if (info.getRunnableCount(X) > 0) {
1036: return X;
1037: }
1038:
1039: wakeUpOthers();
1040: } else if (info.getRunnableCount(X) > 0) {
1041: return X;
1042: } else if ((grantedMode == CHAIN) && (readersNo == 1)) {
1043: // can be the mode advanced from CHAIN? Examine first item of waiters!
1044: for (int i = 0; i < waiters.size(); i++) {
1045: QueueCell qc = waiters.get(i);
1046:
1047: synchronized (qc) {
1048: if (qc.isGotOut()) {
1049: waiters.remove(i--);
1050:
1051: continue;
1052: }
1053:
1054: ThreadInfo tinfo = getThreadInfo(qc.t);
1055:
1056: if (tinfo != null) {
1057: if (tinfo.mode == S) {
1058: if (qc.mode != X) {
1059: // defensive
1060: throw new IllegalStateException();
1061: }
1062:
1063: if (waiters.size() == 1) {
1064: setGrantedMode(X);
1065: }
1066: // else let CHAIN
1067:
1068: tinfo.mode = X;
1069: waiters.remove(i);
1070: qc.wakeMeUp();
1071: }
1072: }
1073: // else first request is a first X request of some thread
1074:
1075: break;
1076: }
1077: // sync (qc)
1078: }
1079: // for
1080: }
1081: // else
1082: }
1083: // count[S] == 0
1084:
1085: return NONE;
1086: }
1087:
1088: /** Adds this thread to the queue of waiting threads
1089: * @warning LOCK must be held
1090: */
1091: private QueueCell chain(final int requested, final Thread t,
1092: final int priority) {
1093: //long timeout = 0;
1094:
1095: /*
1096: if (killDeadlocksOn) {
1097: checkDeadlock(requested, t);
1098: timeout = (isDispatchThread() || checkAwtTreeLock() ? TIMEOUT : 0);
1099: }
1100: */
1101: QueueCell qc = new QueueCell(requested, t);
1102:
1103: //qc.timeout = timeout;
1104: qc.priority2 = priority;
1105:
1106: final int size = waiters.size();
1107:
1108: if (size == 0) {
1109: waiters.add(qc);
1110: } else if (qc.getPriority() == Integer.MAX_VALUE) {
1111: waiters.add(0, qc);
1112: } else {
1113: QueueCell cursor;
1114: int i = 0;
1115:
1116: do {
1117: cursor = waiters.get(i);
1118:
1119: if (cursor.getPriority() < qc.getPriority()) {
1120: waiters.add(i, qc);
1121:
1122: break;
1123: }
1124:
1125: i++;
1126: } while (i < size);
1127:
1128: if (i == size) {
1129: waiters.add(qc);
1130: }
1131: }
1132:
1133: return qc;
1134: }
1135:
1136: /** Scans through waiters and wakes up them */
1137: private void wakeUpOthers() {
1138: if ((grantedMode == X) || (grantedMode == CHAIN)) {
1139: // defensive
1140: throw new IllegalStateException();
1141: }
1142:
1143: if (waiters.size() == 0) {
1144: return;
1145: }
1146:
1147: for (int i = 0; i < waiters.size(); i++) {
1148: QueueCell qc = waiters.get(i);
1149:
1150: synchronized (qc) {
1151: if (qc.isGotOut()) {
1152: // bogus waiter
1153: waiters.remove(i--);
1154:
1155: continue;
1156: }
1157:
1158: if (isCompatible(qc.mode)) { // woken S -> should I wake X? -> no
1159: waiters.remove(i--);
1160: qc.wakeMeUp();
1161: setGrantedMode(qc.mode);
1162:
1163: if (getThreadInfo(qc.t) == null) {
1164: // force to have a record since recorded threads
1165: // do not use isCompatible call
1166: ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
1167: ti.forced = true;
1168:
1169: if (qc.mode == S) {
1170: readersNo++;
1171: }
1172:
1173: registeredThreads.put(qc.t, ti);
1174: }
1175: } else {
1176: setGrantedMode(CHAIN);
1177:
1178: break;
1179: }
1180: }
1181: // sync (qc)
1182: }
1183: }
1184:
1185: private void wakeUpReaders() {
1186: assert (grantedMode == NONE) || (grantedMode == S);
1187:
1188: if (waiters.size() == 0) {
1189: return;
1190: }
1191:
1192: for (int i = 0; i < waiters.size(); i++) {
1193: QueueCell qc = waiters.get(i);
1194:
1195: synchronized (qc) {
1196: if (qc.isGotOut()) {
1197: // bogus waiter
1198: waiters.remove(i--);
1199:
1200: continue;
1201: }
1202:
1203: if (qc.mode == S) { // readers only
1204: waiters.remove(i--);
1205: qc.wakeMeUp();
1206: setGrantedMode(S);
1207:
1208: if (getThreadInfo(qc.t) == null) {
1209: // force to have a record since recorded threads
1210: // do not use isCompatible call
1211: ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
1212: ti.forced = true;
1213: readersNo++;
1214: registeredThreads.put(qc.t, ti);
1215: }
1216: }
1217: }
1218: // sync (qc)
1219: }
1220: }
1221:
1222: /** Posts new request for current thread
1223: * @param mutexMode mutex mode for which the action is rquested
1224: * @param run the action
1225: */
1226: private void postRequest(final int mutexMode, final Runnable run,
1227: Executor exec) {
1228: if (this == EVENT) {
1229: doEventRequest(run);
1230:
1231: return;
1232: }
1233: if (wrapper != null) {
1234: Mutex m = (Mutex) LOCK;
1235: m.postRequest(mutexMode, run, wrapper);
1236: return;
1237: }
1238:
1239: final Thread t = Thread.currentThread();
1240: ThreadInfo info;
1241:
1242: synchronized (LOCK) {
1243: info = getThreadInfo(t);
1244:
1245: if (info != null) {
1246: // the same mode and mutex is not entered in the other mode
1247: // assert (mutexMode == S || mutexMode == X)
1248: if ((mutexMode == info.mode)
1249: && (info.counts[(S + X) - mutexMode] == 0)) {
1250: enter(mutexMode, t, true);
1251: } else { // the mutex is held but can not be entered in X mode
1252: info.enqueue(mutexMode, run);
1253:
1254: return;
1255: }
1256: }
1257: }
1258:
1259: // this mutex is not held
1260: if (info == null) {
1261: if (exec != null) {
1262: class Exec implements Runnable {
1263: public void run() {
1264: enter(mutexMode, t, true);
1265: try {
1266: run.run();
1267: } finally {
1268: leave(t);
1269: }
1270: }
1271: }
1272: exec.execute(new Exec());
1273: return;
1274: }
1275:
1276: enter(mutexMode, t, true);
1277: try {
1278: run.run();
1279: } finally {
1280: leave(t);
1281: }
1282:
1283: return;
1284: }
1285:
1286: // run it immediately
1287: // info != null so enter(...) succeeded
1288: try {
1289: run.run();
1290: } finally {
1291: leave(t);
1292: }
1293: }
1294:
1295: /** @param requested is requested mode of locking
1296: * @return <tt>true</tt> if and only if current mode and requested mode are compatible
1297: */
1298: private boolean isCompatible(int requested) {
1299: // allow next reader in even in chained mode, if it was read access before
1300: if (requested == S && grantedMode == CHAIN && origMode == S)
1301: return true;
1302: return cmatrix[requested][grantedMode];
1303: }
1304:
1305: private ThreadInfo getThreadInfo(Thread t) {
1306: return registeredThreads.get(t);
1307: }
1308:
1309: private boolean canUpgrade(int threadGranted, int requested) {
1310: return (threadGranted == S) && (requested == X)
1311: && (readersNo == 1);
1312: }
1313:
1314: // -------------------------------- WRAPPERS --------------------------------
1315:
1316: private <T> T doWrapperAccess(final ExceptionAction<T> action,
1317: final Runnable runnable, final boolean readOnly)
1318: throws MutexException {
1319: class R implements Runnable {
1320: T ret;
1321: MutexException e;
1322:
1323: public void run() {
1324: Mutex m = (Mutex) LOCK;
1325: try {
1326: if (readOnly) {
1327: if (action != null) {
1328: ret = m.readAccess(action);
1329: } else {
1330: m.readAccess(runnable);
1331: }
1332: } else {
1333: if (action != null) {
1334: ret = m.writeAccess(action);
1335: } else {
1336: m.writeAccess(runnable);
1337: }
1338: }
1339: } catch (MutexException ex) {
1340: this .e = ex;
1341: }
1342: }
1343: }
1344: R run = new R();
1345: Mutex m = (Mutex) LOCK;
1346: if (m.isWriteAccess() || m.isReadAccess()) {
1347: run.run();
1348: } else {
1349: wrapper.execute(run);
1350: }
1351: if (run.e != null) {
1352: throw run.e;
1353: }
1354: return run.ret;
1355: }
1356:
1357: // ------------------------------- EVENT METHODS ----------------------------
1358:
1359: /** Runs the runnable in event queue, either immediatelly,
1360: * or it posts it into the queue.
1361: */
1362: private static void doEvent(Runnable run) {
1363: if (EventQueue.isDispatchThread()) {
1364: run.run();
1365: } else {
1366: EventQueue.invokeLater(run);
1367: }
1368: }
1369:
1370: /** Methods for access to event queue.
1371: * @param run runabble to post later
1372: */
1373: private static void doEventRequest(Runnable run) {
1374: EventQueue.invokeLater(run);
1375: }
1376:
1377: /** Methods for access to event queue and waiting for result.
1378: * @param run runnable to post later
1379: */
1380: private static <T> T doEventAccess(final ExceptionAction<T> run)
1381: throws MutexException {
1382: if (isDispatchThread()) {
1383: try {
1384: return run.run();
1385: } catch (RuntimeException e) {
1386: throw e;
1387: } catch (Exception e) {
1388: throw new MutexException(e);
1389: }
1390: }
1391:
1392: final Throwable[] arr = new Throwable[1];
1393:
1394: try {
1395: final List<T> res = new ArrayList<T>(1);
1396: class AWTWorker implements Runnable {
1397: public void run() {
1398: try {
1399: res.add(run.run());
1400: } catch (Exception e) {
1401: arr[0] = e;
1402: } catch (LinkageError e) {
1403: // #20467
1404: arr[0] = e;
1405: } catch (StackOverflowError e) {
1406: // #20467
1407: arr[0] = e;
1408: }
1409: }
1410: }
1411:
1412: AWTWorker w = new AWTWorker();
1413: EventQueue.invokeAndWait(w);
1414:
1415: if (arr[0] == null) {
1416: return res.get(0);
1417: }
1418: } catch (InterruptedException e) {
1419: arr[0] = e;
1420: } catch (InvocationTargetException e) {
1421: arr[0] = e;
1422: }
1423:
1424: if (arr[0] instanceof RuntimeException) {
1425: throw (RuntimeException) arr[0];
1426: }
1427:
1428: throw notifyException(arr[0]);
1429: }
1430:
1431: /** @return true iff current thread is EventDispatchThread */
1432: static boolean isDispatchThread() {
1433: boolean dispatch = EventQueue.isDispatchThread();
1434:
1435: if (!dispatch
1436: && (Utilities.getOperatingSystem() == Utilities.OS_SOLARIS)) {
1437: // on solaris the event queue is not always recognized correctly
1438: // => try to guess by name
1439: dispatch = (Thread.currentThread().getClass().getName()
1440: .indexOf("EventDispatchThread") >= 0); // NOI18N
1441: }
1442:
1443: return dispatch;
1444: }
1445:
1446: /** Notify exception and returns new MutexException */
1447: private static final MutexException notifyException(Throwable t) {
1448: if (t instanceof InvocationTargetException) {
1449: t = unfoldInvocationTargetException((InvocationTargetException) t);
1450: }
1451:
1452: if (t instanceof Error) {
1453: annotateEventStack(t);
1454: throw (Error) t;
1455: }
1456:
1457: if (t instanceof RuntimeException) {
1458: annotateEventStack(t);
1459: throw (RuntimeException) t;
1460: }
1461:
1462: MutexException exc = new MutexException((Exception) t);
1463: exc.initCause(t);
1464:
1465: return exc;
1466: }
1467:
1468: private static final void annotateEventStack(Throwable t) {
1469: //ErrorManager.getDefault().annotate(t, new Exception("Caught here in mutex")); // NOI18N
1470: }
1471:
1472: private static final Throwable unfoldInvocationTargetException(
1473: InvocationTargetException e) {
1474: Throwable ret;
1475:
1476: do {
1477: ret = e.getTargetException();
1478:
1479: if (ret instanceof InvocationTargetException) {
1480: e = (InvocationTargetException) ret;
1481: } else {
1482: e = null;
1483: }
1484: } while (e != null);
1485:
1486: return ret;
1487: }
1488:
1489: // --------------------------------------------- END OF EVENT METHODS ------------------------------
1490:
1491: /** Action to be executed in a mutex without throwing any checked exceptions.
1492: * Unchecked exceptions will be propagated to calling code.
1493: * @param T the type of object to return
1494: */
1495: public interface Action<T> extends ExceptionAction<T> {
1496: /** Execute the action.
1497: * @return any object, then returned from {@link Mutex#readAccess(Mutex.Action)} or {@link Mutex#writeAccess(Mutex.Action)}
1498: */
1499: T run();
1500: }
1501:
1502: /** Action to be executed in a mutex, possibly throwing checked exceptions.
1503: * May throw a checked exception, in which case calling
1504: * code should catch the encapsulating exception and rethrow the
1505: * real one.
1506: * Unchecked exceptions will be propagated to calling code without encapsulation.
1507: * @param T the type of object to return
1508: */
1509: public interface ExceptionAction<T> {
1510: /** Execute the action.
1511: * Can throw an exception.
1512: * @return any object, then returned from {@link Mutex#readAccess(Mutex.ExceptionAction)} or {@link Mutex#writeAccess(Mutex.ExceptionAction)}
1513: * @exception Exception any exception the body needs to throw
1514: */
1515: T run() throws Exception;
1516: }
1517:
1518: private static final class ThreadInfo {
1519: /** t is forcibly sent from waiters to enter() by wakeUpOthers() */
1520: boolean forced;
1521:
1522: /** ThreadInfo for this Thread */
1523: final Thread t;
1524:
1525: /** granted mode */
1526: int mode;
1527:
1528: // 0 - NONE, 1 - CHAIN, 2 - X, 3 - S
1529:
1530: /** enter counter */
1531: int[] counts;
1532:
1533: /** queue of runnable rquests that are to be executed (in X mode) right after S mode is left
1534: * deadlock avoidance technique
1535: */
1536: List<Runnable>[] queues;
1537:
1538: /** value of counts[S] when the mode was upgraded
1539: * rsnapshot works as follows:
1540: * if a thread holds the mutex in the S mode and it reenters the mutex
1541: * and requests X and the mode can be granted (no other readers) then this
1542: * variable is set to counts[S]. This is used in the leave method in the X branch.
1543: * (X mode is granted by other words)
1544: * If rsnapshot is less than counts[S] then the counter is decremented etc. If the rsnapshot is
1545: * equal to count[S] then count[X] is decremented. If the X counter is zeroed then
1546: * rsnapshot is zeroed as well and current mode is downgraded to S mode.
1547: * rsnapshot gets less than counts[S] if current mode is X and the mutex is reentered
1548: * with S request.
1549: */
1550: int rsnapshot;
1551:
1552: @SuppressWarnings("unchecked")
1553: public ThreadInfo(Thread t, int mode) {
1554: this .t = t;
1555: this .mode = mode;
1556: this .counts = new int[MODE_COUNT];
1557: this .queues = (List<Runnable>[]) new List[MODE_COUNT];
1558: counts[mode] = 1;
1559: }
1560:
1561: public String toString() {
1562: return super .toString() + " thread: " + t + " mode: "
1563: + mode + " X: " + counts[2] + " S: " + counts[3]; // NOI18N
1564: }
1565:
1566: /** Adds the Runnable into the queue of waiting requests */
1567: public void enqueue(int mode, Runnable run) {
1568: if (queues[mode] == null) {
1569: queues[mode] = new ArrayList<Runnable>(13);
1570: }
1571:
1572: queues[mode].add(run);
1573: }
1574:
1575: /** @return a List of enqueued Runnables - may be null */
1576: public List dequeue(int mode) {
1577: List ret = queues[mode];
1578: queues[mode] = null;
1579:
1580: return ret;
1581: }
1582:
1583: public int getRunnableCount(int mode) {
1584: return ((queues[mode] == null) ? 0 : queues[mode].size());
1585: }
1586: }
1587:
1588: /** This class is defined only for better understanding of thread dumps where are informations like
1589: * java.lang.Object@xxxxxxxx owner thread_x
1590: * wait for enter thread_y
1591: */
1592: private static final class InternalLock {
1593: InternalLock() {
1594: }
1595: }
1596:
1597: private static final class QueueCell {
1598: int mode;
1599: Thread t;
1600: boolean signal;
1601: boolean left;
1602:
1603: /** priority of the cell */
1604: int priority2;
1605:
1606: public QueueCell(int mode, Thread t) {
1607: this .mode = mode;
1608: this .t = t;
1609: this .left = false;
1610: this .priority2 = 0;
1611: }
1612:
1613: public String toString() {
1614: return super .toString() + " mode: " + mode + " thread: "
1615: + t; // NOI18N
1616: }
1617:
1618: /** @return priority of this cell */
1619: public long getPriority() {
1620: return ((priority2 == 0) ? t.getPriority() : priority2);
1621: }
1622:
1623: /** @return true iff the thread left sleep */
1624: public boolean isGotOut() {
1625: return left;
1626: }
1627:
1628: /** current thread will sleep until wakeMeUp is called
1629: * if wakeMeUp was already called then the thread will not sleep
1630: */
1631: public synchronized void sleep() {
1632: try {
1633: while (!signal) {
1634: try {
1635: wait();
1636:
1637: return;
1638: } catch (InterruptedException e) {
1639: Logger.getLogger(Mutex.class.getName()).log(
1640: Level.WARNING, null, e);
1641: }
1642: }
1643: } finally {
1644: left = true;
1645: }
1646: }
1647:
1648: /** sends signal to a sleeper - to a thread that is in the sleep() */
1649: public void wakeMeUp() {
1650: signal = true;
1651: notifyAll();
1652: }
1653: }
1654:
1655: /** Provides access to Mutex's internal methods.
1656: *
1657: * This class can be used when one wants to avoid creating a
1658: * bunch of Runnables. Instead,
1659: * <pre>
1660: * try {
1661: * enterXAccess ();
1662: * yourCustomMethod ();
1663: * } finally {
1664: * exitXAccess ();
1665: * }
1666: * </pre>
1667: * can be used.
1668: *
1669: * You must, however, control the related Mutex, i.e. you must be creator of
1670: * the Mutex.
1671: *
1672: * @since 1.17
1673: */
1674: public static final class Privileged {
1675: private Mutex parent;
1676:
1677: final void setParent(Mutex parent) {
1678: this .parent = parent;
1679: }
1680:
1681: public void enterReadAccess() {
1682: parent.readEnter(Thread.currentThread());
1683: }
1684:
1685: public void enterWriteAccess() {
1686: parent.writeEnter(Thread.currentThread());
1687: }
1688:
1689: public void exitReadAccess() {
1690: parent.leave(Thread.currentThread());
1691: }
1692:
1693: public void exitWriteAccess() {
1694: parent.leave(Thread.currentThread());
1695: }
1696: }
1697:
1698: private void setGrantedMode(int mode) {
1699: if (grantedMode != CHAIN && mode == CHAIN) {
1700: origMode = grantedMode;
1701: }
1702: grantedMode = mode;
1703: }
1704: }
|