KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > util > Mutex


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.util;
21
22 import java.awt.EventQueue JavaDoc;
23 import java.lang.reflect.InvocationTargetException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.LinkedList JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.logging.Level JavaDoc;
30 import java.util.logging.Logger JavaDoc;
31
32 /** Read-many/write-one lock.
33 * Allows control over resources that
34 * can be read by several readers at once but only written by one writer.
35 * <P>
36 * It is guaranteed that if you are a writer you can also enter the
37 * mutex as a reader. Conversely, if you are the <em>only</em> reader you
38 * can enter the mutex as a writer, but you'll be warned because it is very
39 * deadlock prone (two readers trying to get write access concurently).
40 * <P>
41 * If the mutex is used only by one thread, the thread can repeatedly
42 * enter it as a writer or reader. So one thread can never deadlock itself,
43 * whichever order operations are performed in.
44 * <P>
45 * There is no strategy to prevent starvation.
46 * Even if there is a writer waiting to enter, another reader might enter
47 * the section instead.
48 * <P>
49 * Examples of use:
50 *
51 * <p><code><PRE>
52 * Mutex m = new Mutex ();
53 *
54 * // Grant write access, compute an integer and return it:
55 * return (Integer)m.writeAccess (new Mutex.Action () {
56 * public Object run () {
57 * return new Integer (1);
58 * }
59 * });
60 *
61 * // Obtain read access, do some computation, possibly throw an IOException:
62 * try {
63 * m.readAccess (new Mutex.ExceptionAction () {
64 * public Object run () throws IOException {
65 * if (...) throw new IOException ();
66 *
67 * return null;
68 * }
69 * });
70 * } catch (MutexException ex) {
71 * throw (IOException)ex.getException ();
72 * }
73 *
74 * // check whether you are already in read access
75 * if (m.isReadAccess ()) {
76 * // do your work
77 * }
78 * </PRE></code>
79 *
80 * @author Ales Novak
81 */

