This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 32439
Collapse All | Expand All

(-)openide/src/org/openide/util/Mutex.java (-861 / +492 lines)
Lines 24-39 Link Here
24
* can be read by several readers at once but only written by one writer.
24
* can be read by several readers at once but only written by one writer.
25
* <P>
25
* <P>
26
* It is guaranteed that if you are a writer you can also enter the
26
* It is guaranteed that if you are a writer you can also enter the
27
* mutex as a reader. Conversely, if you are the <em>only</em> reader you
27
* mutex as a reader. But you cannot enter the write mutex if you hold
28
* are allowed to enter the mutex as a writer.
28
* the read mutex, since that can cause deadlocks.
29
* <P>
29
* <P>
30
* If the mutex is used only by one thread, the thread can repeatedly
30
 * This implementation will not starve a writer or reader indefinitely.
31
* enter it as a writer or reader. So one thread can never deadlock itself,
32
* whichever order operations are performed in.
33
* <P>
34
* There is no strategy to prevent starvation.
35
* Even if there is a writer waiting to enter, another reader might enter
36
* the section instead.
37
* <P>
31
* <P>
38
* Examples of use:
32
* Examples of use:
39
*
33
*
Lines 61-112 Link Here
61
* }
55
* }
62
* </PRE></code>
56
* </PRE></code>
63
*
57
*
64
* @author Ales Novak
58
* @author Ales Novak, Jesse Glick
65
*/
59
*/
66
public final class Mutex extends Object {
60
public final class Mutex extends Object {
61
    
67
    /** Mutex that allows code to be synchronized with the AWT event dispatch thread. */
62
    /** Mutex that allows code to be synchronized with the AWT event dispatch thread. */
68
    public static final Mutex EVENT = new Mutex ();
63
    public static final Mutex EVENT = new Mutex ();
69
64
    
70
    // lock mode constants
65
    private static final RequestProcessor LATER = new RequestProcessor("Mutex"); // NOI18N
71
    /** Lock free */
72
    private static final int NONE = 0x0;
73
    /** Enqueue all requests */
74
    private static final int CHAIN = 0x1;
75
    /** eXclusive */
76
    private static final int X = 0x2;
77
    /** Shared */
78
    private static final int S = 0x3;
79
80
    /** number of modes */
81
    private static final int MODE_COUNT = 0x4;
82
83
    /** compatibility matrix */ // [requested][granted]
84
    private static final boolean[][] cmatrix = {null, null, // NONE, CHAIN
85
                         /* NONE */ /* CHAIN */ /* X */  /* S */  // granted
86
            /*r X */     {true,     false,      false,   false},
87
            /*e S */     {true,     false,      false,   true}
88
            /*q */
89
            //uested
90
                                               };
91
92
    /** Decides whether two locks are compatible.?
93
     * @param granted?
94
     * @param requested?
95
     * @return <tt>true</tt> iff they are compatible?
96
     */
97
    private static boolean compatibleLocks(int granted, int requested) {
98
        return cmatrix[requested][granted];
99
    }
100
    /** granted mode */
101
    private int grantedMode = NONE;
102
    /** protects internal data structures */
103
    private /*final*/ Object LOCK;
104
    /** threads that - owns or waits for this mutex */
105
    private /*final*/ Map registeredThreads;
106
    /** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") */ // NOI18N
107
    private int readersNo = 0;
108
    /** a queue of waiting threads for this mutex */
109
    private List waiters;
110
66
111
    /** Enhanced constructor that permits specifying an object to use as a lock.
67
    /** Enhanced constructor that permits specifying an object to use as a lock.
112
    * The lock is used on entry and exit to {@link #readAccess} and during the
68
    * The lock is used on entry and exit to {@link #readAccess} and during the
Lines 117-173 Link Here
117
    * @param lock lock to use
73
    * @param lock lock to use
118
    */
74
    */
119
    public Mutex (Object lock) {
75
    public Mutex (Object lock) {
120
        init(lock);
76
        this.lock = lock;
121
    }
77
    }
122
78
123
    /** Default constructor.
79
    /** Default constructor.
124
    */
80
    */
125
    public Mutex() {
81
    public Mutex() {
126
        init(new InternalLock());
82
        this(new InternalLock());
127
    }
83
    }
128
    
84
    
129
    /** @param privileged can enter privileged states of this Mutex 
85
    /** @param privileged can enter privileged states of this Mutex 
130
     * This helps avoid creating of custom Runnables.
86
     * This helps avoid creating of custom Runnables.
131
     */
87
     */
132
    public Mutex(Privileged privileged) {
88
    public Mutex(Privileged privileged) {
89
        this();
133
        if (privileged == null) {
90
        if (privileged == null) {
134
            throw new IllegalArgumentException("privileged == null"); //NOI18N
91
            throw new IllegalArgumentException("privileged == null"); //NOI18N
135
        } else {
92
        } else {
136
            init(new InternalLock());
137
            privileged.setParent(this);
93
            privileged.setParent(this);
138
        }
94
        }
139
    }
95
    }
140
    
96
    
141
    /** Initiates this Mutex */
142
    private void init(Object lock) {
143
        this.LOCK = lock;
144
        this.registeredThreads = new HashMap(7);
145
        this.waiters = new LinkedList();
146
    }
147
148
    /** Run an action only with read access.
97
    /** Run an action only with read access.
149
    * See class description re. entering for write access within the dynamic scope.
98
    * See class description re. entering for write access within the dynamic scope.
150
    * @param action the action to perform
99
    * @param action the action to perform
151
    * @return the object returned from {@link Mutex.Action#run}
100
    * @return the object returned from {@link Mutex.Action#run}
152
    */
101
    */
153
    public Object readAccess (Action action) {
102
    public Object readAccess (Action action) {
154
155
        if (this == EVENT) {
103
        if (this == EVENT) {
156
            try {
104
            return eventAccess(action);
157
                return doEventAccess (action);
158
            } catch (MutexException e) {
159
		InternalError err = new InternalError("Exception from non-Exception Action"); // NOI18N
160
		ErrorManager.getDefault().annotate(err, e.getException());
161
		throw err;
162
            }
163
        }
105
        }
164
106
        enterReadAccess();
165
        Thread t = Thread.currentThread();
166
        readEnter(t);
167
        try {
107
        try {
168
            return action.run();
108
            return action.run();
169
        } finally {
109
        } finally {
170
            leave(t);
110
            exitReadAccess();
171
        }
111
        }
172
    }
112
    }
173
113
Lines 196-244 Link Here
196
    * @see #readAccess(Mutex.Action)
136
    * @see #readAccess(Mutex.Action)
197
    */
137
    */
198
    public Object readAccess (ExceptionAction action) throws MutexException {
138
    public Object readAccess (ExceptionAction action) throws MutexException {
199
200
        if (this == EVENT) {
139
        if (this == EVENT) {
201
            return doEventAccess (action);
140
            return eventAccess(action);
202
        }
141
        }
203
142
        enterReadAccess();
204
        Thread t = Thread.currentThread();
205
        readEnter(t);
206
        try {
143
        try {
207
            return action.run();
144
            return action.run();
208
        } catch (RuntimeException e) {
209
            throw e;
210
        } catch (Exception e) {
145
        } catch (Exception e) {
211
            throw new MutexException(e);
146
            throw new MutexException(e);
212
        } catch (LinkageError e) {
213
            // #20467
214
            throw new MutexException(new InvocationTargetException(e));
215
        } catch (StackOverflowError e) {
216
            // #20467
217
            throw new MutexException(new InvocationTargetException(e));
218
        } finally {
147
        } finally {
219
            leave(t);
148
            exitReadAccess();
220
        }
149
        }
221
    }
150
    }
222
151
223
    /** Run an action with read access, returning no result.
152
    /** Run an action with read access, returning no result.
224
    * It may be run asynchronously.
153
    * It may be run asynchronously (or not) at the discretion of the implementation.
225
    *
154
    * This implementation tries to run if synchronously if it will not have to wait
155
    * for a lock, else runs it at some later point in the future.
226
    * @param action the action to perform
156
    * @param action the action to perform
227
    * @see #readAccess(Mutex.Action)
157
    * @see #readAccess(Mutex.Action)
228
    */
158
    */
229
    public void readAccess (final Runnable action) {
159
    public void readAccess (final Runnable action) {
230
231
        if (this == EVENT) {
160
        if (this == EVENT) {
232
            doEvent (action);
161
            eventAccess(action, true);
233
            return;
162
            return;
234
        }
163
        }
235
164
        boolean synch;
236
        Thread t = Thread.currentThread();
165
        synchronized (lock) {
237
        readEnter(t);
166
            synch = semaphore >= 0;
238
        try {
167
            if (synch) {
239
            action.run();
168
                enterReadAccess();
240
        } finally {
169
            }
241
            leave(t);
170
        }
171
        if (synch) {
172
            try {
173
                action.run();
174
            } finally {
175
                exitReadAccess();
176
            }
177
        } else {
178
            LATER.post(new Runnable() {
179
                public void run() {
180
                    enterReadAccess();
181
                    try {
182
                        action.run();
183
                    } finally {
184
                        exitReadAccess();
185
                    }
186
                }
187
            });
242
        }
188
        }
243
    }
189
    }
244
190
Lines 250-272 Link Here
250
    * @return the result of {@link Mutex.Action#run}
196
    * @return the result of {@link Mutex.Action#run}
251
    */
197
    */
252
    public Object writeAccess (Action action) {
198
    public Object writeAccess (Action action) {
253
254
        if (this == EVENT) {
199
        if (this == EVENT) {
255
            try {
200
            return eventAccess(action);
256
                return doEventAccess (action);
257
            } catch (MutexException e) {
258
		InternalError err = new InternalError("Exception from non-Exception Action"); // NOI18N
259
		ErrorManager.getDefault().annotate(err, e.getException());
260
		throw err;
261
            }
262
        }
201
        }
263
202
        enterWriteAccess();
264
        Thread t = Thread.currentThread();
265
        writeEnter(t);
266
        try {
203
        try {
267
            return action.run();
204
            return action.run();
268
        } finally {
205
        } finally {
269
            leave(t);
206
            exitWriteAccess();
270
        }
207
        }
271
    }
208
    }
272
209
Lines 292-341 Link Here
292
    * @see #readAccess(Mutex.ExceptionAction)
229
    * @see #readAccess(Mutex.ExceptionAction)
293
    */
230
    */
294
    public Object writeAccess (ExceptionAction action) throws MutexException {
231
    public Object writeAccess (ExceptionAction action) throws MutexException {
295
296
        if (this == EVENT) {
232
        if (this == EVENT) {
297
            return doEventAccess (action);
233
            return eventAccess(action);
298
        }
234
        }
299
235
        enterWriteAccess();
300
        Thread t = Thread.currentThread();
301
        writeEnter(t);
302
        try {
236
        try {
303
            return action.run();
237
            return action.run();
304
        } catch (RuntimeException e) {
305
            throw e;
306
        } catch (Exception e) {
238
        } catch (Exception e) {
307
            throw new MutexException(e);
239
            throw new MutexException(e);
308
        } catch (LinkageError e) {
309
            // #20467
310
            throw new MutexException(new InvocationTargetException(e));
311
        } catch (StackOverflowError e) {
312
            // #20467
313
            throw new MutexException(new InvocationTargetException(e));
314
        } finally {
240
        } finally {
315
            leave(t);
241
            exitWriteAccess();
316
        }
242
        }
317
    }
243
    }
318
244
319
    /** Run an action with write access and return no result.
245
    /** Run an action with write access and return no result.
320
    * It may be run asynchronously.
246
    * It may be run asynchronously (or not) at the discretion of the implementation.
321
    *
247
    * This implementation tries to run if synchronously if it will not have to wait
248
    * for a lock, else runs it at some later point in the future.
322
    * @param action the action to perform
249
    * @param action the action to perform
323
    * @see #writeAccess(Mutex.Action)
250
    * @see #writeAccess(Mutex.Action)
324
    * @see #readAccess(Runnable)
251
    * @see #readAccess(Runnable)
325
    */
252
    */
326
    public void writeAccess (final Runnable action) {
253
    public void writeAccess (final Runnable action) {
327
328
        if (this == EVENT) {
254
        if (this == EVENT) {
329
            doEvent (action);
255
            eventAccess(action, true);
330
            return;
256
            return;
331
        }
257
        }
332
258
        boolean synch;
333
        Thread t = Thread.currentThread();
259
        synchronized (lock) {
334
        writeEnter(t);
260
            synch = semaphore == 0 || canWrite();
335
        try {
261
            if (synch) {
336
            action.run();
262
                enterWriteAccess();
337
        } finally {
263
            }
338
            leave(t);
264
        }
265
        if (synch) {
266
            try {
267
                action.run();
268
            } finally {
269
                exitWriteAccess();
270
            }
271
        } else {
272
            LATER.post(new Runnable() {
273
                public void run() {
274
                    enterWriteAccess();
275
                    try {
276
                        action.run();
277
                    } finally {
278
                        exitWriteAccess();
279
                    }
280
                }
281
            });
339
        }
282
        }
340
    }
283
    }
341
284
Lines 355-361 Link Here
355
     * @param run runnable to run
298
     * @param run runnable to run
356
     */
299
     */
357
    public void postReadRequest (final Runnable run) {
300
    public void postReadRequest (final Runnable run) {
358
        postRequest(S, run);
301
        if (this == EVENT) {
302
            eventAccess(run, false);
303
            return;
304
        }
305
        if (canWrite()) {
306
            synchronized (lock) {
307
                ThreadInfo ti = currentThreadInfo();
308
                if (ti == null) throw new IllegalStateException();
309
                if (!ti.writer) throw new IllegalStateException();
310
                if (ti.lateReadActions == null) {
311
                    ti.lateReadActions = new LinkedList();
312
                }
313
                ti.lateReadActions.add(run);
314
            }
315
        } else {
316
            enterReadAccess();
317
            try {
318
                run.run();
319
            } finally {
320
                exitReadAccess();
321
            }
322
        }
359
    }
323
    }
360
324
361
    /** Posts a write request. This request runs immediately iff
325
    /** Posts a write request. This request runs immediately iff
Lines 372-984 Link Here
372
     * @param run runnable to run
336
     * @param run runnable to run
373
     */
337
     */
374
    public void postWriteRequest (Runnable run) {
338
    public void postWriteRequest (Runnable run) {
375
        postRequest(X, run);
376
    }
377
378
    /** toString */
379
    public String toString() {
380
        if (this == EVENT) {
339
        if (this == EVENT) {
381
            return "Mutex.EVENT"; // NOI18N
340
            eventAccess(run, false);
382
        }
341
            return;
383
	
384
        String newline = System.getProperty("line.separator");
385
        StringBuffer sbuff = new StringBuffer(512);
386
        synchronized(LOCK){
387
            sbuff.append("threads: ").append(registeredThreads).append(newline); // NOI18N
388
            sbuff.append("readersNo: ").append(readersNo).append(newline); // NOI18N
389
            sbuff.append("waiters: ").append(waiters).append(newline); // NOI18N
390
            sbuff.append("grantedMode: ").append(grantedMode).append(newline); // NOI18N
391
        }
342
        }
392
        return sbuff.toString();
343
        if (canWrite()) {
393
    }
344
            boolean synch;
394
345
            synchronized (lock) {
395
    // priv methods  -----------------------------------------
346
                ThreadInfo ti = currentThreadInfo();
396
347
                if (ti == null) throw new IllegalStateException();
397
    /** enters this mutex for writing */
348
                if (!ti.writer) throw new IllegalStateException();
398
    private void writeEnter(Thread t) {
349
                synch = ti.extraReads == 0;
399
        enter(X, t, true);
350
                if (!synch) {
400
    }
351
                    if (ti.lateWriteActions == null) {
401
    /** enters this mutex for reading */
352
                        ti.lateWriteActions = new LinkedList();
402
    private void readEnter(Thread t) {
403
        enter(S, t, true);
404
    }
405
406
    /** enters this mutex with given mode
407
    * @param requested one of S, X
408
    * @param t
409
    */
410
    private boolean enter(int requested, Thread t, boolean block) {
411
412
        QueueCell cell = null;
413
        int loopc = 0;
414
415
        for (;;) {
416
            loopc++;
417
            synchronized (LOCK) {
418
                // does the thread reenter this mutex?
419
                ThreadInfo info = getThreadInfo(t);
420
421
                if (info != null) {
422
                    if (grantedMode == NONE) {
423
                        // defensive
424
                        throw new IllegalStateException();
425
                    }
353
                    }
426
                    // reenters
354
                    ti.lateWriteActions.add(run);
427
                    // requested == S -> always succeeds
428
                    // info.mode == X -> always succeeds
429
                    if (((info.mode == S) && (grantedMode == X)) ||
430
                            ((info.mode == X) && (grantedMode == S))) {
431
                        // defensive
432
                        throw new IllegalStateException();
433
                    }
434
                    if ((info.mode == X) ||
435
                            (info.mode == requested)) {  // X - X, X - S, S - S
436
                        if (info.forced) {
437
                            info.forced = false;
438
                        } else {
439
                            info.counts[requested]++;
440
                            if ((requested == S) && (info.counts[requested] == 1)) {
441
                                readersNo++;
442
                                info.stamp = readersNo;
443
                            }
444
                        }
445
                        return true;
446
                    } else if (canUpgrade(info.mode, requested)) { // S - X and no holders
447
                        info.mode = X;
448
                        info.counts[requested]++;
449
                        info.rsnapshot = info.counts[S];
450
                        if (grantedMode == S) {
451
                            grantedMode = X;
452
                        } else if (grantedMode == X) {
453
                            // defensive
454
                            throw new IllegalStateException();
455
                        } // else if grantedMode == CHAIN - let it be
456
457
                        return true;
458
                    } else { // S - X and holders
459
                        if (Boolean.getBoolean("netbeans.debug.threads")) { // NOI18N
460
                            System.err.println("WARNING: Going from readAccess to writeAccess");
461
                            Thread.dumpStack();
462
                        }
463
                        // chain follows
464
                    }
465
                } else {  // first acquisition
466
                    if (isCompatible(requested)) {  // NONE -> S,X or S -> S
467
                        grantedMode = requested;
468
                        registeredThreads.put(t, info = new ThreadInfo(t, requested));
469
                        if (requested == S) {
470
                            readersNo++;
471
                            info.stamp = readersNo;
472
                        }
473
                        return true;
474
                    } // else {
475
                    // granted is S and requested is X
476
                    // granted is X and requested is S or X
477
                    //}
478
                }
479
480
                if (! block) {
481
                    return false;
482
                }
355
                }
483
                grantedMode = CHAIN;
484
                cell = chain(requested, t, 0);
485
            } // sync
486
            cell.sleep();
487
        } // for
488
    }
489
490
    /** privilegedEnter serves for processing posted requests */
491
    private boolean reenter(Thread t, int mode, int stamp) {
492
        
493
        // from leaveX -> grantedMode is NONE or S
494
        if (mode == S) {
495
            if (grantedMode != NONE && grantedMode != S) {
496
                throw new IllegalStateException(this.toString());
497
            }
356
            }
498
            enter(mode, t, true);
357
            if (synch) {
499
            return false;
358
                run.run();
500
        }
501
502
        // assert (mode == X)
503
        
504
        ThreadInfo tinfo = getThreadInfo(t);
505
        boolean chainFromLeaveX = (grantedMode == CHAIN && tinfo != null && tinfo.counts[X] > 0);
506
        
507
        // process grantedMode == X or CHAIN from leaveX OR grantedMode == NONE from leaveS
508
        if (grantedMode == X || grantedMode == NONE || chainFromLeaveX) {
509
            enter(mode, t, true);
510
            return false;
511
        } else { // remains grantedMode == CHAIN or S from leaveS, so it will be CHAIN
512
            if (readersNo == 0) {
513
                throw new IllegalStateException(this.toString());
514
            }
515
            ThreadInfo info = new ThreadInfo(t, mode);
516
            registeredThreads.put(t, info);
517
            // prevent from grantedMode == NONE (another thread - leaveS)
518
            readersNo += 2;
519
            info.stamp = stamp;
520
            // prevent from new readers
521
            grantedMode = CHAIN;
522
            return true;
523
        } // else X means ERROR!!!
524
    }
525
526
    /** @param t holds S (one entry) and wants X, grantedMode != NONE && grantedMode != X */
527
    private void privilegedEnter(Thread t, int mode) {
528
        boolean decrease = true;
529
        ThreadInfo info;
530
531
        synchronized (LOCK) {
532
            info = getThreadInfo(t);
533
        }
534
535
        for (;;) {
536
            QueueCell cell;
537
            synchronized (LOCK) {
538
539
                if (decrease) {
540
                    decrease = false;
541
                    readersNo -= 2;
542
                }
543
544
                // always chain this thread
545
                // since there can be another one
546
                // in the queue with higher priority
547
                grantedMode = CHAIN;
548
                cell = chain(mode, t, Integer.MAX_VALUE - info.stamp);
549
550
                if (readersNo == 0) { // seems I may enter
551
                    // no one has higher prio?
552
                    if (waiters.get(0) == cell) {
553
                        waiters.remove(0);
554
                        return;
555
                    } else {
556
                        grantedMode = NONE;
557
                        wakeUpOthers();
558
                    }
559
                }  
560
            } // synchronized (LOCK)
561
            cell.sleep();
562
            // cell already removed from waiters here
563
        }
564
    }
565
566
    /** Leaves this mutex */
567
    private void leave(Thread t) {
568
        ThreadInfo info;
569
        int postedMode = NONE;
570
        boolean needLock = false;
571
572
        synchronized (LOCK) {
573
            info = getThreadInfo(t);
574
575
            switch (grantedMode) {
576
            case NONE:
577
                throw new IllegalStateException();
578
579
            case CHAIN:
580
                if (info.counts[X] > 0) {
581
                    // it matters that X is handled first - see ThreadInfo.rsnapshot
582
                    postedMode = leaveX(info);
583
                } else if (info.counts[S] > 0) {
584
                    postedMode = leaveS(info);
585
                } else {
586
                    throw new IllegalStateException();
587
                }
588
                break;
589
590
            case X:
591
                postedMode = leaveX(info);
592
                break;
593
594
            case S:
595
                postedMode = leaveS(info);
596
                break;
597
            } // switch
598
599
            // do not give up LOCK until queued runnables are run
600
            if (postedMode != NONE) {
601
                int runsize = info.getRunnableCount(postedMode);
602
                if (runsize != 0) {
603
                    needLock = reenter(t, postedMode, info.stamp); // grab lock
604
                }
605
            }
359
            }
606
        } // sync
360
        } else if (canRead()) {
607
361
            throw new IllegalStateException("Cannot call postWriteRequest while holding a read mutex; consider using writeAccess(Runnable)"); // NOI18N
608
        // check posted requests
362
        } else {
609
        if (postedMode != NONE && info.getRunnableCount(postedMode) > 0) { 
363
            enterWriteAccess();
610
            try {
364
            try {
611
                if (needLock) { // go from S to X or CHAIN
365
                run.run();
612
                    privilegedEnter(t, postedMode);
613
                }             
614
                // holds postedMode lock here
615
                List runnables = info.dequeue(postedMode);
616
                final int size = runnables.size();
617
                for (int i = 0; i < size; i++) {
618
                    try {
619
                        Runnable r = (Runnable) runnables.get(i);
620
                        r.run();
621
                    } catch (Exception e) {
622
                        ErrorManager.getDefault().notify(e);
623
                    } catch (LinkageError e) {
624
                        // #20467
625
                        ErrorManager.getDefault().notify(e);
626
                    } catch (StackOverflowError e) {
627
                        // #20467
628
                        ErrorManager.getDefault().notify(e);
629
                    } // try
630
                } // for
631
                // help gc
632
                runnables = null;
633
            } finally {
366
            } finally {
634
                leave(t);  // release lock grabbed - shared
367
                exitWriteAccess();
635
            }
368
            }
636
        } // mode
637
    }
638
639
    /** Leaves the lock supposing that info.counts[X] is greater than zero */
640
    private int leaveX(ThreadInfo info) {
641
642
        if ((info.counts[X] <= 0) ||
643
                (info.rsnapshot > info.counts[S])) {
644
            // defensive
645
            throw new IllegalStateException();
646
        }
369
        }
370
    }
647
371
648
        if (info.rsnapshot == info.counts[S]) {
372
    /**
649
            info.counts[X]--;
373
     * Enter the read mutex.
650
            if (info.counts[X] == 0) {
374
     * @see Mutex.Privileged#enterReadAccess
651
                info.rsnapshot = 0;
375
     */
652
                // downgrade the lock
376
    void enterReadAccess() {
653
                if (info.counts[S] > 0) {
377
        synchronized (lock) {
654
                    info.mode = grantedMode = S;
378
            ThreadInfo ti = currentThreadInfo();
655
                } else {
379
            if (ti != null) {
656
                    info.mode = grantedMode = NONE;
380
                // Already in read or write mutex, can certainly enter read.
657
                    registeredThreads.remove(info.t);
381
                ti.extraReads++;
658
                }
382
            } else {
659
                
383
                // ti == null, entering fresh.
660
                if (info.getRunnableCount(S) > 0) {
384
                // Wait for any writers to exit.
661
                    return S;
385
                while (semaphore < 0) {
662
                }
386
                    try {
663
                
387
                        lock.wait();
664
                // mode has changed
388
                    } catch (InterruptedException e) {
665
                wakeUpOthers();
389
                        throw new IllegalStateException(e.toString());
666
            }
390
                    }
667
        } else {
668
            // rsnapshot < counts[S]
669
670
            if (info.counts[S] <= 0) {
671
                // defensive
672
                throw new IllegalStateException();
673
            }
674
675
            if (--info.counts[S] == 0) {
676
                if (readersNo <= 0) {
677
                    throw new IllegalStateException();
678
                }
391
                }
679
                readersNo--;
392
                // Uncontended or shared, go ahead.
680
                return X;
393
                new ThreadInfo(false).register();
394
                semaphore++;
681
            }
395
            }
682
        }
396
        }
683
        
684
        return NONE;
685
    }
397
    }
686
398
687
    /** Leaves the lock supposing that info.counts[S] is greater than zero */
399
    /**
688
    private int leaveS(ThreadInfo info) {
400
     * Exit the read mutex.
689
        if ((info.counts[S] <= 0) ||
401
     * @see Mutex.Privileged#exitReadAccess
690
                (info.counts[X] > 0)) {
402
     */
691
            // defensive
403
    void exitReadAccess() {
692
            throw new IllegalStateException();
404
        List lateWriteActions = null;
693
        }
405
        synchronized (lock) {
694
406
            ThreadInfo ti = currentThreadInfo();
695
        info.counts[S]--;
407
            if (ti == null) throw new IllegalStateException();
696
        if (info.counts[S] == 0) {
408
            if (ti.extraReads > 0) {
697
409
                // Just mark them off.
698
            // remove the thread
410
                ti.extraReads--;
699
            info.mode = NONE;
411
                if (ti.extraReads == 0 && ti.writer && ti.lateWriteActions != null) {
700
            registeredThreads.remove(info.t);
412
                    // We are a writer who has just finished nested reads and now can
701
413
                    // run some late write actions.
702
            // downsize readersNo
414
                    if (semaphore != -1) throw new IllegalStateException();
703
            if (readersNo <= 0) {
415
                    lateWriteActions = ti.lateWriteActions;
704
                throw new IllegalStateException();
416
                    ti.lateWriteActions = null;
705
            }
706
            readersNo--;
707
            
708
            if (readersNo == 0) {
709
                // set grantedMode to NONE
710
                // and then wakeUp others - either immediately 
711
                // or in privelegedEnter()
712
                grantedMode = NONE;
713
                
714
                if (info.getRunnableCount(X) > 0) {
715
                    return X;
716
                }
717
                
718
                wakeUpOthers();
719
            } else if (info.getRunnableCount(X) > 0) {
720
                return X;
721
            } else if ((grantedMode == CHAIN) &&
722
                       (readersNo == 1)) {
723
                // can be the mode advanced from CHAIN? Examine first item of waiters!
724
725
                for (int i = 0; i < waiters.size(); i++) {
726
                    QueueCell qc = (QueueCell) waiters.get(i);
727
                    synchronized (qc) {
728
                        if (qc.isGotOut()) {
729
                            waiters.remove(i--);
730
                            continue;
731
                        }
732
733
                        ThreadInfo tinfo = getThreadInfo(qc.t);
734
735
                        if (tinfo != null) {
736
                            if (tinfo.mode == S) {
737
                                if (qc.mode != X) {
738
                                    // defensive
739
                                    throw new IllegalStateException();
740
                                }
741
742
                                if (waiters.size() == 1) {
743
                                    grantedMode = X;
744
                                } // else let CHAIN
745
                                tinfo.mode = X;
746
                                waiters.remove(i);
747
                                qc.wakeMeUp();
748
                            }
749
                        } // else first request is a first X request of some thread
750
                        break;
751
                    } // sync (qc)
752
                } // for
753
            } // else
754
        } // count[S] == 0
755
        
756
        return NONE;
757
    }
758
759
    /** Adds this thread to the queue of waiting threads
760
    * @warning LOCK must be held
761
    */
762
    private QueueCell chain(final int requested, final Thread t, final int priority) {
763
764
        //long timeout = 0;
765
766
        /*
767
        if (killDeadlocksOn) {
768
            checkDeadlock(requested, t);
769
            timeout = (isDispatchThread() || checkAwtTreeLock() ? TIMEOUT : 0);
770
        }
771
        */
772
773
        QueueCell qc = new QueueCell(requested, t);
774
        //qc.timeout = timeout;
775
        qc.priority2 = priority;
776
777
        final int size = waiters.size();
778
        if (size == 0) {
779
            waiters.add(qc);
780
        } else {
781
            QueueCell cursor;
782
            int i = 0;
783
            do {
784
                cursor = (QueueCell) waiters.get(i);
785
                if (cursor.getPriority() < qc.getPriority()) {
786
                    waiters.add(i, qc);
787
                    break;
788
                }
417
                }
789
                i++;
418
            } else {
790
            } while (i < size);
419
                // Really exiting.
791
            if (i == size) {
420
                if (ti.writer) throw new IllegalStateException();
792
                waiters.add(qc);
421
                if (semaphore <= 0) throw new IllegalStateException();
422
                if (ti.extraWrites > 0) throw new IllegalStateException();
423
                ti.unregister();
424
                semaphore--;
425
                lock.notifyAll();
426
                if (semaphore == 0 && !threads.isEmpty()) throw new IllegalStateException(threads.toString());
427
            }
428
        }
429
        if (lateWriteActions != null) {
430
            // We exited the lock, but since are a writer no one else can enter
431
            // anything, so that is safe.
432
            Iterator it = lateWriteActions.iterator();
433
            while (it.hasNext()) {
434
                Runnable r = (Runnable)it.next();
435
                r.run();
793
            }
436
            }
794
        }
437
        }
795
        return qc;
796
    }
438
    }
797
439
798
    /** Scans through waiters and wakes up them */
440
    /**
799
    private void wakeUpOthers() {
441
     * Enter the write mutex.
800
442
     * @see Mutex.Privileged#enterWriteAccess
801
        if ((grantedMode == X) ||
443
     */
802
                (grantedMode == CHAIN)) {
444
    void enterWriteAccess() {
803
            // defensive
445
        synchronized (lock) {
804
            throw new IllegalStateException();
446
            ThreadInfo ti = currentThreadInfo();
805
        }
447
            if (ti != null) {
806
448
                if (ti.writer) {
807
        if (waiters.size() == 0) {
449
                    // Already in write mutex, can reenter freely.
808
            return;
450
                    ti.extraWrites++;
809
        }
451
                } else {
810
452
                    throw new IllegalStateException("Illegal mutex upgrade from read to write"); // NOI18N
811
        for (int i = 0; i < waiters.size(); i++) {
812
            QueueCell qc = (QueueCell) waiters.get(i);
813
814
            synchronized (qc) {
815
                if (qc.isGotOut()) {
816
                    // bogus waiter
817
                    waiters.remove(i--);
818
                    continue;
819
                }
453
                }
820
454
            } else {
821
                if (compatibleLocks(grantedMode, qc.mode)) {  // woken S -> should I wake X? -> no
455
                // ti == null, entering fresh.
822
                    waiters.remove(i--);
456
                // Wait for any readers or writers to exit.
823
                    qc.wakeMeUp();
457
                while (semaphore != 0) {
824
                    grantedMode = qc.mode;
458
                    try {
825
                    if (getThreadInfo(qc.t) == null) {
459
                        lock.wait();
826
                        // force to have a record since recorded threads
460
                    } catch (InterruptedException e) {
827
                        // do not use isCompatible call
461
                        throw new IllegalStateException(e.toString());
828
                        ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
829
                        ti.forced = true;
830
                        if (qc.mode == S) {
831
                            readersNo++;
832
                        }
833
                        registeredThreads.put(qc.t, ti);
834
                    }
462
                    }
835
                } else {
836
                    grantedMode = CHAIN;
837
                    break;
838
                }
463
                }
839
            } // sync (qc)
464
                // Uncontended, go ahead.
465
                new ThreadInfo(true).register();
466
                semaphore = -1;
467
            }
840
        }
468
        }
841
    }
