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 |
} |