Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/jcl/src/java.base/share/classes/java/lang/ThreadGroup.java
12513 views
1
/*[INCLUDE-IF Sidecar18-SE & !OPENJDK_THREAD_SUPPORT]*/
2
/*******************************************************************************
3
* Copyright (c) 1998, 2022 IBM Corp. and others
4
*
5
* This program and the accompanying materials are made available under
6
* the terms of the Eclipse Public License 2.0 which accompanies this
7
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
8
* or the Apache License, Version 2.0 which accompanies this distribution and
9
* is available at https://www.apache.org/licenses/LICENSE-2.0.
10
*
11
* This Source Code may also be made available under the following
12
* Secondary Licenses when the conditions for such availability set
13
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
14
* General Public License, version 2 with the GNU Classpath
15
* Exception [1] and GNU General Public License, version 2 with the
16
* OpenJDK Assembly Exception [2].
17
*
18
* [1] https://www.gnu.org/software/classpath/license.html
19
* [2] http://openjdk.java.net/legal/assembly-exception.html
20
*
21
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
22
*******************************************************************************/
23
package java.lang;
24
25
/**
26
* ThreadGroups are containers of Threads and ThreadGroups, therefore providing
27
* a tree-like structure to organize Threads. The root ThreadGroup name is "system"
28
* and it has no parent ThreadGroup. All other ThreadGroups have exactly one parent
29
* ThreadGroup. All Threads belong to exactly one ThreadGroup.
30
*
31
* @author OTI
32
* @version initial
33
*
34
* @see Thread
35
* @see SecurityManager
36
*/
37
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
38
39
private String name; // Name of this ThreadGroup
40
private int maxPriority = Thread.MAX_PRIORITY; // Maximum priority for Threads inside this ThreadGroup
41
ThreadGroup parent; // The ThreadGroup to which this ThreadGroup belongs
42
/*[PR 93952]*/
43
int numThreads;
44
private Thread[] childrenThreads = new Thread[5]; // The Threads this ThreadGroup contains
45
int numGroups; // The number of children groups
46
private ThreadGroup[] childrenGroups = new ThreadGroup[3]; // The ThreadGroups this ThreadGroup contains
47
48
/*[PR 106322] - Cannot synchronize on childrenThreads and childrenGroups arrays */
49
/*[PR 122459] LIR646 - Remove use of generic object for synchronization */
50
private static final class ChildrenGroupsLock { ChildrenGroupsLock() { super(); } }
51
// Locked when using the childrenGroups field
52
private Object childrenGroupsLock = new ChildrenGroupsLock();
53
/*[PR 122459] LIR646 - Remove use of generic object for synchronization */
54
private static final class ChildrenThreadsLock { ChildrenThreadsLock() { super(); } }
55
// Locked when using the childrenThreads field
56
private Object childrenThreadsLock = new ChildrenThreadsLock();
57
58
private boolean isDaemon; // Whether this ThreadGroup is a daemon ThreadGroup or not
59
private boolean isDestroyed; // Whether this ThreadGroup has already been destroyed or not
60
/*[PR CMVC 99507] Do not destroy daemon group if threads have been added but not started */
61
private int addedNotStartedThreads; // Threads that have been added but not yet started
62
63
/**
64
* Used by the JVM to create the "system" ThreadGroup. Construct
65
* a ThreadGroup instance, and assign the name "system".
66
*/
67
@SuppressWarnings("unused")
68
private ThreadGroup() {
69
name = "system"; //$NON-NLS-1$
70
}
71
72
/**
73
* Constructs a new ThreadGroup with the name provided.
74
* The new ThreadGroup will be child of the ThreadGroup to which
75
* the <code>Thread.currentThread()</code> belongs.
76
*
77
* @param name Name for the ThreadGroup being created
78
*
79
* @throws SecurityException if <code>checkAccess()</code> for the parent group fails with a SecurityException
80
*
81
* @see java.lang.Thread#currentThread
82
*/
83
84
public ThreadGroup(String name) {
85
this(Thread.currentThread().getThreadGroup(), name);
86
}
87
88
/**
89
* Constructs a new ThreadGroup with the name provided, as child of
90
* the ThreadGroup <code>parent</code>
91
*
92
* @param parent Parent ThreadGroup
93
* @param name Name for the ThreadGroup being created
94
*
95
* @throws NullPointerException if <code>parent</code> is <code>null</code>
96
* @throws SecurityException if <code>checkAccess()</code> for the parent group fails with a SecurityException
97
* @throws IllegalThreadStateException if <code>parent</code> has been destroyed already
98
*/
99
public ThreadGroup(ThreadGroup parent, String name) {
100
super();
101
if (Thread.currentThread() != null) {
102
// If parent is null we must throw NullPointerException, but that will be done "for free"
103
// with the message send below
104
parent.checkAccess();
105
}
106
107
this.name = name;
108
this.setParent(parent);
109
if (parent != null) {
110
this.setMaxPriority(parent.getMaxPriority());
111
if (parent.isDaemon()) this.setDaemon(true);
112
}
113
}
114
115
/**
116
* Initialize the "main" ThreadGroup
117
*/
118
/*[PR CMVC 71192] Initialize the "main" thread group without calling checkAccess() */
119
ThreadGroup(ThreadGroup parent) {
120
this.name = "main"; //$NON-NLS-1$
121
this.setParent(parent);
122
}
123
124
/**
125
* Returns the number of Threads which are children of
126
* the receiver, directly or indirectly.
127
*
128
* @return Number of children Threads
129
*/
130
131
public int activeCount() {
132
/*[PR 115667, CMVC 93001] should only count active threads */
133
int count = 0;
134
// Lock this subpart of the tree as we walk
135
synchronized (childrenThreadsLock) {
136
for (int i = numThreads; --i >= 0;) {
137
if (childrenThreads[i].isAlive()) {
138
count++;
139
}
140
}
141
142
}
143
synchronized ( this.childrenGroupsLock ) { // Lock this subpart of the tree as we walk
144
for (int i = 0; i < numGroups; i++)
145
count += this.childrenGroups[i].activeCount();
146
}
147
return count;
148
}
149
/**
150
* Returns the number of ThreadGroups which are children of
151
* the receiver, directly or indirectly.
152
*
153
* @return Number of children ThreadGroups
154
*/
155
156
public int activeGroupCount() {
157
int count = 0;
158
synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk
159
for (int i = 0 ; i < numGroups; i++)
160
// One for this group & the subgroups
161
count += 1 + this.childrenGroups[i].activeGroupCount();
162
}
163
return count;
164
}
165
166
final void checkNewThread(Thread thread) throws IllegalThreadStateException {
167
synchronized (this.childrenThreadsLock) {
168
/*[PR 1FJC24P] testing state has to be done inside synchronized */
169
if (isDestroyed) {
170
throw new IllegalThreadStateException();
171
}
172
addedNotStartedThreads++;
173
}
174
}
175
176
/**
177
* Adds a Thread to the receiver. This should only be visible to class
178
* java.lang.Thread, and should only be called when a new Thread is created
179
* and initialized by the constructor.
180
*
181
* @param thread Thread to add to the receiver
182
*
183
* @throws IllegalThreadStateException if the receiver has been destroyed already
184
*
185
* @see #remove(java.lang.Thread)
186
*/
187
188
final void add(Thread thread) throws IllegalThreadStateException {
189
synchronized (this.childrenThreadsLock) {
190
/*[PR 1FJC24P] testing state has to be done inside synchronized */
191
if (!isDestroyed) {
192
if (childrenThreads.length == numThreads) {
193
Thread[] newThreads = new Thread[childrenThreads.length * 2];
194
System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads);
195
newThreads[numThreads++] = thread;
196
childrenThreads = newThreads;
197
} else childrenThreads[numThreads++] = thread;
198
addedNotStartedThreads--;
199
} else throw new IllegalThreadStateException();
200
}
201
}
202
203
/**
204
* Adds a ThreadGroup to the receiver.
205
*
206
* @param g ThreadGroup to add to the receiver
207
*
208
* @throws IllegalThreadStateException if the receiver has been destroyed already
209
*
210
*/
211
private void add(ThreadGroup g) throws IllegalThreadStateException {
212
/*[PR 1FJC24P] testing state has to be done inside synchronized */
213
/*[PR JAZZ 9154] ThreadGroup.isDestroyed is not properly synchronized */
214
synchronized(this.childrenThreadsLock) {
215
synchronized (this.childrenGroupsLock) {
216
if (!isDestroyed()) {
217
if (childrenGroups.length == numGroups) {
218
ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2];
219
System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups);
220
childrenGroups = newGroups;
221
}
222
childrenGroups[numGroups++] = g;
223
}
224
else throw new IllegalThreadStateException();
225
}
226
}
227
}
228
229
/**
230
* The definition of this method depends on the deprecated method <code>suspend()</code>.
231
* The behavior of this call was never specified.
232
*
233
* @param b Used to control low memory implicit suspension
234
* @return always returns true
235
*
236
* @deprecated Required deprecated method suspend().
237
*/
238
/*[IF JAVA_SPEC_VERSION >= 11]*/
239
/*[IF JAVA_SPEC_VERSION >= 14]*/
240
@Deprecated(forRemoval=true, since="1.2")
241
/*[ELSE] JAVA_SPEC_VERSION >= 14 */
242
@Deprecated(forRemoval=false, since="1.2")
243
/*[ENDIF] JAVA_SPEC_VERSION >= 14 */
244
/*[ELSE] JAVA_SPEC_VERSION >= 11 */
245
@Deprecated
246
/*[ENDIF] JAVA_SPEC_VERSION >= 11 */
247
public boolean allowThreadSuspension(boolean b) {
248
// Does not apply to this VM, no-op
249
/*[PR 1PR4U1E]*/
250
return true;
251
}
252
/**
253
* If there is a SecurityManager installed, call <code>checkAccess</code>
254
* in it passing the receiver as parameter, otherwise do nothing.
255
*/
256
/*[IF JAVA_SPEC_VERSION >= 17]*/
257
@Deprecated(since="17", forRemoval=true)
258
/*[ENDIF] JAVA_SPEC_VERSION >= 17 */
259
public final void checkAccess() {
260
// Forwards the message to the SecurityManager (if there's one)
261
// passing the receiver as parameter
262
@SuppressWarnings("removal")
263
SecurityManager currentManager = System.getSecurityManager();
264
if (currentManager != null) currentManager.checkAccess(this);
265
}
266
/**
267
* Destroys the receiver and recursively all its subgroups. It is only legal
268
* to destroy a ThreadGroup that has no Threads.
269
* Any daemon ThreadGroup is destroyed automatically when it becomes empty
270
* (no Threads and no ThreadGroups in it).
271
*
272
* @throws IllegalThreadStateException if the receiver or any of its subgroups has been destroyed already
273
* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException
274
/*[IF JAVA_SPEC_VERSION >= 16]
275
* @deprecated
276
/*[ENDIF] JAVA_SPEC_VERSION >= 16
277
*/
278
/*[IF JAVA_SPEC_VERSION >= 16]*/
279
@Deprecated(forRemoval = true, since = "16")
280
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */
281
public final void destroy() {
282
destroyImpl();
283
removeFromParent();
284
}
285
286
/**
287
* Do the destroy logic but do not remove ourselves from the parent threadgroup
288
* Callers must be sure to call removeFromParent() without holding locks to avoid deadlocks
289
*/
290
private void destroyImpl(){
291
/*[PR CMVC 198585] Deadlock when removing self from parent */
292
checkAccess();
293
294
synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk
295
synchronized (this.childrenGroupsLock) {
296
/*[PR 1FJC24P] testing state has to be done inside synchronized */
297
if (isDestroyed)
298
/*[MSG "K0056", "Already destroyed"]*/
299
throw new IllegalThreadStateException(com.ibm.oti.util.Msg.getString("K0056")); //$NON-NLS-1$
300
if (numThreads > 0)
301
/*[MSG "K0057", "Has threads"]*/
302
throw new IllegalThreadStateException(com.ibm.oti.util.Msg.getString("K0057")); //$NON-NLS-1$
303
304
int toDestroy = numGroups;
305
// Call recursively for subgroups
306
for (int i = 0 ; i < toDestroy; i++) {
307
// We always get the first element - remember,
308
// when the child dies it removes itself from our collection. See removeFromParent().
309
this.childrenGroups[0].destroy();
310
}
311
/*[PR CMVC 137999] move enclosing braces to avoid deadlock (allow parent to be removed outside of locks) */
312
}
313
// Now that the ThreadGroup is really destroyed it can be tagged as so
314
/*[PR 1FJJ0N7] Comment and code have to be consistent */
315
this.isDestroyed = true;
316
}
317
318
}
319
/**
320
* Auxiliary method that destroys the receiver and recursively all its subgroups
321
* if the receiver is a daemon ThreadGroup.
322
*
323
* @see #destroy
324
* @see #setDaemon
325
* @see #isDaemon
326
*/
327
328
private void destroyIfEmptyDaemon() {
329
boolean shouldRemoveFromParent = false;
330
331
// Has to be non-destroyed daemon to make sense
332
synchronized (this.childrenThreadsLock) {
333
/*[PR 1FJC24P] testing state has to be done inside synchronized */
334
if (isDaemon && !isDestroyed &&
335
/*[PR CMVC 99507] Do not destroy daemon group if threads have been added but not started */
336
addedNotStartedThreads == 0 &&
337
numThreads == 0)
338
{
339
synchronized (this.childrenGroupsLock) {
340
if (numGroups == 0) {
341
destroyImpl();
342
shouldRemoveFromParent = true;
343
}
344
}
345
}
346
}
347
348
if (shouldRemoveFromParent) {
349
removeFromParent();
350
}
351
}
352
353
/**
354
* Does a nullcheck and removes this threadgroup from the parent.
355
* Callers must not hold any locks when calling this function or a deadlock my occur
356
*/
357
private void removeFromParent() {
358
/*[PR CMVC 198585] Deadlock when removing self from parent */
359
/*[PR 97314] Cannot call getParent() */
360
if (parent != null) {
361
parent.remove(this);
362
}
363
}
364
/**
365
* Copies an array with all Threads which are children of
366
* the receiver (directly or indirectly) into the array <code>threads</code>
367
* passed as parameters. If the array passed as parameter is too small no
368
* exception is thrown - the extra elements are simply not copied.
369
*
370
* @param threads Thread array into which the Threads will be copied
371
* @return How many Threads were copied over
372
*
373
*/
374
375
public int enumerate(Thread[] threads) {
376
return enumerate(threads, true);
377
}
378
/**
379
* Copies an array with all Threads which are children of
380
* the receiver into the array <code>threads</code>
381
* passed as parameter. Children Threads of subgroups are recursively copied
382
* as well if parameter <code>recurse</code> is <code>true</code>.
383
*
384
* If the array passed as parameter is too small no
385
* exception is thrown - the extra elements are simply not copied.
386
*
387
* @param threads array into which the Threads will be copied
388
* @param recurse Indicates whether Threads in subgroups should be recursively copied as well or not
389
* @return How many Threads were copied over
390
*
391
*/
392
393
public int enumerate(Thread[] threads, boolean recurse) {
394
return enumerateGeneric(threads, recurse, 0, true);
395
}
396
/**
397
* Copies an array with all ThreadGroups which are children of
398
* the receiver (directly or indirectly) into the array <code>groups</code>
399
* passed as parameters. If the array passed as parameter is too small no
400
* exception is thrown - the extra elements are simply not copied.
401
*
402
* @param groups array into which the ThreadGroups will be copied
403
* @return How many ThreadGroups were copied over
404
*
405
*/
406
407
public int enumerate(ThreadGroup[] groups) {
408
return enumerate(groups, true);
409
}
410
/**
411
* Copies an array with all ThreadGroups which are children of
412
* the receiver into the array <code>groups</code>
413
* passed as parameter. Children ThreadGroups of subgroups are recursively copied
414
* as well if parameter <code>recurse</code> is <code>true</code>.
415
*
416
* If the array passed as parameter is too small no
417
* exception is thrown - the extra elements are simply not copied.
418
*
419
* @param groups array into which the ThreadGroups will be copied
420
* @param recurse Indicates whether ThreadGroups in subgroups should be recursively copied as well or not
421
* @return How many ThreadGroups were copied over
422
*
423
*/
424
425
public int enumerate(ThreadGroup[] groups, boolean recurse) {
426
return enumerateGeneric(groups, recurse, 0, false);
427
}
428
/**
429
* Copies into <param>enumeration</param> starting at </param>enumerationIndex</param>
430
* all Threads or ThreadGroups in the receiver. If </param>recurse</param>
431
* is true, recursively enumerate the elements in subgroups.
432
*
433
* If the array passed as parameter is too small no
434
* exception is thrown - the extra elements are simply not copied.
435
*
436
* @param enumeration array into which the elements will be copied
437
* @param recurse Indicates whether </param>recurseCollection</param> should be enumerated or not
438
* @param enumerationIndex Indicates in which position of the enumeration array we are
439
* @param enumeratingThreads Indicates whether we are enumerating Threads or ThreadGroups
440
* @return How many elements were enumerated/copied over
441
*/
442
443
private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, boolean enumeratingThreads) {
444
checkAccess();
445
446
Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;
447
448
synchronized (syncLock) { // Lock this subpart of the tree as we walk
449
/*[PR CMVC 94112] ArrayIndexOutOfBoundsException when enumerating Threads.*/
450
Object[] immediateCollection = enumeratingThreads ? (Object[])childrenThreads : (Object[])childrenGroups;
451
452
for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {
453
if (!enumeratingThreads || ((Thread)immediateCollection[i]).isAlive()) {
454
if (enumerationIndex >= enumeration.length) return enumerationIndex;
455
enumeration[enumerationIndex++] = immediateCollection[i];
456
}
457
}
458
}
459
460
if (recurse) { // Lock this subpart of the tree as we walk
461
synchronized (this.childrenGroupsLock) {
462
for (int i = 0; i < numGroups; i++) {
463
if (enumerationIndex >= enumeration.length) return enumerationIndex;
464
enumerationIndex = childrenGroups[i].
465
enumerateGeneric(enumeration, recurse, enumerationIndex, enumeratingThreads);
466
}
467
}
468
}
469
return enumerationIndex;
470
}
471
472
/**
473
* Copies into <param>enumeration</param> starting at </param>enumerationIndex</param>
474
* all Threads or ThreadGroups in the receiver. If </param>recurse</param>
475
* is true, recursively enumerate the elements in subgroups.
476
*
477
* If the array passed as parameter is too small no
478
* exception is thrown - the extra elements are simply not copied.
479
*
480
* @param enumeration array into which the elements will be copied
481
* @param recurse Indicates whether </param>recurseCollection</param> should be enumerated or not
482
* @param enumerationIndex Indicates in which position of the enumeration array we are
483
* @param enumeratingThreads Indicates whether we are enumerating Threads or ThreadGroups
484
* @return How many elements were enumerated/copied over
485
*/
486
487
int enumerateDeadThreads(Object[] enumeration, int enumerationIndex) {
488
boolean recurse = true;
489
boolean enumeratingThreads = true;
490
491
Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock;
492
493
synchronized (syncLock) { // Lock this subpart of the tree as we walk
494
/*[PR CMVC 94112] ArrayIndexOutOfBoundsException when enumerating Threads.*/
495
Object[] immediateCollection = enumeratingThreads ? (Object[])childrenThreads : (Object[])childrenGroups;
496
497
for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) {
498
if (!enumeratingThreads || !((Thread)immediateCollection[i]).isAlive()) {
499
if (enumerationIndex >= enumeration.length) return enumerationIndex;
500
enumeration[enumerationIndex++] = immediateCollection[i];
501
}
502
}
503
}
504
505
if (recurse) { // Lock this subpart of the tree as we walk
506
synchronized (this.childrenGroupsLock) {
507
for (int i = 0; i < numGroups; i++) {
508
if (enumerationIndex >= enumeration.length) return enumerationIndex;
509
enumerationIndex = childrenGroups[i].
510
enumerateDeadThreads(enumeration, enumerationIndex);
511
}
512
}
513
}
514
return enumerationIndex;
515
}
516
517
/**
518
* Answers the maximum allowed priority for a Thread in the receiver.
519
*
520
* @return the maximum priority (an <code>int</code>)
521
*
522
* @see #setMaxPriority
523
*/
524
525
public final int getMaxPriority() {
526
return maxPriority;
527
}
528
/**
529
* Answers the name of the receiver.
530
*
531
* @return the receiver's name (a java.lang.String)
532
*/
533
534
public final String getName() {
535
return name;
536
}
537
/**
538
* Answers the receiver's parent ThreadGroup. It can be null if the receiver
539
* is the root ThreadGroup.
540
*
541
* @return the parent ThreadGroup
542
*
543
*/
544
545
public final ThreadGroup getParent() {
546
/*[PR 97314]*/
547
if (parent != null)
548
parent.checkAccess();
549
else {
550
/*[IF] user created threadgroups can set the name to be system, however in
551
* the test below the name hasn't been set yet if the parent is null, and a
552
* nullpointerexception was thrown during threadgroup creation. */
553
/*[ENDIF]*/
554
/*[MSG "K0550", "current thread cannot modify this thread group"]*/
555
if (this.name == null || !this.name.equalsIgnoreCase("system")) //$NON-NLS-1$
556
throw new SecurityException(com.ibm.oti.util.Msg.getString("K0550")); //$NON-NLS-1$
557
}
558
559
return parent;
560
}
561
/**
562
* Interrupts every Thread in the receiver and recursively in all its subgroups.
563
*
564
* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException
565
*
566
* @see Thread#interrupt
567
*/
568
569
public final void interrupt() {
570
checkAccess();
571
synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk
572
for (int i = 0 ; i < numThreads; i++)
573
this.childrenThreads[i].interrupt();
574
}
575
synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk
576
for (int i = 0 ; i < numGroups; i++)
577
this.childrenGroups[i].interrupt();
578
}
579
}
580
/**
581
* Answers true if the receiver is a daemon ThreadGroup, false otherwise.
582
*
583
* @return if the receiver is a daemon ThreadGroup
584
*
585
* @see #setDaemon
586
* @see #destroy
587
/*[IF JAVA_SPEC_VERSION >= 16]
588
* @deprecated
589
/*[ENDIF] JAVA_SPEC_VERSION >= 16
590
*/
591
/*[IF JAVA_SPEC_VERSION >= 16]*/
592
@Deprecated(forRemoval = true, since = "16")
593
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */
594
public final boolean isDaemon() {
595
return isDaemon;
596
}
597
598
/**
599
* Answers true if the receiver has been destroyed already, false otherwise.
600
*
601
* @return if the receiver has been destroyed already
602
*
603
* @see #destroy
604
/*[IF JAVA_SPEC_VERSION >= 16]
605
* @deprecated
606
/*[ENDIF] JAVA_SPEC_VERSION >= 16
607
*/
608
/*[IF JAVA_SPEC_VERSION >= 16]*/
609
@Deprecated(forRemoval = true, since = "16")
610
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */
611
public boolean isDestroyed() {
612
// never call this when synchronized on childrenThreadsGroup or deadlock will occur
613
/*[PR JAZZ 9154] ThreadGroup.isDestroyed is not properly synchronized */
614
synchronized(childrenThreadsLock) {
615
return isDestroyed;
616
}
617
}
618
/**
619
* Outputs to <code>System.out</code> a text representation of the hierarchy of
620
* Threads and ThreadGroups in the receiver (and recursively). Proper indentation
621
* is done to suggest the nesting of groups inside groups and threads inside groups.
622
*/
623
624
public void list() {
625
System.out.println(); // We start in a fresh line
626
list(0);
627
}
628
/**
629
* Outputs to <code>System.out</code>a text representation of the hierarchy of
630
* Threads and ThreadGroups in the receiver (and recursively). The indentation
631
* will be four spaces per level of nesting.
632
*
633
* @param levels How many levels of nesting, so that proper indentation can be output.
634
*
635
*/
636
637
private void list(int levels) {
638
String spaces = " "; // 4 spaces for each level //$NON-NLS-1$
639
for (int i = 0; i < levels; i++)
640
System.out.print(spaces);
641
642
// Print the receiver
643
System.out.println(this.toString());
644
645
// Print the children threads, with 1 extra indentation
646
synchronized (this.childrenThreadsLock) {
647
for (int i = 0; i < numThreads; i++) {
648
for (int j = 0; j <= levels; j++)
649
System.out.print(spaces); // children get an extra indentation, 4 spaces for each level
650
System.out.println(this.childrenThreads[i]);
651
}
652
}
653
synchronized (this.childrenGroupsLock) {
654
for (int i = 0; i < numGroups; i++)
655
this.childrenGroups[i].list(levels + 1);
656
}
657
}
658
/**
659
* Answers true if the receiver is a direct or indirect parent group of
660
* ThreadGroup <code>g</code>, false otherwise.
661
*
662
* @param g ThreadGroup to test
663
*
664
* @return if the receiver is parent of the ThreadGroup passed as parameter
665
*
666
*/
667
668
public final boolean parentOf(ThreadGroup g) {
669
while (g != null) {
670
if (this == g) return true;
671
/*[PR 97314] Cannot call getParent() */
672
g = g.parent;
673
}
674
return false;
675
}
676
677
/**
678
* Removes a Thread from the receiver. This should only be visible to class
679
* java.lang.Thread, and should only be called when a Thread dies.
680
*
681
* @param thread Thread to remove from the receiver
682
*
683
* @see #add(Thread)
684
*/
685
final void remove(java.lang.Thread thread) {
686
687
/*[PR JAZZ 86608] Hang because ThreadGroup.remove synchronizes childrenThreadsLock and then synchronizes ThreadGroup itself */
688
boolean isThreadGroupEmpty = false;
689
690
synchronized (this.childrenThreadsLock) {
691
for (int i=0; i<numThreads; i++) {
692
/*[PR CMVC 109438] Dead Threads not removed from ThreadGroups */
693
if (childrenThreads[i] == thread) {
694
numThreads--;
695
if (numThreads == 0) {
696
isThreadGroupEmpty = true;
697
} else {
698
System.arraycopy (childrenThreads, i + 1, childrenThreads, i, numThreads - i);
699
}
700
childrenThreads[numThreads] = null;
701
break;
702
}
703
}
704
}
705
706
/*[PR CMVC 114880] ThreadGroup is not notified when all threads complete */
707
if (isThreadGroupEmpty) {
708
synchronized (this) {
709
notifyAll();
710
}
711
}
712
destroyIfEmptyDaemon();
713
}
714
715
/**
716
* Removes an immediate subgroup from the receiver.
717
*
718
* @param g Threadgroup to remove from the receiver
719
*
720
* @see #add(Thread)
721
* @see #add(ThreadGroup)
722
*/
723
private void remove(ThreadGroup g) {
724
synchronized (this.childrenGroupsLock) {
725
for (int i=0; i<numGroups; i++) {
726
/*[PR CMVC 109438] Dead Threads not removed from ThreadGroups */
727
if (childrenGroups[i] == g) {
728
numGroups--;
729
System.arraycopy (childrenGroups, i + 1, childrenGroups, i, numGroups - i);
730
childrenGroups[numGroups] = null;
731
break;
732
}
733
}
734
}
735
destroyIfEmptyDaemon();
736
}
737
/**
738
* Resumes every Thread in the receiver and recursively in all its subgroups.
739
*
740
* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException
741
*
742
* @see Thread#resume
743
* @see #suspend
744
*
745
* @deprecated Requires deprecated method Thread.resume().
746
*/
747
/*[IF JAVA_SPEC_VERSION >= 11]*/
748
/*[IF JAVA_SPEC_VERSION >= 14]*/
749
@Deprecated(forRemoval=true, since="1.2")
750
/*[ELSE] JAVA_SPEC_VERSION >= 14 */
751
@Deprecated(forRemoval=false, since="1.2")
752
/*[ENDIF] JAVA_SPEC_VERSION >= 14 */
753
/*[ELSE] JAVA_SPEC_VERSION >= 11 */
754
@Deprecated
755
/*[ENDIF] JAVA_SPEC_VERSION >= 11 */
756
public final void resume() {
757
checkAccess();
758
synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk
759
for (int i = 0 ; i < numThreads; i++)
760
this.childrenThreads[i].resume();
761
}
762
synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk
763
for (int i = 0 ; i < numGroups; i++)
764
this.childrenGroups[i].resume();
765
}
766
}
767
/**
768
* Configures the receiver to be a daemon ThreadGroup or not.
769
* Daemon ThreadGroups are automatically destroyed when they become empty.
770
*
771
* @param isDaemon new value defining if receiver should be daemon or not
772
*
773
* @throws SecurityException if <code>checkAccess()</code> for the parent group fails with a SecurityException
774
*
775
* @see #isDaemon
776
* @see #destroy
777
/*[IF JAVA_SPEC_VERSION >= 16]
778
* @deprecated
779
/*[ENDIF] JAVA_SPEC_VERSION >= 16
780
*/
781
/*[IF JAVA_SPEC_VERSION >= 16]*/
782
@Deprecated(forRemoval = true, since = "16")
783
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */
784
public final void setDaemon(boolean isDaemon) {
785
checkAccess();
786
this.isDaemon = isDaemon;
787
}
788
/**
789
* Configures the maximum allowed priority for a Thread in the receiver
790
* and recursively in all its subgroups.
791
*
792
* One can never change the maximum priority of a ThreadGroup to be
793
* higher than it was. Such an attempt will not result in an exception, it will
794
* simply leave the ThreadGroup with its current maximum priority.
795
*
796
* @param newMax the new maximum priority to be set
797
*
798
* @throws SecurityException if <code>checkAccess()</code> fails with a SecurityException
799
* @throws IllegalArgumentException if the new priority is greater than Thread.MAX_PRIORITY or less than
800
* Thread.MIN_PRIORITY
801
*
802
* @see #getMaxPriority
803
*/
804
805
public final void setMaxPriority(int newMax) {
806
checkAccess();
807
808
/*[PR 1FJ9S51] If new priority is greater than the current maximum, the maximum remains unchanged */
809
/*[PR CMVC 177870] Java7:JCK:java_lang/ThreadGroup/setMaxPriority fails in all platform */
810
if (Thread.MIN_PRIORITY <= newMax && newMax <= this.maxPriority) {
811
/*[PR 97314] Cannot call getParent() */
812
int parentPriority = parent == null ? newMax : parent.getMaxPriority();
813
this.maxPriority = parentPriority <= newMax ? parentPriority : newMax;
814
synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk
815
for (int i = 0 ; i < numGroups; i++)
816
this.childrenGroups[i].setMaxPriority(newMax);
817
}
818
}
819
}
820
/**
821
* Sets the parent ThreadGroup of the receiver, and adds the receiver to the parent's
822
* collection of immediate children (if <code>parent</code> is not <code>null</code>).
823
*
824
* @param parent The parent ThreadGroup, or null if the receiver is to be the root ThreadGroup
825
*
826
* @see #getParent
827
* @see #parentOf
828
*/
829
830
private void setParent(ThreadGroup parent) {
831
if (parent != null) parent.add(this);
832
this.parent = parent;
833
}
834
/**
835
* Stops every Thread in the receiver and recursively in all its subgroups.
836
*
837
* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException
838
*
839
* @see Thread#stop()
840
/*[IF JAVA_SPEC_VERSION < 11]
841
* @see Thread#stop(Throwable)
842
/*[ENDIF] JAVA_SPEC_VERSION < 11
843
* @see ThreadDeath
844
*
845
* @deprecated Requires deprecated method Thread.stop().
846
*/
847
/*[IF JAVA_SPEC_VERSION >= 16]*/
848
@Deprecated(forRemoval = true, since = "1.2")
849
/*[ELSE]*/
850
/*[IF Sidecar19-SE]*/
851
@Deprecated(forRemoval = false, since = "1.2")
852
/*[ELSE] Sidecar19-SE */
853
@Deprecated
854
/*[ENDIF] Sidecar19-SE */
855
/*[ENDIF] JAVA_SPEC_VERSION >= 16 */
856
public final void stop() {
857
/*[PR CMVC 73122] Stop the running thread last */
858
if (stopHelper())
859
Thread.currentThread().stop();
860
}
861
862
/**
863
* @deprecated Requires deprecated method Thread.suspend().
864
*/
865
@Deprecated
866
private final boolean stopHelper() {
867
checkAccess();
868
869
boolean stopCurrent = false;
870
synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk
871
Thread current = Thread.currentThread();
872
for (int i = 0 ; i < numThreads; i++)
873
if (this.childrenThreads[i] == current) {
874
stopCurrent = true;
875
} else {
876
this.childrenThreads[i].stop();
877
}
878
}
879
synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk
880
for (int i = 0 ; i < numGroups; i++)
881
stopCurrent |= this.childrenGroups[i].stopHelper();
882
}
883
return stopCurrent;
884
}
885
/**
886
* Suspends every Thread in the receiver and recursively in all its subgroups.
887
*
888
* @throws SecurityException if <code>this.checkAccess()</code> fails with a SecurityException
889
*
890
* @see Thread#suspend
891
* @see #resume
892
*
893
* @deprecated Requires deprecated method Thread.suspend().
894
*/
895
/*[IF JAVA_SPEC_VERSION >= 11]*/
896
/*[IF JAVA_SPEC_VERSION >= 14]*/
897
@Deprecated(forRemoval=true, since="1.2")
898
/*[ELSE] JAVA_SPEC_VERSION >= 14 */
899
@Deprecated(forRemoval=false, since="1.2")
900
/*[ENDIF] JAVA_SPEC_VERSION >= 14 */
901
/*[ELSE] JAVA_SPEC_VERSION >= 11 */
902
@Deprecated
903
/*[ENDIF] JAVA_SPEC_VERSION >= 11 */
904
public final void suspend() {
905
if (suspendHelper())
906
Thread.currentThread().suspend();
907
}
908
909
/**
910
* @deprecated Requires deprecated method Thread.suspend().
911
*/
912
@Deprecated
913
private final boolean suspendHelper() {
914
checkAccess();
915
916
boolean suspendCurrent = false;
917
synchronized (this.childrenThreadsLock) { // Lock this subpart of the tree as we walk
918
Thread current = Thread.currentThread();
919
for (int i = 0 ; i < numThreads; i++)
920
if (this.childrenThreads[i] == current) {
921
suspendCurrent = true;
922
} else {
923
this.childrenThreads[i].suspend();
924
}
925
}
926
synchronized (this.childrenGroupsLock) { // Lock this subpart of the tree as we walk
927
for (int i = 0 ; i < numGroups; i++)
928
suspendCurrent |= this.childrenGroups[i].suspendHelper();
929
}
930
return suspendCurrent;
931
}
932
933
/**
934
* Answers a string containing a concise, human-readable
935
* description of the receiver.
936
*
937
* @return a printable representation for the receiver.
938
*/
939
@Override
940
public String toString() {
941
return getClass().getName() + "[name=" + this.getName() + ",maxpri=" + this.getMaxPriority() + "]" ; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
942
}
943
944
945
/**
946
* Any uncaught exception in any Thread has to be forwarded (by the VM) to the Thread's ThreadGroup
947
* by sending this message (uncaughtException). This allows users to define custom ThreadGroup classes
948
* and custom behavior for when a Thread has an uncaughtException or when it does (ThreadDeath).
949
*
950
* @param t Thread with an uncaught exception
951
* @param e The uncaught exception itself
952
*
953
* @see Thread#stop()
954
/*[IF JAVA_SPEC_VERSION < 11]
955
* @see Thread#stop(Throwable)
956
/*[ENDIF] JAVA_SPEC_VERSION < 11
957
* @see ThreadDeath
958
*/
959
@Override
960
public void uncaughtException(Thread t, Throwable e) {
961
Thread.UncaughtExceptionHandler handler;
962
/*[PR 95801]*/
963
if (parent != null) {
964
parent.uncaughtException(t,e);
965
} else if ((handler = Thread.getDefaultUncaughtExceptionHandler()) != null) {
966
handler.uncaughtException(t, e);
967
} else if (!(e instanceof ThreadDeath)) {
968
// No parent group, has to be 'system' Thread Group
969
/*[MSG "K0319", "Exception in thread \"{0}\" "]*/
970
System.err.print(com.ibm.oti.util.Msg.getString("K0319", t.getName())); //$NON-NLS-1$
971
e.printStackTrace(System.err);
972
}
973
}
974
}
975
976