469
    }
842
470
843
    /** Posts new request for current thread
471
    /**
844
    * @param mutexMode mutex mode for which the action is rquested
472
     * Exit the write mutex.
845
    * @param run the action
473
     * @see Mutex.Privileged#exitWriteAccess
846
    */
474
     */
847
    private void postRequest(int mutexMode, Runnable run) {
475
    void exitWriteAccess() {
848
476
        List lateReadActions = null;
849
        if (this == EVENT) {
477
        ThreadInfo ti;
850
            doEventRequest(run);
478
        synchronized (lock) {
851
            return;
479
            ti = currentThreadInfo();
852
        }
480
            if (ti == null) throw new IllegalStateException();
853
481
            if (!ti.writer) throw new IllegalStateException();
854
        Thread t = Thread.currentThread();
482
            if (semaphore != -1) throw new IllegalStateException();
855
        ThreadInfo info;
483
            if (ti.extraWrites > 0) {
856
484
                // Just mark them off.
857
        synchronized (LOCK) {
485
                ti.extraWrites--;
858
            info = getThreadInfo(t);
486
            } else if (ti.lateReadActions != null) {
859
            if (info != null) {
487
                if (ti.extraReads > 0) throw new IllegalStateException();
860
                // the same mode and mutex is not entered in the other mode
488
                // Will exit after running these.
861
                // assert (mutexMode == S || mutexMode == X)
489
                lateReadActions = ti.lateReadActions;
862
                if (mutexMode == info.mode && info.counts[S + X - mutexMode] == 0) {
490
                ti.lateReadActions = null;
863
                    enter(mutexMode, t, true);
491
            } else {
864
                } else {  // the mutex is held but can not be entered in X mode
492
                // Really exiting.
865
                    info.enqueue(mutexMode, run);
493
                if (ti.extraReads > 0) throw new IllegalStateException();
866
                    return;
494
                ti.unregister();
867
                }
495
                semaphore = 0;
496
                lock.notifyAll();
497
                if (!threads.isEmpty()) throw new IllegalStateException(threads.toString());
498
            }
499
        }
500
        if (lateReadActions != null) {
501
            // No other threads can enter before this because we have not yet
502
            // released the semaphore.
503
            synchronized (lock) {
504
                semaphore = 1;
505
                ti.writer = false;
506
                lock.notifyAll();
507
                // Now the semaphore is released, we are in plain read access.
508
                if (threads.size() != 1) throw new IllegalStateException(threads.toString());
868
            }
509
            }
869
        }
870
871
        // this mutex is not held
872
        if (info == null) {
873
            enter(mutexMode, t, true);
874
            try {
510
            try {
875
                run.run();
511
                Iterator it = lateReadActions.iterator();
512
                while (it.hasNext()) {
513
                    Runnable r = (Runnable)it.next();
514
                    r.run();
515
                }
876
            } finally {
516
            } finally {
877
                leave(t);
517
                exitReadAccess();
878
            }
518
            }
879
            return;
880
        }
881
882
        // run it immediately
883
        // info != null so enter(...) succeeded
884
        try {
885
            run.run();
886
        } finally {
887
            leave(t);
888
        }
519
        }
889
    }
520
    }