82 public final class Mutex extends Object JavaDoc {
83     /** counter of created mutexes */
84     private static int counter;
85
86     /** logger for things that happen in mutex */
87     private static Logger JavaDoc LOG = Logger.getLogger(Mutex.class.getName());
88
89     /** Mutex that allows code to be synchronized with the AWT event dispatch thread.
90      * <P>
91      * When the Mutex methods are invoked on this mutex, the methods' semantics
92      * change as follows:
93      * <UL>
94      * <LI>The {@link #isReadAccess} and {@link #isWriteAccess} methods
95      * return <code>true</code> if the current thread is the event dispatch thread
96      * and false otherwise.
97      * <LI>The {@link #postReadRequest} and {@link #postWriteRequest} methods
98      * asynchronously execute the {@link java.lang.Runnable} passed in their
99      * <code>run</code> parameter on the event dispatch thead.
100      * <LI>The {@link #readAccess(java.lang.Runnable)} and
101      * {@link #writeAccess(java.lang.Runnable)} methods asynchronously execute the
102      * {@link java.lang.Runnable} passed in their <code>run</code> parameter
103      * on the event dispatch thread, unless the current thread is
104      * the event dispatch thread, in which case
105      * <code>run.run()</code> is immediately executed.
106      * <LI>The {@link #readAccess(Mutex.Action)},
107      * {@link #readAccess(Mutex.ExceptionAction action)},
108      * {@link #writeAccess(Mutex.Action action)} and
109      * {@link #writeAccess(Mutex.ExceptionAction action)}
110      * methods synchronously execute the {@link Mutex.ExceptionAction}
111      * passed in their <code>action</code> parameter on the event dispatch thread,
112      * unless the current thread is the event dispatch thread, in which case
113      * <code>action.run()</code> is immediately executed.
114      * </UL>
115      */

116     public static final Mutex EVENT = new Mutex();
117
118     /** this is used from tests to prevent upgrade from readAccess to writeAccess
119      * by strictly throwing exception. Otherwise we just notify that using ErrorManager.
120      */

121     static boolean beStrict;
122
123     // lock mode constants
124

125     /** Lock free */
126     private static final int NONE = 0x0;
127
128     /** Enqueue all requests */
129     private static final int CHAIN = 0x1;
130
131     /** eXclusive */
132     private static final int X = 0x2;
133
134     /** Shared */
135     private static final int S = 0x3;
136
137     /** number of modes */
138     private static final int MODE_COUNT = 0x4;
139
140     /** compatibility matrix */
141
142     // [requested][granted]
143
private static final boolean[][] cmatrix = {null,
144         null, // NONE, CHAIN
145
{ true, false, false, false },{ true, false, false, true }
146     };
147
148     /** granted mode */
149     private int grantedMode = NONE;
150     
151     /** The mode the mutex was in before it started chaining */
152     private int origMode;
153
154     /** protects internal data structures */
155     private /*final*/ Object JavaDoc LOCK;
156
157     /** threads that - owns or waits for this mutex */
158     private /*final*/ Map JavaDoc<Thread JavaDoc,ThreadInfo> registeredThreads;
159
160     /** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") */
161
162     // NOI18N
163
private int readersNo = 0;
164
165     /** a queue of waiting threads for this mutex */
166     private List JavaDoc<QueueCell> waiters;
167
168     /** identification of the mutex */
169     private int cnt;
170
171     /** Enhanced constructor that permits specifying an object to use as a lock.
172     * The lock is used on entry and exit to {@link #readAccess} and during the
173     * whole execution of {@link #writeAccess}. The ability to specify locks
174     * allows several <code>Mutex</code>es to synchronize on one object or to synchronize
175     * a mutex with another critical section.
176     *
177     * @param lock lock to use
178     */

179     public Mutex(Object JavaDoc lock) {
180         init(lock);
181     }
182
183     /** Default constructor.
184     */

185     public Mutex() {
186         init(new InternalLock());
187     }
188
189     /** @param privileged can enter privileged states of this Mutex
190      * This helps avoid creating of custom Runnables.
191      */

192     public Mutex(Privileged privileged) {
193         if (privileged == null) {
194             throw new IllegalArgumentException JavaDoc("privileged == null"); //NOI18N
195
} else {
196             init(new InternalLock());
197             privileged.setParent(this);
198         }
199     }
200
201     /** Initiates this Mutex */
202     private void init(Object JavaDoc lock) {
203         this.LOCK = lock;
204         this.registeredThreads = new HashMap JavaDoc<Thread JavaDoc,ThreadInfo>(7);
205         this.waiters = new LinkedList JavaDoc<QueueCell>();
206         this.cnt = counter++;
207         if (LOG.isLoggable(Level.FINE)) {
208             LOG.log(Level.FINE, "[" + cnt + "] created here", new Exception JavaDoc());
209         }
210     }
211
212     /** Run an action only with read access.
213     * See class description re. entering for write access within the dynamic scope.
214     * @param action the action to perform
215     * @return the object returned from {@link Mutex.Action#run}
216     */

217     public <T> T readAccess(Action<T> action) {
218         if (this == EVENT) {
219             try {
220                 return doEventAccess(action);
221             } catch (MutexException e) {
222                 throw (InternalError JavaDoc) new InternalError JavaDoc("Exception from non-Exception Action").initCause(e.getException()); // NOI18N
223
}
224         }
225
226         Thread JavaDoc t = Thread.currentThread();
227         readEnter(t);
228
229         try {
230             return action.run();
231         } finally {
232             leave(t);
233         }
234     }
235
236     /** Run an action with read access and possibly throw a checked exception.
237     * The exception if thrown is then encapsulated
238     * in a <code>MutexException</code> and thrown from this method. One is encouraged
239     * to catch <code>MutexException</code>, obtain the inner exception, and rethrow it.
240     * Here is an example:
241     * <p><code><PRE>
242     * try {
243     * mutex.readAccess (new ExceptionAction () {
244     * public void run () throws IOException {
245     * throw new IOException ();
246     * }
247     * });
248     * } catch (MutexException ex) {
249     * throw (IOException) ex.getException ();
250     * }
251     * </PRE></code>
252     * Note that <em>runtime exceptions</em> are always passed through, and neither
253     * require this invocation style, nor are encapsulated.
254     * @param action the action to execute
255     * @return the object returned from {@link Mutex.ExceptionAction#run}
256     * @exception MutexException encapsulates a user exception
257     * @exception RuntimeException if any runtime exception is thrown from the run method
258     * @see #readAccess(Mutex.Action)
259     */

260     public <T> T readAccess(ExceptionAction<T> action) throws MutexException {
261         if (this == EVENT) {
262             return doEventAccess(action);
263         }
264
265         Thread JavaDoc t = Thread.currentThread();
266         readEnter(t);
267
268         try {
269             return action.run();
270         } catch (RuntimeException JavaDoc e) {
271             throw e;
272         } catch (Exception JavaDoc e) {
273             throw new MutexException(e);
274         } finally {
275             leave(t);
276         }
277     }
278
279     /** Run an action with read access, returning no result.
280     * It may be run asynchronously.
281     *
282     * @param action the action to perform
283     * @see #readAccess(Mutex.Action)
284     */

285     public void readAccess(final Runnable JavaDoc action) {
286         if (this == EVENT) {
287             doEvent(action);
288
289             return;
290         }
291
292         Thread JavaDoc t = Thread.currentThread();
293         readEnter(t);
294
295         try {
296             action.run();
297         } finally {
298             leave(t);
299         }
300     }
301
302     /** Run an action with write access.
303     * The same thread may meanwhile reenter the mutex; see the class description for details.
304     *
305     * @param action the action to perform
306     * @return the result of {@link Mutex.Action#run}
307     */

308     public <T> T writeAccess(Action<T> action) {
309         if (this == EVENT) {
310             try {
311                 return doEventAccess(action);
312             } catch (MutexException e) {
313                 throw (InternalError JavaDoc) new InternalError JavaDoc("Exception from non-Exception Action").initCause(e.getException()); // NOI18N
314
}
315         }
316
317         Thread JavaDoc t = Thread.currentThread();
318         writeEnter(t);
319
320         try {
321             return action.run();
322         } finally {
323             leave(t);
324         }
325     }
326
327     /** Run an action with write access and possibly throw an exception.
328     * Here is an example:
329     * <p><code><PRE>
330     * try {
331     * mutex.writeAccess (new ExceptionAction () {
332     * public void run () throws IOException {
333     * throw new IOException ();
334     * }
335     * });
336     * } catch (MutexException ex) {
337     * throw (IOException) ex.getException ();
338     * }
339     * </PRE></code>
340     *
341     * @param action the action to execute
342     * @return the result of {@link Mutex.ExceptionAction#run}
343     * @exception MutexException an encapsulated checked exception, if any
344     * @exception RuntimeException if a runtime exception is thrown in the action
345     * @see #writeAccess(Mutex.Action)
346     * @see #readAccess(Mutex.ExceptionAction)
347     */

348     public <T> T writeAccess(ExceptionAction<T> action) throws MutexException {
349         if (this == EVENT) {
350             return doEventAccess(action);
351         }
352
353         Thread JavaDoc t = Thread.currentThread();
354         writeEnter(t);
355
356         try {
357             return action.run();
358         } catch (RuntimeException JavaDoc e) {
359             throw e;
360         } catch (Exception JavaDoc e) {
361             throw new MutexException(e);
362         } finally {
363             leave(t);
364         }
365     }
366
367     /** Run an action with write access and return no result.
368     * It may be run asynchronously.
369     *
370     * @param action the action to perform
371     * @see #writeAccess(Mutex.Action)
372     * @see #readAccess(Runnable)
373     */

374     public void writeAccess(final Runnable JavaDoc action) {
375         if (this == EVENT) {
376             doEvent(action);
377
378             return;
379         }
380
381         Thread JavaDoc t = Thread.currentThread();
382         writeEnter(t);
383
384         try {
385             action.run();
386         } finally {
387             leave(t);
388         }
389     }
390
391     /** Tests whether this thread has already entered the mutex in read access.
392      * If it returns true, calling <code>readAccess</code>
393      * will be executed immediatelly
394      * without any blocking.
395      * Calling <code>postWriteAccess</code> will delay the execution
396      * of its <code>Runnable</code> until a readAccess section is over
397      * and calling <code>writeAccess</code> is strongly prohibited and will
398      * result in a warning as a deadlock prone behaviour.
399      * <p><strong>Warning:</strong> since a thread with write access automatically
400      * has effective read access as well (whether or not explicitly requested), if
401      * you want to check whether a thread can read some data, you should check for
402      * either kind of access, e.g.:
403      * <pre>assert myMutex.isReadAccess() || myMutex.isWriteAccess();</pre>
404      *
405      * @return true if the thread is in read access section
406      * @since 4.48
407      */

408     public boolean isReadAccess() {
409         if (this == EVENT) {
410             return javax.swing.SwingUtilities.isEventDispatchThread();
411         }
412
413         Thread JavaDoc t = Thread.currentThread();
414         ThreadInfo info;
415
416         synchronized (LOCK) {
417             info = getThreadInfo(t);
418
419             if (info != null) {
420                 if (info.counts[S] > 0) {
421                     return true;
422                 }
423             }
424         }
425
426         return false;
427     }
428
429     /** Tests whether this thread has already entered the mutex in write access.
430      * If it returns true, calling <code>writeAccess</code> will be executed
431      * immediatelly without any other blocking. <code>postReadAccess</code>
432      * will be delayed until a write access runnable is over.
433      *
434      * @return true if the thread is in write access section
435      * @since 4.48
436      */

437     public boolean isWriteAccess() {
438         if (this == EVENT) {
439             return javax.swing.SwingUtilities.isEventDispatchThread();
440         }
441
442         Thread JavaDoc t = Thread.currentThread();
443         ThreadInfo info;
444
445         synchronized (LOCK) {
446             info = getThreadInfo(t);
447
448             if (info != null) {
449                 if (info.counts[X] > 0) {
450                     return true;
451                 }
452             }
453         }
454
455         return false;
456     }
457
458     /** Posts a read request. This request runs immediately iff
459      * this Mutex is in the shared mode or this Mutex is not contended
460      * at all.
461      *
462      * This request is delayed if this Mutex is in the exclusive
463      * mode and is held by this thread, until the exclusive is left.
464      *
465      * Finally, this request blocks, if this Mutex is in the exclusive
466      * mode and is held by another thread.
467      *
468      * <p><strong>Warning:</strong> this method blocks.</p>
469      *
470      * @param run runnable to run
471      */

472     public void postReadRequest(final Runnable JavaDoc run) {
473         postRequest(S, run);
474     }
475
476     /** Posts a write request. This request runs immediately iff
477      * this Mutex is in the "pure" exclusive mode, i.e. this Mutex
478      * is not reentered in shared mode after the exclusive mode
479      * was acquired. Otherwise it is delayed until all read requests
480      * are executed.
481      *
482      * This request runs immediately if this Mutex is not contended at all.
483      *
484      * This request blocks if this Mutex is in the shared mode.
485      *
486      * <p><strong>Warning:</strong> this method blocks.</p>
487      * @param run runnable to run
488      */

489     public void postWriteRequest(Runnable JavaDoc run) {
490         postRequest(X, run);
491     }
492
493     /** toString */
494     public String JavaDoc toString() {
495         if (this == EVENT) {
496             return "Mutex.EVENT"; // NOI18N
497
}
498
499         String JavaDoc newline = System.getProperty("line.separator");
500         StringBuffer JavaDoc sbuff = new StringBuffer JavaDoc(512);
501
502         synchronized (LOCK) {
503             sbuff.append("threads: ").append(registeredThreads).append(newline); // NOI18N
504
sbuff.append("readersNo: ").append(readersNo).append(newline); // NOI18N
505
sbuff.append("waiters: ").append(waiters).append(newline); // NOI18N
506
sbuff.append("grantedMode: ").append(grantedMode).append(newline); // NOI18N
507
}
508
509         return sbuff.toString();
510     }
511
512     // priv methods -----------------------------------------
513

514     /** enters this mutex for writing */
515     private void writeEnter(Thread JavaDoc t) {
516         enter(X, t, true);
517     }
518
519     /** enters this mutex for reading */
520     private void readEnter(Thread JavaDoc t) {
521         enter(S, t, true);
522     }
523
524     private void doLog(String JavaDoc action, Object JavaDoc ... params) {
525         String JavaDoc tid = Integer.toHexString(Thread.currentThread().hashCode());
526         LOG.log(Level.FINE, "[#" + cnt + "@" + tid + "] " + action, params);
527     }
528     
529     /** enters this mutex with given mode
530     * @param requested one of S, X
531     * @param t
532     */

533     private boolean enter(int requested, Thread JavaDoc t, boolean block) {
534         boolean log = LOG.isLoggable(Level.FINE);
535
536         if (log) doLog("Entering {0}, {1}", requested, block); // NOI18N
537

538         boolean ret = enterImpl(requested, t, block);
539
540         if (log) doLog("Entering exit: {0}", ret); // NOI18N
541

542         return ret;
543     }
544
545     private boolean enterImpl(int requested, Thread JavaDoc t, boolean block) {
546         QueueCell cell = null;
547         int loopc = 0;
548
549         for (;;) {
550             loopc++;
551             synchronized (LOCK) {
552                 // does the thread reenter this mutex?
553
ThreadInfo info = getThreadInfo(t);
554
555                 if (info != null) {
556                     if (grantedMode == NONE) {
557                         // defensive
558
throw new IllegalStateException JavaDoc();
559                     }
560                     // reenters
561
// requested == S -> always succeeds
562
// info.mode == X -> always succeeds
563
if (((info.mode == S) && (grantedMode == X)) ||
564                         ((info.mode == X) && (grantedMode == S))) {
565                         // defensive
566
throw new IllegalStateException JavaDoc();
567                     }
568                     if ((info.mode == X) || (info.mode == requested)) {
569                         if (info.forced) {
570                             info.forced = false;
571                         } else {
572                             if ((requested == X) && (info.counts[S] > 0)) {
573                                 IllegalStateException JavaDoc e = new IllegalStateException JavaDoc("WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
574
575                                 if (beStrict) {
576                                     throw e;
577                                 }
578                                 Exceptions.printStackTrace(e);
579                             }
580                             info.counts[requested]++;
581                             if ((requested == S) &&
582                                 (info.counts[requested] == 1)) {
583                                 readersNo++;
584                             }
585                         }
586                         return true;
587                     } else if (canUpgrade(info.mode, requested)) {
588                         IllegalStateException JavaDoc e = new IllegalStateException JavaDoc("WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
589
590                         if (beStrict) {
591                             throw e;
592                         }
593                         Exceptions.printStackTrace(e);
594                         info.mode = X;
595                         info.counts[requested]++;
596                         info.rsnapshot = info.counts[S];
597                         if (grantedMode == S) {
598                             setGrantedMode(X);
599                         } else if (grantedMode == X) {
600                             // defensive
601
throw new IllegalStateException JavaDoc();
602                         }
603                         // else if grantedMode == CHAIN - let it be
604
return true;
605                     } else {
606                         IllegalStateException JavaDoc e = new IllegalStateException JavaDoc("WARNING: Going from readAccess to writeAccess through queue, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
607
608                         if (beStrict) {
609                             throw e;
610                         }
611                         Exceptions.printStackTrace(e);
612                     }
613                 } else {
614                     if (isCompatible(requested)) {
615                         setGrantedMode(requested);
616                         registeredThreads.put(t,
617                                               info = new ThreadInfo(t, requested));
618                         if (requested == S) {
619                             readersNo++;
620                         }
621                         return true;
622                     }
623                 }
624                 if (!block) {
625                     return false;
626                 }
627                 setGrantedMode(CHAIN);
628                 cell = chain(requested, t, 0);
629             }
630             // sync
631
cell.sleep();
632         }
633          // for
634
}
635     
636     /** privilegedEnter serves for processing posted requests */
637     private boolean reenter(Thread JavaDoc t, int mode) {
638         boolean log = LOG.isLoggable(Level.FINE);
639
640         if (log) doLog("Re-Entering {0}", mode); // NOI18N
641

642         boolean ret = reenterImpl(t, mode);
643
644         if (log) doLog("Re-Entering exit: {0}", ret); // NOI18N
645

646         return ret;
647     }
648
649
650     private boolean reenterImpl(Thread JavaDoc t, int mode) {
651         // from leaveX -> grantedMode is NONE or S
652
if (mode == S) {
653             if ((grantedMode != NONE) && (grantedMode != S)) {
654                 throw new IllegalStateException JavaDoc(this.toString());
655             }
656
657             enter(mode, t, true);
658
659             return false;
660         }
661
662         // assert (mode == X)
663
ThreadInfo tinfo = getThreadInfo(t);
664         boolean chainFromLeaveX = ((grantedMode == CHAIN) && (tinfo != null) && (tinfo.counts[X] > 0));
665
666         // process grantedMode == X or CHAIN from leaveX OR grantedMode == NONE from leaveS
667
if ((grantedMode == X) || (grantedMode == NONE) || chainFromLeaveX) {
668             enter(mode, t, true);
669
670             return false;
671         } else { // remains grantedMode == CHAIN or S from leaveS, so it will be CHAIN
672

673             if (readersNo == 0) {
674                 throw new IllegalStateException JavaDoc(this.toString());
675             }
676
677             ThreadInfo info = new ThreadInfo(t, mode);
678             registeredThreads.put(t, info);
679
680             // prevent from grantedMode == NONE (another thread - leaveS)
681
readersNo += 2;
682
683             // prevent from new readers
684
setGrantedMode(CHAIN);
685
686             return true;
687         }
688          // else X means ERROR!!!
689
}
690
691     /** @param t holds S (one entry) and wants X, grantedMode != NONE && grantedMode != X */
692     private void privilegedEnter(Thread JavaDoc t, int mode) {
693         boolean decrease = true;
694
695         synchronized (LOCK) {
696             getThreadInfo(t);
697         }
698
699         for (;;) {
700             QueueCell cell;
701
702             synchronized (LOCK) {
703                 if (decrease) {
704                     decrease = false;
705                     readersNo -= 2;
706                 }
707
708                 // always chain this thread
709
// since there can be another one
710
// in the queue with higher priority
711
setGrantedMode(CHAIN);
712                 cell = chain(mode, t, Integer.MAX_VALUE);
713
714                 if (readersNo == 0) { // seems I may enter
715

716                     // no one has higher prio?
717
if (waiters.get(0) == cell) {
718                         waiters.remove(0);
719                         
720                         setGrantedMode(mode);
721
722                         return;
723                     } else {
724                         setGrantedMode(NONE);
725                         wakeUpOthers();
726                     }
727                 }
728             }
729              // synchronized (LOCK)
730

731             cell.sleep();
732
733             // cell already removed from waiters here
734
}
735     }
736
737     /** Leaves this mutex */
738     private void leave(Thread JavaDoc t) {
739         boolean log = LOG.isLoggable(Level.FINE);
740
741         if (log) doLog("Leaving {0}", grantedMode); // NOI18N
742

743         leaveImpl(t);
744
745         if (log) doLog("Leaving exit: {0}", grantedMode); // NOI18N
746
}
747
748     private void leaveImpl(Thread JavaDoc t) {
749         ThreadInfo info;
750         int postedMode = NONE;
751         boolean needLock = false;
752
753         synchronized (LOCK) {
754             info = getThreadInfo(t);
755
756             switch (grantedMode) {
757             case NONE:
758                 throw new IllegalStateException JavaDoc();
759
760             case CHAIN:
761
762                 if (info.counts[X] > 0) {
763                     // it matters that X is handled first - see ThreadInfo.rsnapshot
764
postedMode = leaveX(info);
765                 } else if (info.counts[S] > 0) {
766                     postedMode = leaveS(info);
767                 } else {
768                     throw new IllegalStateException JavaDoc();
769                 }
770
771                 break;
772
773             case X:
774                 postedMode = leaveX(info);
775
776                 break;
777
778             case S:
779                 postedMode = leaveS(info);
780
781                 break;
782             } // switch
783

784             // do not give up LOCK until queued runnables are run
785
if (postedMode != NONE) {
786                 int runsize = info.getRunnableCount(postedMode);
787
788                 if (runsize != 0) {
789                     needLock = reenter(t, postedMode); // grab lock
790
}
791             }
792         } // sync
793

794         // check posted requests
795
if ((postedMode != NONE) && (info.getRunnableCount(postedMode) > 0)) {
796             doLog("Processing posted requests: {0}", postedMode); // NOI18N
797
try {
798                 if (needLock) { // go from S to X or CHAIN
799
privilegedEnter(t, postedMode);
800                 }
801
802                 // holds postedMode lock here
803
List JavaDoc runnables = info.dequeue(postedMode);
804                 final int size = runnables.size();
805
806                 for (int i = 0; i < size; i++) {
807                     try {
808                         Runnable JavaDoc r = (Runnable JavaDoc) runnables.get(i);
809
810                         r.run();
811                     }
812                     catch (Exception JavaDoc e) {
813                         Exceptions.printStackTrace(e);
814                     }
815                     catch (StackOverflowError JavaDoc e) {
816                         // Try as hard as possible to get a real stack trace
817
e.printStackTrace();
818                         Exceptions.printStackTrace(e);
819                     }
820                     catch (ThreadDeath JavaDoc td) {
821                         throw td;
822                     }
823                     catch (Error JavaDoc e) {
824                         Exceptions.printStackTrace(e);
825                     }
826                 }
827                  // for
828

829                 // help gc
830
runnables = null;
831             } finally {
832                 leave(t); // release lock grabbed - shared
833
}
834         }
835          // mode
836
}
837
838     /** Leaves the lock supposing that info.counts[X] is greater than zero */
839     private int leaveX(ThreadInfo info) {
840         if ((info.counts[X] <= 0) || (info.rsnapshot > info.counts[S])) {
841             // defensive
842
throw new IllegalStateException JavaDoc();
843         }
844
845         if (info.rsnapshot == info.counts[S]) {
846             info.counts[X]--;
847
848             if (info.counts[X] == 0) {
849                 info.rsnapshot = 0;
850
851                 // downgrade the lock
852
if (info.counts[S] > 0) {
853                     info.mode = S;
854                     setGrantedMode(S);
855                 } else {
856                     info.mode = NONE;
857                     setGrantedMode(NONE);
858                     registeredThreads.remove(info.t);
859                 }
860
861                 if (info.getRunnableCount(S) > 0) {
862                     // wake up other readers of this mutex
863
wakeUpReaders();
864
865                     return S;
866                 }
867
868                 // mode has changed
869
wakeUpOthers();
870             }
871         } else {
872             // rsnapshot < counts[S]
873
if (info.counts[S] <= 0) {
874                 // defensive
875
throw new IllegalStateException JavaDoc();
876             }
877
878             if (--info.counts[S] == 0) {
879                 if (readersNo <= 0) {
880                     throw new IllegalStateException JavaDoc();
881                 }
882
883                 readersNo--;
884
885                 return X;
886             }
887         }
888
889         return NONE;
890     }
891
892     /** Leaves the lock supposing that info.counts[S] is greater than zero */
893     private int leaveS(ThreadInfo info) {
894         if ((info.counts[S] <= 0) || (info.counts[X] > 0)) {
895             // defensive
896
throw new IllegalStateException JavaDoc();
897         }
898
899         info.counts[S]--;
900
901         if (info.counts[S] == 0) {
902             // remove the thread
903
info.mode = NONE;
904             registeredThreads.remove(info.t);
905
906             // downsize readersNo
907
if (readersNo <= 0) {
908                 throw new IllegalStateException JavaDoc();
909             }
910
911             readersNo--;
912
913             if (readersNo == 0) {
914                 // set grantedMode to NONE
915
// and then wakeUp others - either immediately
916
// or in privelegedEnter()
917
setGrantedMode(NONE);
918
919                 if (info.getRunnableCount(X) > 0) {
920                     return X;
921                 }
922
923                 wakeUpOthers();
924             } else if (info.getRunnableCount(X) > 0) {
925                 return X;
926             } else if ((grantedMode == CHAIN) && (readersNo == 1)) {
927                 // can be the mode advanced from CHAIN? Examine first item of waiters!
928
for (int i = 0; i < waiters.size(); i++) {
929                     QueueCell qc = waiters.get(i);
930
931                     synchronized (qc) {
932                         if (qc.isGotOut()) {
933                             waiters.remove(i--);
934
935                             continue;
936                         }
937
938                         ThreadInfo tinfo = getThreadInfo(qc.t);
939
940                         if (tinfo != null) {
941                             if (tinfo.mode == S) {
942                                 if (qc.mode != X) {
943                                     // defensive
944
throw new IllegalStateException JavaDoc();
945                                 }
946
947                                 if (waiters.size() == 1) {
948                                     setGrantedMode(X);
949                                 }
950                                  // else let CHAIN
951

952                                 tinfo.mode = X;
953                                 waiters.remove(i);
954                                 qc.wakeMeUp();
955                             }
956                         }
957                          // else first request is a first X request of some thread
958

959                         break;
960                     }
961                      // sync (qc)
962
}
963                  // for
964
}
965              // else
966
}
967          // count[S] == 0
968

969         return NONE;
970     }
971
972     /** Adds this thread to the queue of waiting threads
973     * @warning LOCK must be held
974     */

975     private QueueCell chain(final int requested, final Thread JavaDoc t, final int priority) {
976         //long timeout = 0;
977

978         /*
979         if (killDeadlocksOn) {
980             checkDeadlock(requested, t);
981             timeout = (isDispatchThread() || checkAwtTreeLock() ? TIMEOUT : 0);
982         }
983         */

984         QueueCell qc = new QueueCell(requested, t);
985
986         //qc.timeout = timeout;
987
qc.priority2 = priority;
988
989         final int size = waiters.size();
990
991         if (size == 0) {
992             waiters.add(qc);
993         } else if (qc.getPriority() == Integer.MAX_VALUE) {
994             waiters.add(0, qc);
995         } else {
996             QueueCell cursor;
997             int i = 0;
998
999             do {
1000                cursor = waiters.get(i);
1001
1002                if (cursor.getPriority() < qc.getPriority()) {
1003                    waiters.add(i, qc);
1004
1005                    break;
1006                }
1007
1008                i++;
1009            } while (i < size);
1010
1011            if (i == size) {
1012                waiters.add(qc);
1013            }
1014        }
1015
1016        return qc;
1017    }
1018
1019    /** Scans through waiters and wakes up them */
1020    private void wakeUpOthers() {
1021        if ((grantedMode == X) || (grantedMode == CHAIN)) {
1022            // defensive
1023
throw new IllegalStateException JavaDoc();
1024        }
1025
1026        if (waiters.size() == 0) {
1027            return;
1028        }
1029
1030        for (int i = 0; i < waiters.size(); i++) {
1031            QueueCell qc = waiters.get(i);
1032
1033            synchronized (qc) {
1034                if (qc.isGotOut()) {
1035                    // bogus waiter
1036
waiters.remove(i--);
1037
1038                    continue;
1039                }
1040
1041                if (isCompatible(qc.mode)) { // woken S -> should I wake X? -> no
1042
waiters.remove(i--);
1043                    qc.wakeMeUp();
1044                    setGrantedMode(qc.mode);
1045
1046                    if (getThreadInfo(qc.t) == null) {
1047                        // force to have a record since recorded threads
1048
// do not use isCompatible call
1049
ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
1050                        ti.forced = true;
1051
1052                        if (qc.mode == S) {
1053                            readersNo++;
1054                        }
1055
1056                        registeredThreads.put(qc.t, ti);
1057                    }
1058                } else {
1059                    setGrantedMode(CHAIN);
1060
1061                    break;
1062                }
1063            }
1064             // sync (qc)
1065
}
1066    }
1067
1068    private void wakeUpReaders() {
1069        assert (grantedMode == NONE) || (grantedMode == S);
1070
1071        if (waiters.size() == 0) {
1072            return;
1073        }
1074
1075        for (int i = 0; i < waiters.size(); i++) {
1076            QueueCell qc = waiters.get(i);
1077
1078            synchronized (qc) {
1079                if (qc.isGotOut()) {
1080                    // bogus waiter
1081
waiters.remove(i--);
1082
1083                    continue;
1084                }
1085
1086                if (qc.mode == S) { // readers only
1087
waiters.remove(i--);
1088                    qc.wakeMeUp();
1089                    setGrantedMode(S);
1090
1091                    if (getThreadInfo(qc.t) == null) {
1092                        // force to have a record since recorded threads
1093
// do not use isCompatible call
1094
ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
1095                        ti.forced = true;
1096                        readersNo++;
1097                        registeredThreads.put(qc.t, ti);
1098                    }
1099                }
1100            }
1101             // sync (qc)
1102
}
1103    }
1104
1105    /** Posts new request for current thread
1106    * @param mutexMode mutex mode for which the action is rquested
1107    * @param run the action
1108    */

1109    private void postRequest(int mutexMode, Runnable JavaDoc run) {
1110        if (this == EVENT) {
1111            doEventRequest(run);
1112
1113            return;
1114        }
1115
1116        Thread JavaDoc t = Thread.currentThread();
1117        ThreadInfo info;
1118
1119        synchronized (LOCK) {
1120            info = getThreadInfo(t);
1121
1122            if (info != null) {
1123                // the same mode and mutex is not entered in the other mode
1124
// assert (mutexMode == S || mutexMode == X)
1125
if ((mutexMode == info.mode) && (info.counts[(S + X) - mutexMode] == 0)) {
1126                    enter(mutexMode, t, true);
1127                } else { // the mutex is held but can not be entered in X mode
1128
info.enqueue(mutexMode, run);
1129
1130                    return;
1131                }
1132            }
1133        }
1134
1135        // this mutex is not held
1136
if (info == null) {
1137            enter(mutexMode, t, true);
1138
1139            try {
1140                run.run();
1141            } finally {
1142                leave(t);
1143            }
1144
1145            return;
1146        }
1147
1148        // run it immediately
1149
// info != null so enter(...) succeeded
1150
try {
1151            run.run();
1152        } finally {
1153            leave(t);
1154        }
1155    }
1156
1157    /** @param requested is requested mode of locking
1158    * @return <tt>true</tt> if and only if current mode and requested mode are compatible
1159    */

1160    private boolean isCompatible(int requested) {
1161        // allow next reader in even in chained mode, if it was read access before
1162
if (requested == S && grantedMode == CHAIN && origMode == S) return true;
1163        return cmatrix[requested][grantedMode];
1164    }
1165
1166    private ThreadInfo getThreadInfo(Thread JavaDoc t) {
1167        return registeredThreads.get(t);
1168    }
1169
1170    private boolean canUpgrade(int threadGranted, int requested) {
1171        return (threadGranted == S) && (requested == X) && (readersNo == 1);
1172    }
1173
1174    // ------------------------------- EVENT METHODS ----------------------------
1175

1176    /** Runs the runnable in event queue, either immediatelly,
1177    * or it posts it into the queue.
1178    */

1179    private static void doEvent(Runnable JavaDoc run) {
1180        if (EventQueue.isDispatchThread()) {
1181            run.run();
1182        } else {
1183            EventQueue.invokeLater(run);
1184        }
1185    }
1186
1187    /** Methods for access to event queue.
1188    * @param run runabble to post later
1189    */

1190    private static void doEventRequest(Runnable JavaDoc run) {
1191        EventQueue.invokeLater(run);
1192    }
1193
1194    /** Methods for access to event queue and waiting for result.
1195    * @param run runnable to post later
1196    */

1197    private static <T> T doEventAccess(final ExceptionAction<T> run)
1198    throws MutexException {
1199        if (isDispatchThread()) {
1200            try {
1201                return run.run();
1202            } catch (RuntimeException JavaDoc e) {
1203                throw e;
1204            } catch (Exception JavaDoc e) {
1205                throw new MutexException(e);
1206            }
1207        }
1208
1209        final Throwable JavaDoc[] arr = new Throwable JavaDoc[1];
1210
1211        try {
1212            final List JavaDoc<T> res = new ArrayList JavaDoc<T>(1);
1213            class AWTWorker implements Runnable JavaDoc {
1214                public void run() {
1215                    try {
1216                        res.add(run.run());
1217                    } catch (Exception JavaDoc e) {
1218                        arr[0] = e;
1219                    } catch (LinkageError JavaDoc e) {
1220                        // #20467
1221
arr[0] = e;
1222                    } catch (StackOverflowError JavaDoc e) {
1223                        // #20467
1224
arr[0] = e;
1225                    }
1226                }
1227            }
1228            
1229            AWTWorker w = new AWTWorker();
1230            EventQueue.invokeAndWait(w);
1231
1232            if (arr[0] == null) {
1233                return res.get(0);
1234            }
1235        } catch (InterruptedException JavaDoc e) {
1236            arr[0] = e;
1237        } catch (InvocationTargetException JavaDoc e) {
1238            arr[0] = e;
1239        }
1240
1241        if (arr[0] instanceof RuntimeException JavaDoc) {
1242            throw (RuntimeException JavaDoc) arr[0];
1243        }
1244
1245        throw notifyException(arr[0]);
1246    }
1247
1248    /** @return true iff current thread is EventDispatchThread */
1249    static boolean isDispatchThread() {
1250        boolean dispatch = EventQueue.isDispatchThread();
1251
1252        if (!dispatch && (Utilities.getOperatingSystem() == Utilities.OS_SOLARIS)) {
1253            // on solaris the event queue is not always recognized correctly
1254
// => try to guess by name
1255
dispatch = (Thread.currentThread().getClass().getName().indexOf("EventDispatchThread") >= 0); // NOI18N
1256
}
1257
1258        return dispatch;
1259    }
1260
1261    /** Notify exception and returns new MutexException */
1262    private static final MutexException notifyException(Throwable JavaDoc t) {
1263        if (t instanceof InvocationTargetException JavaDoc) {
1264            t = unfoldInvocationTargetException((InvocationTargetException JavaDoc) t);
1265        }
1266
1267        if (t instanceof Error JavaDoc) {
1268            annotateEventStack(t);
1269            throw (Error JavaDoc) t;
1270        }
1271
1272        if (t instanceof RuntimeException JavaDoc) {
1273            annotateEventStack(t);
1274            throw (RuntimeException JavaDoc) t;
1275        }
1276
1277        MutexException exc = new MutexException((Exception JavaDoc) t);
1278        exc.initCause(t);
1279
1280        return exc;
1281    }
1282
1283    private static final void annotateEventStack(Throwable JavaDoc t) {
1284        //ErrorManager.getDefault().annotate(t, new Exception("Caught here in mutex")); // NOI18N
1285
}
1286
1287    private static final Throwable JavaDoc unfoldInvocationTargetException(InvocationTargetException JavaDoc e) {
1288        Throwable JavaDoc ret;
1289
1290        do {
1291            ret = e.getTargetException();
1292
1293            if (ret instanceof InvocationTargetException JavaDoc) {
1294                e = (InvocationTargetException JavaDoc) ret;
1295            } else {
1296                e = null;
1297            }
1298        } while (e != null);
1299
1300        return ret;
1301    }
1302
1303    // --------------------------------------------- END OF EVENT METHODS ------------------------------
1304

1305    /** Action to be executed in a mutex without throwing any checked exceptions.
1306    * Unchecked exceptions will be propagated to calling code.
1307     * @param T the type of object to return
1308    */

1309    public interface Action<T> extends ExceptionAction<T> {
1310        /** Execute the action.
1311        * @return any object, then returned from {@link Mutex#readAccess(Mutex.Action)} or {@link Mutex#writeAccess(Mutex.Action)}
1312        */

1313        T run();
1314    }
1315
1316    /** Action to be executed in a mutex, possibly throwing checked exceptions.
1317    * May throw a checked exception, in which case calling
1318    * code should catch the encapsulating exception and rethrow the
1319    * real one.
1320    * Unchecked exceptions will be propagated to calling code without encapsulation.
1321     * @param T the type of object to return
1322    */

1323    public interface ExceptionAction<T> {
1324        /** Execute the action.
1325        * Can throw an exception.
1326        * @return any object, then returned from {@link Mutex#readAccess(Mutex.ExceptionAction)} or {@link Mutex#writeAccess(Mutex.ExceptionAction)}
1327        * @exception Exception any exception the body needs to throw
1328        */

1329        T run() throws Exception JavaDoc;
1330    }
1331
1332    private static final class ThreadInfo {
1333        /** t is forcibly sent from waiters to enter() by wakeUpOthers() */
1334        boolean forced;
1335
1336        /** ThreadInfo for this Thread */
1337        final Thread JavaDoc t;
1338
1339        /** granted mode */
1340        int mode;
1341
1342        // 0 - NONE, 1 - CHAIN, 2 - X, 3 - S
1343

1344        /** enter counter */
1345        int[] counts;
1346
1347        /** queue of runnable rquests that are to be executed (in X mode) right after S mode is left
1348        * deadlock avoidance technique
1349        */

1350        List JavaDoc<Runnable JavaDoc>[] queues;
1351
1352        /** value of counts[S] when the mode was upgraded
1353        * rsnapshot works as follows:
1354        * if a thread holds the mutex in the S mode and it reenters the mutex
1355        * and requests X and the mode can be granted (no other readers) then this
1356        * variable is set to counts[S]. This is used in the leave method in the X branch.
1357        * (X mode is granted by other words)
1358        * If rsnapshot is less than counts[S] then the counter is decremented etc. If the rsnapshot is
1359        * equal to count[S] then count[X] is decremented. If the X counter is zeroed then
1360        * rsnapshot is zeroed as well and current mode is downgraded to S mode.
1361        * rsnapshot gets less than counts[S] if current mode is X and the mutex is reentered
1362        * with S request.
1363        */

1364        int rsnapshot;
1365
1366        @SuppressWarnings JavaDoc("unchecked")
1367        public ThreadInfo(Thread JavaDoc t, int mode) {
1368            this.t = t;
1369            this.mode = mode;
1370            this.counts = new int[MODE_COUNT];
1371            this.queues = (List JavaDoc<Runnable JavaDoc>[])new List JavaDoc[MODE_COUNT];
1372            counts[mode] = 1;
1373        }
1374
1375        public String JavaDoc toString() {
1376            return super.toString() + " thread: " + t + " mode: " + mode + " X: " + counts[2] + " S: " + counts[3]; // NOI18N
1377
}
1378
1379        /** Adds the Runnable into the queue of waiting requests */
1380        public void enqueue(int mode, Runnable JavaDoc run) {
1381            if (queues[mode] == null) {
1382                queues[mode] = new ArrayList JavaDoc<Runnable JavaDoc>(13);
1383            }
1384
1385            queues[mode].add(run);
1386        }
1387
1388        /** @return a List of enqueued Runnables - may be null */
1389        public List JavaDoc dequeue(int mode) {
1390            List JavaDoc ret = queues[mode];
1391            queues[mode] = null;
1392
1393            return ret;
1394        }
1395
1396        public int getRunnableCount(int mode) {
1397            return ((queues[mode] == null) ? 0 : queues[mode].size());
1398        }
1399    }
1400
1401    /** This class is defined only for better understanding of thread dumps where are informations like
1402    * java.lang.Object@xxxxxxxx owner thread_x
1403    * wait for enter thread_y
1404    */

1405    private static final class InternalLock {
1406        InternalLock() {
1407        }
1408    }
1409
1410    private static final class QueueCell {
1411        int mode;
1412        Thread JavaDoc t;
1413        boolean signal;
1414        boolean left;
1415
1416        /** priority of the cell */
1417        int priority2;
1418
1419        public QueueCell(int mode, Thread JavaDoc t) {
1420            this.mode = mode;
1421            this.t = t;
1422            this.left = false;
1423            this.priority2 = 0;
1424        }
1425
1426        public String JavaDoc toString() {
1427            return super.toString() + " mode: " + mode + " thread: " + t; // NOI18N
1428
}
1429
1430        /** @return priority of this cell */
1431        public long getPriority() {
1432            return ((priority2 == 0) ? t.getPriority() : priority2);
1433        }
1434
1435        /** @return true iff the thread left sleep */
1436        public boolean isGotOut() {
1437            return left;
1438        }
1439
1440        /** current thread will sleep until wakeMeUp is called
1441        * if wakeMeUp was already called then the thread will not sleep
1442        */

1443        public synchronized void sleep() {
1444            try {
1445                while (!signal) {
1446                    try {
1447                        wait();
1448
1449                        return;
1450                    } catch (InterruptedException JavaDoc e) {
1451                        Logger.getLogger(Mutex.class.getName()).log(Level.WARNING, null, e);
1452                    }
1453                }
1454            } finally {
1455                left = true;
1456            }
1457        }
1458
1459        /** sends signal to a sleeper - to a thread that is in the sleep() */
1460        public void wakeMeUp() {
1461            signal = true;
1462            notifyAll();
1463        }
1464    }
1465
1466    /** Provides access to Mutex's internal methods.
1467     *
1468     * This class can be used when one wants to avoid creating a
1469     * bunch of Runnables. Instead,
1470     * <pre>
1471     * try {
1472     * enterXAccess ();
1473     * yourCustomMethod ();
1474     * } finally {
1475     * exitXAccess ();
1476     * }
1477     * </pre>
1478     * can be used.
1479     *
1480     * You must, however, control the related Mutex, i.e. you must be creator of
1481     * the Mutex.
1482     *
1483     * @since 1.17
1484     */

1485    public static final class Privileged {
1486        private Mutex parent;
1487
1488        final void setParent(Mutex parent) {
1489            this.parent = parent;
1490        }
1491
1492        public void enterReadAccess() {
1493            parent.readEnter(Thread.currentThread());
1494        }
1495
1496        public void enterWriteAccess() {
1497            parent.writeEnter(Thread.currentThread());
1498        }
1499
1500        public void exitReadAccess() {
1501            parent.leave(Thread.currentThread());
1502        }
1503
1504        public void exitWriteAccess() {
1505            parent.leave(Thread.currentThread());
1506        }
1507    }
1508
1509    private void setGrantedMode(int mode) {
1510        if (grantedMode != CHAIN && mode == CHAIN) {
1511            origMode = grantedMode;
1512        }
1513        grantedMode = mode;
1514    }
1515}
1516
Popular Tags