890
521
    
891
    /** @param requested is requested mode of locking
522
    /**
892
    * @return <tt>true</tt> if and only if current mode and requested mode are compatible
523
     * Check if the current thread is holding a read or write mutex.
893
    */
524
     * @since XXX
894
    private boolean isCompatible(int requested) {
525
     */
895
        return compatibleLocks(grantedMode, requested);
526
    public boolean canRead() {
896
    }
527
        if (this == EVENT) {
897
528
            return isDispatchThread();
898
    private ThreadInfo getThreadInfo(Thread t) {
529
        }
899
        return (ThreadInfo) registeredThreads.get(t);
530
        synchronized (lock) {
900
    }
531
            if (semaphore == 0) {
901
532
                // Uncontended, obviously not. Only a shortcut,
902
    private boolean canUpgrade(int threadGranted, int requested) {
533
                // next clause would catch it anyway.
903
        return (threadGranted == S) && (requested == X) && (readersNo == 1);
534
                return false;
535
            }
536
            return currentThreadInfo() != null;
537
        }
904
    }
538
    }
905
539
    
906
    // ------------------------------- EVENT METHODS ----------------------------
540
    /**
907
    /** Runs the runnable in event queue, either immediatelly,
541
     * Check if the current thread is holding the write mutex.
908
    * or it posts it into the queue.
542
     * @since XXX
909
    */
543
     */
910
    private static void doEvent (Runnable run) {
544
    public boolean canWrite() {
911
        if (EventQueue.isDispatchThread ()) {
545
        if (this == EVENT) {
912
            run.run ();
546
            return isDispatchThread();
913
        } else {
547
        }
914
            EventQueue.invokeLater (run);
548
        synchronized (lock) {
549
            if (semaphore != -1) {
550
                // Not in write mode, obviously not. Only a shortcut,
551
                // next clause would catch it anyway.
552
                return false;
553
            }
554
            ThreadInfo ti = currentThreadInfo();
555
            if (ti != null) {
556
                if (!ti.writer) throw new IllegalStateException();
557
                return true;
558
            } else {
559
                return false;
560
            }
915
        }
561
        }
916
    }
562
    }
917
563
918
    /** Methods for access to event queue.
564
    /** toString */
919
    * @param run runabble to post later
565
    public String toString() {
920
    */
566
        if (this == EVENT) {
921
    private static void doEventRequest (Runnable run) {
567
            return "Mutex.EVENT"; // NOI18N
922
        EventQueue.invokeLater (run);
568
        }
569
        return "Mutex<semaphore=" + semaphore + ",threads=" + threads + ">"; // NOI18N
923
    }
570
    }
571
    
572
    // --- Internal data structures ---
924
573
925
    /** Methods for access to event queue and waiting for result.
574
    /**
926
    * @param run runabble to post later
575
     * Lock for all internal structures.
927
    */
576
     * They may be accessed only when synched on this.
928
    private static Object doEventAccess (final ExceptionAction run) throws MutexException {
577
     * Also waiting threads are waiting on this lock and
578
     * are when exiting some operation any waiters are notified.
579
     */
580
    private final Object lock;
929
581
930
        if (isDispatchThread()) {
582
    /**
931
            try {
583
     * Map from threads to associated information.
932
                return run.run ();
584
     * A thread will only be in here if it is currently either in the mutex
933
            } catch (RuntimeException e) {
585
     * somehow or waiting for it; when it is done, its entry is removed.
934
                throw e;
586
     */
935
            } catch (Exception e) {
587
    private final Map threads = new HashMap(); // Map<Thread,ThreadInfo>
936
                throw new MutexException (e);
588
    
937
            } catch (LinkageError e) {
589
    /**
938
                // #20467
590
     * Get information about the current thread.
939
                throw new MutexException(new InvocationTargetException(e));
591
     * Must be called with the lock held.
940
            } catch (StackOverflowError e) {
592
     */
941
                // #20467
593
    private ThreadInfo currentThreadInfo() {
942
                throw new MutexException(new InvocationTargetException(e));
594
        return (ThreadInfo)threads.get(Thread.currentThread());
943
            }
595
    }
596
    
597
    /**
598
     * Count of active readers, or write mutex state.
599
     * When positive, one or more readers are holding the read mutex.
600
     * When zero, the mutex is uncontended.
601
     * When -1, the write mutex is held.
602
     */
603
    private int semaphore = 0;
604
    
605
    /**
606
     * Information about a thread and what it is waiting to do.
607
     */
608
    private final class ThreadInfo {
609
        
610
        /**
611
         * The associated thread.
612
         */
613
        public final Thread t;
614
        
615
        /**
616
         * If non-null, a list of things to do in the read mutex
617
         * immediately after exiting the write mutex.
618
         * @see postReadRequest
619
         */
620
        public List lateReadActions = null;
621
        
622
        /**
623
         * If non-null, a list of things to do in the write mutex
624
         * immediately after exiting a read mutex inside a write mutex.
625
         * @see #postWriteRequest
626
         */
627
        public List lateWriteActions = null;
628
        
629
        /**
630
         * What state this thread is in.
631
         * If true, it is a writer (possibly in a nested read mutex),
632
         * if false it is purely a reader (possibly again in a nested read mutex).
633
         */
634
        public boolean writer;
635
        
636
        /**
637
         * Count of additional read mutex reentries made by this thread.
638
         * They have no additional effect but are counted so that enter
639
         * and exit calls can be properly paired.
640
         */
641
        public int extraReads = 0;
642
        
643
        /**
644
         * Count of additional write mutex reentries made by this thread.
645
         * They have no additional effect but are counted so that enter
646
         * and exit calls can be properly paired.
647
         */
648
        public int extraWrites = 0;
649
        
650
        /**
651
         * Create new thread info based on the current thread.
652
         */
653
        public ThreadInfo(boolean writer) {
654
            t = Thread.currentThread();
655
            this.writer = writer;
656
        }
657
        
658
        /**
659
         * Register this thread info in the table.
660
         */
661
        public void register() {
662
            threads.put(t, this);
663
        }
664
        
665
        /**
666
         * Remove this thread info from the table.
667
         */
668
        public void unregister() {
669
            threads.remove(t);
944
        }
670
        }
945
671
        
946
        final Throwable[] arr = new Throwable[1];
672
        public String toString() {
947
        try {
673
            return "ThreadInfo<" + t + ",writer=" + writer + ",extraReads=" + extraReads + ",extraWrites=" + extraWrites + ",lateReadActions=" + lateReadActions + ",lateWriteActions=" + lateWriteActions + ">"; // NOI18N
948
            final Object[] res = new Object[1];
949
            EventQueue.invokeAndWait (new Runnable () {
950
                                          public void run () {
951
                                              try {
952
                                                  res[0] = run.run ();
953
                                              } catch (Exception e) {
954
                                                  arr[0] = e;
955
                                              } catch (LinkageError e) {
956
                                                  // #20467
957
                                                  arr[0] = e;
958
                                              } catch (StackOverflowError e) {
959
                                                  // #20467
960
                                                  arr[0] = e;
961
                                              }
962
                                          }
963
                                      });
964
            if (arr[0] == null) {
965
                return res[0];
966
            }
967
        } catch (InterruptedException e) {
968
            arr[0] = e;
969
        } catch (InvocationTargetException e) {
970
            arr[0] = e;
971
        }
674
        }
972
        
675
        
973
	if(arr[0] instanceof RuntimeException) {
974
            throw (RuntimeException)arr[0];
975
	}
976
	
977
	throw notifyException(ErrorManager.EXCEPTION, arr[0]);
978
    }
676
    }
677
    
678
    // --- Mutex.EVENT stuff ---
979
679
980
    /** @return true iff current thread is EventDispatchThread */
680
    /** @return true iff current thread is EventDispatchThread */
981
    static boolean isDispatchThread() {
681
    private static boolean isDispatchThread() {
982
        boolean dispatch = EventQueue.isDispatchThread ();
682
        boolean dispatch = EventQueue.isDispatchThread ();
983
        if (!dispatch && Utilities.getOperatingSystem () == Utilities.OS_SOLARIS) {
683
        if (!dispatch && Utilities.getOperatingSystem () == Utilities.OS_SOLARIS) {
984
            // on solaris the event queue is not always recognized correctly
684
            // on solaris the event queue is not always recognized correctly
Lines 987-1035 Link Here
987
        }
687
        }
988
        return dispatch;
688
        return dispatch;
989
    }
689
    }
990
    
690
991
    /** Notify exception and returns new MutexException */
691
    /**
992
    private static final MutexException notifyException(int severity, Throwable t) {
692
     * Run an exception action synchronously in AWT and return the result.
993
        if (t instanceof InvocationTargetException) {
693
     */
994
            t = unfoldInvocationTargetException((InvocationTargetException) t);
694
    private static Object eventAccess(final ExceptionAction action) throws MutexException {
995
        }
695
        if (isDispatchThread()) {
996
        
696
            try {
997
        if (t instanceof Error) {
697
                return action.run();
998
            annotateEventStack(t);
698
            } catch (Exception e) {
999
            throw (Error) t;
699
                throw new MutexException(e);
1000
        }
700
            }
1001
        
701
        } else {
1002
        if (t instanceof RuntimeException) {
702
            final Exception[] exc = new Exception[0];
1003
            annotateEventStack(t);
703
            final Object[] result = new Object[0];
1004
            throw (RuntimeException) t;
704
            try {
705
                EventQueue.invokeAndWait(new Runnable() {
706
                    public void run() {
707
                        try {
708
                            result[0] = action.run();
709
                        } catch (Exception e) {
710
                            exc[0] = e;
711
                        }
712
                    }
713
                });
714
            } catch (InterruptedException e) {
715
                throw new IllegalStateException(e.toString());
716
            } catch (InvocationTargetException e) {
717
                // Should not happen since we caught Exception above already:
718
                throw new IllegalStateException(e.getTargetException().toString());
719
            }
720
            if (exc[0] != null) {
721
                throw new MutexException(exc[0]);
722
            } else {
723
                return result[0];
724
            }
1005
        }
725
        }
1006
        
1007
        MutexException exc = new MutexException((Exception) t);
1008
        ErrorManager.getDefault().annotate(exc, t);
1009
	return exc;
1010
    }
726
    }
1011
    
727
    
1012
    private static final void annotateEventStack(Throwable t) {
728
    /**
1013
        ErrorManager.getDefault().annotate(t,
729
     * Run a plain action synchronously in AWT and return the result.
1014
		    new Exception("Caught here in mutex")); // NOI18N
730
     */
731
    private static Object eventAccess(final Action action) {
732
        if (isDispatchThread()) {
733
            return action.run();
734
        } else {
735
            final Object[] result = new Object[0];
736
            try {
737
                EventQueue.invokeAndWait(new Runnable() {
738
                    public void run() {
739
                        result[0] = action.run();
740
                    }
741
                });
742
            } catch (InterruptedException e) {
743
                throw new IllegalStateException(e.toString());
744
            } catch (InvocationTargetException e) {
745
                Throwable t = e.getTargetException();
746
                if (t instanceof RuntimeException) {
747
                    throw (RuntimeException)t;
748
                } else if (t instanceof Error) {
749
                    throw (Error)t;
750
                } else {
751
                    throw new IllegalStateException(t.toString());
752
                }
753
            }
754
            return result[0];
755
        }
1015
    }
756
    }
1016
    
757
    
1017
    private static final Throwable unfoldInvocationTargetException(InvocationTargetException e) {
758
    /**
1018
        Throwable ret;
759
     * Run something in AWT.
1019
        do {
760
     * If we are already in AWT, it is just run.
1020
            ret = e.getTargetException();
761
     * Otherwise it may be run synch or asynch according to the parameter.
1021
            if (ret instanceof InvocationTargetException) {
762
     */
1022
                e = (InvocationTargetException) ret;
763
    private static void eventAccess(Runnable run, boolean asynch) {
1023
            } else {
764
        if (isDispatchThread()) {
1024
                e = null;
765
            run.run();
766
        } else if (asynch) {
767
            EventQueue.invokeLater(run);
768
        } else {
769
            try {
770
                EventQueue.invokeAndWait(run);
771
            } catch (InterruptedException e) {
772
                throw new IllegalStateException(e.toString());
773
            } catch (InvocationTargetException e) {
774
                Throwable t = e.getTargetException();
775
                if (t instanceof RuntimeException) {
776
                    throw (RuntimeException)t;
777
                } else if (t instanceof Error) {
778
                    throw (Error)t;
779
                } else {
780
                    throw new IllegalStateException(t.toString());
781
                }
1025
            }
782
            }
1026
        } while (e != null);
783
        }
1027
        
1028
        return ret;
1029
    }
784
    }
1030
785
    
1031
    // --------------------------------------------- END OF EVENT METHODS ------------------------------
786
    // --- Action interfaces ---
1032
787
    
1033
    /** Action to be executed in a mutex without throwing any checked exceptions.
788
    /** Action to be executed in a mutex without throwing any checked exceptions.
1034
    * Unchecked exceptions will be propagated to calling code.
789
    * Unchecked exceptions will be propagated to calling code.
1035
    */
790
    */
Lines 1055-1126 Link Here
1055
        public Object run () throws Exception;
810
        public Object run () throws Exception;
1056
    }
811
    }
1057
812
1058
    private static final class ThreadInfo {
1059
1060
        /** t is forcibly sent from waiters to enter() by wakeUpOthers() */
1061
        boolean forced;
1062
        /** ThreadInfo for this Thread */
1063
        final Thread t;
1064
        /** granted mode */
1065
        int mode;
1066
        // 0 - NONE, 1 - CHAIN, 2 - X, 3 - S
1067
        /** enter counter */
1068
        int[] counts;
1069
        /** queue of runnable rquests that are to be executed (in X mode) right after S mode is left
1070
        * deadlock avoidance technique 
1071
        */
1072
        List[] queues;
1073
        /** time stamp of the thread
1074
        * set only for S mode
1075
        */
1076
        int stamp;
1077
1078
        /** value of counts[S] when the mode was upgraded
1079
        * rsnapshot works as follows:
1080
        * if a thread holds the mutex in the S mode and it reenters the mutex
1081
        * and requests X and the mode can be granted (no other readers) then this
1082
        * variable is set to counts[S]. This is used in the leave method in the X branch.
1083
        * (X mode is granted by other words)
1084
        * If rsnapshot is less than counts[S] then the counter is decremented etc. If the rsnapshot is
1085
        * equal to count[S] then count[X] is decremented. If the X counter is zeroed then
1086
        * rsnapshot is zeroed as well and current mode is downgraded to S mode.
1087
        * rsnapshot gets less than counts[S] if current mode is X and the mutex is reentered
1088
        * with S request.
1089
        */
1090
        int rsnapshot;
1091
1092
        public ThreadInfo(Thread t, int mode) {
1093
            this.t = t;
1094
            this.mode = mode;
1095
            this.counts = new int[MODE_COUNT];
1096
            this.queues = new List[MODE_COUNT];
1097
            counts[mode] = 1;
1098
        }
1099
1100
        public String toString() {
1101
            return super.toString() + " thread: " + t + " mode: " + mode + " X: " + counts[2] + " S: " + counts[3]; // NOI18N
1102
        }
1103
1104
        /** Adds the Runnable into the queue of waiting requests */
1105
        public void enqueue(int mode, Runnable run) {
1106
            if (queues[mode] == null) {
1107
                queues[mode] = new ArrayList(13);
1108
            }
1109
            queues[mode].add(run);
1110
        }
1111
1112
        /** @return a List of enqueued Runnables - may be null */
1113
        public List dequeue(int mode) {
1114
            List ret = queues[mode];
1115
            queues[mode] = null;
1116
            return ret;
1117
        }
1118
1119
        public int getRunnableCount(int mode) {
1120
            return (queues[mode] == null ? 0 : queues[mode].size());
1121
        }
1122
    }
1123
1124
    /** This class is defined only for better understanding of thread dumps where are informations like
813
    /** This class is defined only for better understanding of thread dumps where are informations like
1125
    * java.lang.Object@xxxxxxxx owner thread_x
814
    * java.lang.Object@xxxxxxxx owner thread_x
1126
    *   wait for enter thread_y
815
    *   wait for enter thread_y
Lines 1129-1192 Link Here
1129
	InternalLock() {}
818
	InternalLock() {}
1130
    }
819
    }
1131
820
1132
    private static final class QueueCell {
1133
1134
        int mode;
1135
        Thread t;
1136
        boolean signal;
1137
        /** if the thread is owner of AWTTreeLock then the timeout is greater than zero */
1138
        long timeout;
1139
        boolean left;
1140
        /** priority of the cell */
1141
        int priority2;
1142
1143
        public QueueCell(int mode, Thread t) {
1144
            this.mode = mode;
1145
            this.t = t;
1146
            this.timeout = 0;
1147
            this.left = false;
1148
            this.priority2 = 0;
1149
        }
1150
1151
        public String toString() {
1152
            return super.toString() + " mode: " + mode + " thread: " + t; // NOI18N
1153
        }
1154
1155
        /** @return priority of this cell */
1156
        public long getPriority() {
1157
            return (priority2 == 0 ? t.getPriority() : priority2);
1158
        }
1159
1160
        /** @return true iff the thread left sleep */
1161
        public boolean isGotOut() {
1162
            return left;
1163
        }
1164
1165
        /** current thread will sleep until wakeMeUp is called
1166
        * if wakeMeUp was already called then the thread will not sleep
1167
        */
1168
        public synchronized void sleep() {
1169
            try {
1170
                while (!signal) {
1171
                    try {
1172
                        wait();
1173
                        return;
1174
                    } catch (InterruptedException e) {
1175
                        ErrorManager.getDefault().notify(e);
1176
                    }
1177
                }
1178
            } finally {
1179
                left = true;
1180
            }
1181
        }
1182
1183
        /** sends signal to a sleeper - to a thread that is in the sleep() */
1184
        public void wakeMeUp() {
1185
            signal = true;
1186
            notifyAll();
1187
        }
1188
    }
1189
    
1190
    /** Provides access to Mutex's internal methods.
821
    /** Provides access to Mutex's internal methods.
1191
     * 
822
     * 
1192
     * This class can be used when one wants to avoid creating a 
823
     * This class can be used when one wants to avoid creating a 
Lines 1214-1232 Link Here
1214
        }
845
        }
1215
        
846
        
1216
        public void enterReadAccess() {
847
        public void enterReadAccess() {
1217
            parent.readEnter(Thread.currentThread());
848
            parent.enterReadAccess();
1218
        }
849
        }
1219
        
850
        
1220
        public void enterWriteAccess() {
851
        public void enterWriteAccess() {
1221
            parent.writeEnter(Thread.currentThread());
852
            parent.enterWriteAccess();
1222
        }
853
        }
1223
        
854
        
1224
        public void exitReadAccess() {
855
        public void exitReadAccess() {
1225
            parent.leave(Thread.currentThread());
856
            parent.exitReadAccess();
1226
        }
857
        }
1227
        
858
        
1228
        public void exitWriteAccess() {
859
        public void exitWriteAccess() {
1229
            parent.leave(Thread.currentThread());
860
            parent.exitWriteAccess();
1230
        }
861
        }
1231
    }
862
    }
1232
}
863
}
(-)openide/test/unit/src/org/openide/util/MutexTest.java (-4 / +8 lines)
Lines 133-139 Link Here
133
    
133
    
134
    /** Behaviour of postReadRequest is defined by this test.
134
    /** Behaviour of postReadRequest is defined by this test.
135
     */
135
     */
136
    public void testPostReadRequest () {
136
    // Cannot go S -> X
137
    public void DONOTtestPostReadRequest () {
137
        
138
        
138
        State s = new State ();
139
        State s = new State ();
139
        
140
        
Lines 162-168 Link Here
162
    }
163
    }
163
    
164
    
164
    /** Test enter from S mode to X mode */
165
    /** Test enter from S mode to X mode */
165
    public void testXtoS() {
166
    // You cannot go from S -> X
167
    public void DONOTtestXtoS() {
166
        State s = new State ();
168
        State s = new State ();
167
        
169
        
168
        p.enterReadAccess ();
170
        p.enterReadAccess ();
Lines 409-415 Link Here
409
    /** Tests postWriteRequest and postReadRequest while the Mutex is contended in S mode by
411
    /** Tests postWriteRequest and postReadRequest while the Mutex is contended in S mode by
410
     * another thread as well as this thread.
412
     * another thread as well as this thread.
411
     */
413
     */
412
    public void testSContendedSPsPx() throws InterruptedException {
414
    // Cannot go S -> X
415
    public void DONOTtestSContendedSPsPx() throws InterruptedException {
413
        asyncEnter(p, false, 2000);
416
        asyncEnter(p, false, 2000);
414
        
417
        
415
        State s = new State ();
418
        State s = new State ();
Lines 439-445 Link Here
439
    /** The Mutex is held in S mode by a thread which also posted a
442
    /** The Mutex is held in S mode by a thread which also posted a
440
     * write request. Another thread tries enterWriteAccess.
443
     * write request. Another thread tries enterWriteAccess.
441
     */
444
     */
442
    public void testSPxContendedX() throws Exception {
445
    // No good. Cannot call postWriteRequest from a read mutex.
446
    public void DONOTtestSPxContendedX() throws Exception {
443
        final State s = new State ();
447
        final State s = new State ();
444
448
445
        asyncEnter(p, false, 2000, new Runnable() {
449
        asyncEnter(p, false, 2000, new Runnable() {

Return to bug 32439