1 |
/* java.lang.ThreadGroup |
/* ThreadGroup -- a group of Threads |
2 |
Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc. |
Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
7 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
8 |
the Free Software Foundation; either version 2, or (at your option) |
the Free Software Foundation; either version 2, or (at your option) |
9 |
any later version. |
any later version. |
10 |
|
|
11 |
GNU Classpath is distributed in the hope that it will be useful, but |
GNU Classpath is distributed in the hope that it will be useful, but |
12 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
34 |
this exception to your version of the library, but you are not |
this exception to your version of the library, but you are not |
35 |
obligated to do so. If you do not wish to do so, delete this |
obligated to do so. If you do not wish to do so, delete this |
36 |
exception statement from your version. */ |
exception statement from your version. */ |
37 |
|
|
38 |
package java.lang; |
package java.lang; |
39 |
|
|
40 |
import java.util.Vector; |
import java.util.Vector; |
|
import java.util.Enumeration; |
|
41 |
|
|
|
/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 |
|
|
* "The Java Language Specification", ISBN 0-201-63451-1 |
|
|
* plus online API docs for JDK 1.2 from http://www.javasoft.com. |
|
|
* Status: Complete for 1.2. Some parts from the JDK 1.0 spec only are |
|
|
* not implemented. |
|
|
*/ |
|
|
|
|
42 |
/** |
/** |
43 |
* ThreadGroup allows you to group Threads together. There is a |
* ThreadGroup allows you to group Threads together. There is a hierarchy |
44 |
* hierarchy of ThreadGroups, and only the initial ThreadGroup has |
* of ThreadGroups, and only the initial ThreadGroup has no parent. A Thread |
45 |
* no parent. A Thread may access information about its own |
* may access information about its own ThreadGroup, but not its parents or |
46 |
* ThreadGroup, but not its parents or others outside the tree. |
* others outside the tree. |
47 |
* |
* |
48 |
* @author John Keiser |
* @author John Keiser |
49 |
* @author Tom Tromey |
* @author Tom Tromey |
50 |
* @author Bryce McKinlay |
* @author Bryce McKinlay |
51 |
* @version 1.2.0 |
* @author Eric Blake <ebb9@email.byu.edu> |
52 |
* @since JDK1.0 |
* @see Thread |
53 |
|
* @since 1.0 |
54 |
|
* @status updated to 1.4 |
55 |
*/ |
*/ |
|
|
|
56 |
public class ThreadGroup |
public class ThreadGroup |
57 |
{ |
{ |
58 |
/* The Initial, top-level ThreadGroup. */ |
/** The Initial, top-level ThreadGroup. */ |
59 |
static ThreadGroup root = new ThreadGroup(); |
static ThreadGroup root = new ThreadGroup(); |
60 |
/* This flag is set if an uncaught exception occurs. The runtime should |
|
61 |
check this and exit with an error status if it is set. */ |
/** |
62 |
static boolean had_uncaught_exception = false; |
* This flag is set if an uncaught exception occurs. The runtime should |
63 |
|
* check this and exit with an error status if it is set. |
64 |
private ThreadGroup parent; |
*/ |
65 |
private String name; |
static boolean had_uncaught_exception; |
66 |
private Vector threads = new Vector(); |
|
67 |
|
/** The parent thread group. */ |
68 |
|
private final ThreadGroup parent; |
69 |
|
|
70 |
|
/** The group name, non-null. */ |
71 |
|
final String name; |
72 |
|
|
73 |
|
/** The threads in the group. */ |
74 |
|
private final Vector threads = new Vector(); |
75 |
|
|
76 |
|
/** Child thread groups, or null when this group is destroyed. */ |
77 |
private Vector groups = new Vector(); |
private Vector groups = new Vector(); |
78 |
|
|
79 |
|
/** If all threads in the group are daemons. */ |
80 |
private boolean daemon_flag = false; |
private boolean daemon_flag = false; |
|
private int maxpri = Thread.MAX_PRIORITY; |
|
81 |
|
|
82 |
|
/** The maximum group priority. */ |
83 |
|
private int maxpri; |
84 |
|
|
85 |
|
/** |
86 |
|
* Hidden constructor to build the root node. |
87 |
|
*/ |
88 |
private ThreadGroup() |
private ThreadGroup() |
89 |
{ |
{ |
90 |
name = "main"; |
name = "main"; |
91 |
|
parent = null; |
92 |
|
maxpri = Thread.MAX_PRIORITY; |
93 |
} |
} |
94 |
|
|
95 |
/** Create a new ThreadGroup using the given name and the |
/** |
96 |
* current thread's ThreadGroup as a parent. |
* Create a new ThreadGroup using the given name and the current thread's |
97 |
* @param name the name to use for the ThreadGroup. |
* ThreadGroup as a parent. There may be a security check, |
98 |
|
* <code>checkAccess</code>. |
99 |
|
* |
100 |
|
* @param name the name to use for the ThreadGroup |
101 |
|
* @throws SecurityException if the current thread cannot create a group |
102 |
|
* @see #checkAccess() |
103 |
*/ |
*/ |
104 |
public ThreadGroup(String name) |
public ThreadGroup(String name) |
105 |
{ |
{ |
106 |
this (Thread.currentThread().getThreadGroup(), name); |
this(Thread.currentThread().group, name); |
107 |
} |
} |
108 |
|
|
109 |
/** Create a new ThreadGroup using the given name and |
/** |
110 |
* parent group. |
* Create a new ThreadGroup using the given name and parent group. The new |
111 |
* @param name the name to use for the ThreadGroup. |
* group inherits the maximum priority and daemon status of its parent |
112 |
* @param parent the ThreadGroup to use as a parent. |
* group. There may be a security check, <code>checkAccess</code>. |
113 |
* @exception NullPointerException if parent is null. |
* |
114 |
* @exception SecurityException if you cannot change |
* @param name the name to use for the ThreadGroup |
115 |
* the intended parent group. |
* @param parent the ThreadGroup to use as a parent |
116 |
|
* @throws NullPointerException if parent is null |
117 |
|
* @throws SecurityException if the current thread cannot create a group |
118 |
|
* @throws IllegalThreadStateException if the parent is destroyed |
119 |
|
* @see #checkAccess() |
120 |
*/ |
*/ |
121 |
public ThreadGroup(ThreadGroup parent, String name) |
public ThreadGroup(ThreadGroup parent, String name) |
122 |
{ |
{ |
123 |
parent.checkAccess(); |
parent.checkAccess(); |
124 |
this.parent = parent; |
this.parent = parent; |
|
if (parent.isDestroyed()) |
|
|
throw new IllegalArgumentException (); |
|
125 |
this.name = name; |
this.name = name; |
126 |
maxpri = parent.maxpri; |
maxpri = parent.maxpri; |
127 |
daemon_flag = parent.daemon_flag; |
daemon_flag = parent.daemon_flag; |
128 |
parent.addGroup(this); |
synchronized (parent) |
129 |
|
{ |
130 |
|
if (parent.groups == null) |
131 |
|
throw new IllegalThreadStateException(); |
132 |
|
parent.groups.add(this); |
133 |
|
} |
134 |
} |
} |
135 |
|
|
136 |
/** Get the name of this ThreadGroup. |
/** |
137 |
* @return the name of this ThreadGroup. |
* Get the name of this ThreadGroup. |
138 |
|
* |
139 |
|
* @return the name of this ThreadGroup |
140 |
*/ |
*/ |
141 |
public final String getName() |
public final String getName() |
142 |
{ |
{ |
143 |
return name; |
return name; |
144 |
} |
} |
145 |
|
|
146 |
/** Get the parent of this ThreadGroup. |
/** |
147 |
* @return the parent of this ThreadGroup. |
* Get the parent of this ThreadGroup. If the parent is not null, there |
148 |
|
* may be a security check, <code>checkAccess</code>. |
149 |
|
* |
150 |
|
* @return the parent of this ThreadGroup |
151 |
|
* @throws SecurityException if permission is denied |
152 |
*/ |
*/ |
153 |
public final ThreadGroup getParent() |
public final ThreadGroup getParent() |
154 |
{ |
{ |
155 |
|
if (parent != null) |
156 |
|
parent.checkAccess(); |
157 |
return parent; |
return parent; |
158 |
} |
} |
159 |
|
|
160 |
/** Set the maximum priority for Threads in this ThreadGroup. setMaxPriority |
/** |
161 |
* can only be used to reduce the current maximum. If maxpri |
* Get the maximum priority of Threads in this ThreadGroup. Threads created |
162 |
* is greater than the current Maximum, the current value is not changed. |
* after this call in this group may not exceed this priority. |
163 |
* Calling this does not effect threads already in this ThreadGroup. |
* |
164 |
* @param maxpri the new maximum priority for this ThreadGroup. |
* @return the maximum priority of Threads in this ThreadGroup |
|
* @exception SecurityException if you cannoy modify this ThreadGroup. |
|
|
*/ |
|
|
public final synchronized void setMaxPriority(int maxpri) |
|
|
{ |
|
|
checkAccess(); |
|
|
if (maxpri < this.maxpri |
|
|
&& maxpri >= Thread.MIN_PRIORITY |
|
|
&& maxpri <= Thread.MAX_PRIORITY) |
|
|
{ |
|
|
this.maxpri = maxpri; |
|
|
} |
|
|
} |
|
|
|
|
|
/** Get the maximum priority of Threads in this ThreadGroup. |
|
|
* @return the maximum priority of Threads in this ThreadGroup. |
|
165 |
*/ |
*/ |
166 |
public final int getMaxPriority() |
public final int getMaxPriority() |
167 |
{ |
{ |
168 |
return maxpri; |
return maxpri; |
169 |
} |
} |
170 |
|
|
171 |
/** Set whether this ThreadGroup is a daemon group. A daemon |
/** |
172 |
* group will be destroyed when its last thread is stopped and |
* Tell whether this ThreadGroup is a daemon group. A daemon group will |
173 |
* its last thread group is destroyed. |
* be automatically destroyed when its last thread is stopped and |
174 |
* @specnote The Java API docs indicate that the group is destroyed |
* its last thread group is destroyed. |
175 |
* when either of those happen, but that doesn't make |
* |
176 |
* sense. |
* @return whether this ThreadGroup is a daemon group |
|
* @param daemon whether this ThreadGroup should be a daemon group. |
|
|
* @exception SecurityException if you cannoy modify this ThreadGroup. |
|
177 |
*/ |
*/ |
|
public final void setDaemon (boolean daemon) |
|
|
{ |
|
|
checkAccess(); |
|
|
daemon_flag = daemon; |
|
|
} |
|
|
|
|
|
/** Tell whether this ThreadGroup is a daemon group. A daemon |
|
|
* group will be destroyed when its last thread is stopped and |
|
|
* its last thread group is destroyed. |
|
|
* @specnote The Java API docs indicate that the group is destroyed |
|
|
* when either of those happen, but that doesn't make |
|
|
* sense. |
|
|
* @return whether this ThreadGroup is a daemon group. |
|
|
*/ |
|
178 |
public final boolean isDaemon() |
public final boolean isDaemon() |
179 |
{ |
{ |
180 |
return daemon_flag; |
return daemon_flag; |
181 |
} |
} |
182 |
|
|
183 |
/** Tell whether this ThreadGroup has been destroyed or not. |
/** |
184 |
* @return whether this ThreadGroup has been destroyed or not. |
* Tell whether this ThreadGroup has been destroyed or not. |
185 |
*/ |
* |
186 |
|
* @return whether this ThreadGroup has been destroyed or not |
187 |
|
* @since 1.1 |
188 |
|
*/ |
189 |
public synchronized boolean isDestroyed() |
public synchronized boolean isDestroyed() |
190 |
{ |
{ |
191 |
return parent == null && this != root; |
return groups == null; |
192 |
} |
} |
193 |
|
|
194 |
/** Check whether this ThreadGroup is an ancestor of the |
/** |
195 |
* specified ThreadGroup, or if they are the same. |
* Set whether this ThreadGroup is a daemon group. A daemon group will be |
196 |
* |
* destroyed when its last thread is stopped and its last thread group is |
197 |
* @param g the group to test on. |
* destroyed. There may be a security check, <code>checkAccess</code>. |
198 |
* @return whether this ThreadGroup is a parent of the |
* |
199 |
* specified group. |
* @param daemon whether this ThreadGroup should be a daemon group |
200 |
*/ |
* @throws SecurityException if you cannot modify this ThreadGroup |
201 |
|
* @see #checkAccess() |
202 |
|
*/ |
203 |
|
public final void setDaemon(boolean daemon) |
204 |
|
{ |
205 |
|
checkAccess(); |
206 |
|
daemon_flag = daemon; |
207 |
|
} |
208 |
|
|
209 |
|
/** |
210 |
|
* Set the maximum priority for Threads in this ThreadGroup. setMaxPriority |
211 |
|
* can only be used to reduce the current maximum. If maxpri is greater |
212 |
|
* than the current Maximum of the parent group, the current value is not |
213 |
|
* changed. Otherwise, all groups which belong to this have their priority |
214 |
|
* adjusted as well. Calling this does not affect threads already in this |
215 |
|
* ThreadGroup. There may be a security check, <code>checkAccess</code>. |
216 |
|
* |
217 |
|
* @param maxpri the new maximum priority for this ThreadGroup |
218 |
|
* @throws SecurityException if you cannot modify this ThreadGroup |
219 |
|
* @see #getMaxPriority() |
220 |
|
* @see #checkAccess() |
221 |
|
*/ |
222 |
|
public final synchronized void setMaxPriority(int maxpri) |
223 |
|
{ |
224 |
|
checkAccess(); |
225 |
|
if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY) |
226 |
|
return; |
227 |
|
if (parent != null && maxpri > parent.maxpri) |
228 |
|
maxpri = parent.maxpri; |
229 |
|
this.maxpri = maxpri; |
230 |
|
if (groups == null) |
231 |
|
return; |
232 |
|
int i = groups.size(); |
233 |
|
while (--i >= 0) |
234 |
|
((ThreadGroup) groups.get(i)).setMaxPriority(maxpri); |
235 |
|
} |
236 |
|
|
237 |
|
/** |
238 |
|
* Check whether this ThreadGroup is an ancestor of the specified |
239 |
|
* ThreadGroup, or if they are the same. |
240 |
|
* |
241 |
|
* @param g the group to test on |
242 |
|
* @return whether this ThreadGroup is a parent of the specified group |
243 |
|
*/ |
244 |
public final boolean parentOf(ThreadGroup tg) |
public final boolean parentOf(ThreadGroup tg) |
245 |
{ |
{ |
246 |
while (tg != null) |
while (tg != null) |
252 |
return false; |
return false; |
253 |
} |
} |
254 |
|
|
255 |
/** Return the total number of active threads in this ThreadGroup |
/** |
256 |
* and all its descendants.<P> |
* Find out if the current Thread can modify this ThreadGroup. This passes |
257 |
* |
* the check on to <code>SecurityManager.checkAccess(this)</code>. |
258 |
* This cannot return an exact number, since the status of threads |
* |
259 |
* may change after they were counted. But it should be pretty |
* @throws SecurityException if the current Thread cannot modify this |
260 |
* close.<P> |
* ThreadGroup |
261 |
* |
* @see SecurityManager#checkAccess(ThreadGroup) |
262 |
* @return the number of active threads in this ThreadGroup and |
*/ |
263 |
* its descendants. |
public final void checkAccess() |
|
* @specnote it isn't clear what the definition of an "Active" thread is. |
|
|
* Current JDKs regard a thread as active if has been |
|
|
* started and not finished. We implement this behaviour. |
|
|
* There is a JDC bug, <A HREF="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html"> |
|
|
* 4089701</A>, regarding this issue. |
|
|
* |
|
|
*/ |
|
|
public synchronized int activeCount() |
|
264 |
{ |
{ |
265 |
int total = 0; |
// Bypass System.getSecurityManager, for bootstrap efficiency. |
266 |
for (int i = 0; i < threads.size(); ++i) |
SecurityManager sm = Runtime.getSecurityManager(); |
267 |
{ |
if (sm != null) |
268 |
if (((Thread) threads.elementAt(i)).isAlive ()) |
sm.checkAccess(this); |
269 |
++total; |
} |
|
} |
|
270 |
|
|
271 |
for (int i=0; i < groups.size(); i++) |
/** |
272 |
{ |
* Return an estimate of the total number of active threads in this |
273 |
ThreadGroup g = (ThreadGroup) groups.elementAt(i); |
* ThreadGroup and all its descendants. This cannot return an exact number, |
274 |
total += g.activeCount(); |
* since the status of threads may change after they were counted; but it |
275 |
} |
* should be pretty close. Based on a JDC bug, |
276 |
|
* <a href="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html"> |
277 |
|
* 4089701</a>, we take active to mean isAlive(). |
278 |
|
* |
279 |
|
* @return count of active threads in this ThreadGroup and its descendants |
280 |
|
*/ |
281 |
|
public int activeCount() |
282 |
|
{ |
283 |
|
int total = 0; |
284 |
|
if (groups == null) |
285 |
|
return total; |
286 |
|
int i = threads.size(); |
287 |
|
while (--i >= 0) |
288 |
|
if (((Thread) threads.get(i)).isAlive()) |
289 |
|
total++; |
290 |
|
i = groups.size(); |
291 |
|
while (--i >= 0) |
292 |
|
total += ((ThreadGroup) groups.get(i)).activeCount(); |
293 |
return total; |
return total; |
294 |
} |
} |
295 |
|
|
296 |
/** Get the number of active groups in this ThreadGroup. This group |
/** |
297 |
* itself is not included in the count. |
* Copy all of the active Threads from this ThreadGroup and its descendants |
298 |
* @specnote it is unclear what exactly constitutes an |
* into the specified array. If the array is not big enough to hold all |
299 |
* active ThreadGroup. Currently we assume that |
* the Threads, extra Threads will simply not be copied. There may be a |
300 |
* all sub-groups are active, per current JDKs. |
* security check, <code>checkAccess</code>. |
301 |
* @return the number of active groups in this ThreadGroup. |
* |
302 |
*/ |
* @param array the array to put the threads into |
303 |
public synchronized int activeGroupCount() |
* @return the number of threads put into the array |
304 |
|
* @throws SecurityException if permission was denied |
305 |
|
* @throws NullPointerException if array is null |
306 |
|
* @throws ArrayStoreException if a thread does not fit in the array |
307 |
|
* @see #activeCount() |
308 |
|
* @see #checkAccess() |
309 |
|
* @see #enumerate(Thread[], boolean) |
310 |
|
*/ |
311 |
|
public int enumerate(Thread[] array) |
312 |
{ |
{ |
313 |
int total = groups.size(); |
return enumerate(array, 0, true); |
|
for (int i=0; i < groups.size(); i++) |
|
|
{ |
|
|
ThreadGroup g = (ThreadGroup) groups.elementAt(i); |
|
|
total += g.activeGroupCount(); |
|
|
} |
|
|
return total; |
|
314 |
} |
} |
315 |
|
|
316 |
/** Copy all of the active Threads from this ThreadGroup and |
/** |
317 |
* its descendants into the specified array. If the array is |
* Copy all of the active Threads from this ThreadGroup and, if desired, |
318 |
* not big enough to hold all the Threads, extra Threads will |
* from its descendants, into the specified array. If the array is not big |
319 |
* simply not be copied. |
* enough to hold all the Threads, extra Threads will simply not be copied. |
320 |
* |
* There may be a security check, <code>checkAccess</code>. |
321 |
* @param threads the array to put the threads into. |
* |
322 |
* @return the number of threads put into the array. |
* @param array the array to put the threads into |
323 |
*/ |
* @param recurse whether to recurse into descendent ThreadGroups |
324 |
public int enumerate(Thread[] threads) |
* @return the number of threads put into the array |
325 |
{ |
* @throws SecurityException if permission was denied |
326 |
return enumerate(threads, 0, true); |
* @throws NullPointerException if array is null |
327 |
} |
* @throws ArrayStoreException if a thread does not fit in the array |
328 |
|
* @see #activeCount() |
329 |
/** Copy all of the active Threads from this ThreadGroup and, |
* @see #checkAccess() |
330 |
* if desired, from its descendants, into the specified array. |
*/ |
331 |
* If the array is not big enough to hold all the Threads, |
public int enumerate(Thread[] array, boolean recurse) |
|
* extra Threads will simply not be copied. |
|
|
* |
|
|
* @param threads the array to put the threads into. |
|
|
* @param useDescendants whether to count Threads in this |
|
|
* ThreadGroup's descendants or not. |
|
|
* @return the number of threads put into the array. |
|
|
*/ |
|
|
public int enumerate(Thread[] threads, boolean useDescendants) |
|
|
{ |
|
|
return enumerate(threads, 0, useDescendants); |
|
|
} |
|
|
|
|
|
// This actually implements enumerate. |
|
|
private synchronized int enumerate(Thread[] list, int next_index, |
|
|
boolean recurse) |
|
332 |
{ |
{ |
333 |
Enumeration e = threads.elements(); |
return enumerate(array, 0, recurse); |
|
while (e.hasMoreElements() && next_index < list.length) |
|
|
{ |
|
|
Thread t = (Thread) e.nextElement(); |
|
|
if (t.isAlive ()) |
|
|
list[next_index++] = t; |
|
|
} |
|
|
if (recurse && next_index != list.length) |
|
|
{ |
|
|
e = groups.elements(); |
|
|
while (e.hasMoreElements() && next_index < list.length) |
|
|
{ |
|
|
ThreadGroup g = (ThreadGroup) e.nextElement(); |
|
|
next_index = g.enumerate(list, next_index, true); |
|
|
} |
|
|
} |
|
|
return next_index; |
|
334 |
} |
} |
335 |
|
|
336 |
/** Copy all active ThreadGroups that are descendants of this |
/** |
337 |
* ThreadGroup into the specified array. If the array is not |
* Get the number of active groups in this ThreadGroup. This group itself |
338 |
* large enough to hold all active ThreadGroups, extra |
* is not included in the count. A sub-group is active if it has not been |
339 |
* ThreadGroups simply will not be copied. |
* destroyed. This cannot return an exact number, since the status of |
340 |
* |
* threads may change after they were counted; but it should be pretty close. |
341 |
* @param groups the array to put the ThreadGroups into. |
* |
342 |
* @return the number of ThreadGroups copied into the array. |
* @return the number of active groups in this ThreadGroup |
343 |
*/ |
*/ |
344 |
public int enumerate(ThreadGroup[] groups) |
public int activeGroupCount() |
|
{ |
|
|
return enumerate(groups, 0, true); |
|
|
} |
|
|
|
|
|
/** Copy all active ThreadGroups that are children of this |
|
|
* ThreadGroup into the specified array, and if desired, also |
|
|
* copy all active descendants into the array. If the array |
|
|
* is not large enough to hold all active ThreadGroups, extra |
|
|
* ThreadGroups simply will not be copied. |
|
|
* |
|
|
* @param groups the array to put the ThreadGroups into. |
|
|
* @param recurse whether to include all descendants |
|
|
* of this ThreadGroup's children in determining |
|
|
* activeness. |
|
|
* @return the number of ThreadGroups copied into the array. |
|
|
*/ |
|
|
public int enumerate(ThreadGroup[] groups, boolean recurse) |
|
|
{ |
|
|
return enumerate(groups, 0, recurse); |
|
|
} |
|
|
|
|
|
// This actually implements enumerate. |
|
|
private synchronized int enumerate (ThreadGroup[] list, int next_index, |
|
|
boolean recurse) |
|
345 |
{ |
{ |
346 |
Enumeration e = groups.elements(); |
if (groups == null) |
347 |
while (e.hasMoreElements() && next_index < list.length) |
return 0; |
348 |
{ |
int total = groups.size(); |
349 |
ThreadGroup g = (ThreadGroup) e.nextElement(); |
int i = total; |
350 |
list[next_index++] = g; |
while (--i >= 0) |
351 |
if (recurse && next_index != list.length) |
total += ((ThreadGroup) groups.get(i)).activeGroupCount(); |
352 |
next_index = g.enumerate(list, next_index, true); |
return total; |
|
} |
|
|
return next_index; |
|
353 |
} |
} |
354 |
|
|
355 |
/** Interrupt all Threads in this ThreadGroup and its sub-groups. |
/** |
356 |
* @exception SecurityException if you cannot modify this |
* Copy all active ThreadGroups that are descendants of this ThreadGroup |
357 |
* ThreadGroup or any of its Threads or children |
* into the specified array. If the array is not large enough to hold all |
358 |
* ThreadGroups. |
* active ThreadGroups, extra ThreadGroups simply will not be copied. There |
359 |
* @since JDK1.2 |
* may be a security check, <code>checkAccess</code>. |
360 |
*/ |
* |
361 |
public final synchronized void interrupt() |
* @param array the array to put the ThreadGroups into |
362 |
|
* @return the number of ThreadGroups copied into the array |
363 |
|
* @throws SecurityException if permission was denied |
364 |
|
* @throws NullPointerException if array is null |
365 |
|
* @throws ArrayStoreException if a group does not fit in the array |
366 |
|
* @see #activeCount() |
367 |
|
* @see #checkAccess() |
368 |
|
* @see #enumerate(ThreadGroup[], boolean) |
369 |
|
*/ |
370 |
|
public int enumerate(ThreadGroup[] array) |
371 |
{ |
{ |
372 |
checkAccess(); |
return enumerate(array, 0, true); |
|
for (int i=0; i < threads.size(); i++) |
|
|
{ |
|
|
Thread t = (Thread) threads.elementAt(i); |
|
|
t.interrupt(); |
|
|
} |
|
|
for (int i=0; i < groups.size(); i++) |
|
|
{ |
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
|
|
tg.interrupt(); |
|
|
} |
|
373 |
} |
} |
374 |
|
|
375 |
/** Stop all Threads in this ThreadGroup and its descendants. |
/** |
376 |
* @exception SecurityException if you cannot modify this |
* Copy all active ThreadGroups that are children of this ThreadGroup into |
377 |
* ThreadGroup or any of its Threads or children |
* the specified array, and if desired, also all descendents. If the array |
378 |
* ThreadGroups. |
* is not large enough to hold all active ThreadGroups, extra ThreadGroups |
379 |
* @deprecated This method calls Thread.stop(), which is dangerous. |
* simply will not be copied. There may be a security check, |
380 |
*/ |
* <code>checkAccess</code>. |
381 |
public final synchronized void stop() |
* |
382 |
|
* @param array the array to put the ThreadGroups into |
383 |
|
* @param recurse whether to recurse into descendent ThreadGroups |
384 |
|
* @return the number of ThreadGroups copied into the array |
385 |
|
* @throws SecurityException if permission was denied |
386 |
|
* @throws NullPointerException if array is null |
387 |
|
* @throws ArrayStoreException if a group does not fit in the array |
388 |
|
* @see #activeCount() |
389 |
|
* @see #checkAccess() |
390 |
|
*/ |
391 |
|
public int enumerate(ThreadGroup[] array, boolean recurse) |
392 |
{ |
{ |
393 |
checkAccess(); |
return enumerate(array, 0, recurse); |
|
for (int i=0; i<threads.size(); i++) |
|
|
{ |
|
|
Thread t = (Thread) threads.elementAt(i); |
|
|
t.stop(); |
|
|
} |
|
|
for (int i=0; i < groups.size(); i++) |
|
|
{ |
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
|
|
tg.stop(); |
|
|
} |
|
394 |
} |
} |
395 |
|
|
396 |
/** Suspend all Threads in this ThreadGroup and its descendants. |
/** |
397 |
* @exception SecurityException if you cannot modify this |
* Stop all Threads in this ThreadGroup and its descendants. |
398 |
* ThreadGroup or any of its Threads or children |
* |
399 |
* ThreadGroups. |
* <p>This is inherently unsafe, as it can interrupt synchronized blocks and |
400 |
* @deprecated This method calls Thread.suspend(), which is dangerous. |
* leave data in bad states. Hence, there is a security check: |
401 |
*/ |
* <code>checkAccess()</code>, followed by further checks on each thread |
402 |
|
* being stopped. |
403 |
|
* |
404 |
|
* @throws SecurityException if permission is denied |
405 |
|
* @see #checkAccess() |
406 |
|
* @see Thread#stop(Throwable) |
407 |
|
* @deprecated unsafe operation, try not to use |
408 |
|
*/ |
409 |
|
public final synchronized void stop() |
410 |
|
{ |
411 |
|
checkAccess(); |
412 |
|
if (groups == null) |
413 |
|
return; |
414 |
|
int i = threads.size(); |
415 |
|
while (--i >= 0) |
416 |
|
((Thread) threads.get(i)).stop(); |
417 |
|
i = groups.size(); |
418 |
|
while (--i >= 0) |
419 |
|
((ThreadGroup) groups.get(i)).stop(); |
420 |
|
} |
421 |
|
|
422 |
|
/** |
423 |
|
* Interrupt all Threads in this ThreadGroup and its sub-groups. There may |
424 |
|
* be a security check, <code>checkAccess</code>. |
425 |
|
* |
426 |
|
* @throws SecurityException if permission is denied |
427 |
|
* @see #checkAccess() |
428 |
|
* @see Thread#interrupt() |
429 |
|
* @since 1.2 |
430 |
|
*/ |
431 |
|
public final synchronized void interrupt() |
432 |
|
{ |
433 |
|
checkAccess(); |
434 |
|
if (groups == null) |
435 |
|
return; |
436 |
|
int i = threads.size(); |
437 |
|
while (--i >= 0) |
438 |
|
((Thread) threads.get(i)).interrupt(); |
439 |
|
i = groups.size(); |
440 |
|
while (--i >= 0) |
441 |
|
((ThreadGroup) groups.get(i)).interrupt(); |
442 |
|
} |
443 |
|
|
444 |
|
/** |
445 |
|
* Suspend all Threads in this ThreadGroup and its descendants. |
446 |
|
* |
447 |
|
* <p>This is inherently unsafe, as suspended threads still hold locks, |
448 |
|
* which can lead to deadlock. Hence, there is a security check: |
449 |
|
* <code>checkAccess()</code>, followed by further checks on each thread |
450 |
|
* being suspended. |
451 |
|
* |
452 |
|
* @throws SecurityException if permission is denied |
453 |
|
* @see #checkAccess() |
454 |
|
* @see Thread#suspend() |
455 |
|
* @deprecated unsafe operation, try not to use |
456 |
|
*/ |
457 |
public final synchronized void suspend() |
public final synchronized void suspend() |
458 |
{ |
{ |
459 |
checkAccess(); |
checkAccess(); |
460 |
for (int i=0; i<threads.size(); i++) |
if (groups == null) |
461 |
{ |
return; |
462 |
Thread t = (Thread) threads.elementAt(i); |
int i = threads.size(); |
463 |
t.suspend(); |
while (--i >= 0) |
464 |
} |
((Thread) threads.get(i)).suspend(); |
465 |
for (int i=0; i < groups.size(); i++) |
i = groups.size(); |
466 |
{ |
while (--i >= 0) |
467 |
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
((ThreadGroup) groups.get(i)).suspend(); |
468 |
tg.suspend(); |
} |
469 |
} |
|
470 |
} |
/** |
471 |
|
* Resume all suspended Threads in this ThreadGroup and its descendants. |
472 |
/** Resume all Threads in this ThreadGroup and its descendants. |
* To mirror suspend(), there is a security check: |
473 |
* @exception SecurityException if you cannot modify this |
* <code>checkAccess()</code>, followed by further checks on each thread |
474 |
* ThreadGroup or any of its Threads or children |
* being resumed. |
475 |
* ThreadGroups. |
* |
476 |
* @deprecated This method relies on Thread.suspend(), which is dangerous. |
* @throws SecurityException if permission is denied |
477 |
*/ |
* @see #checkAccess() |
478 |
|
* @see Thread#suspend() |
479 |
|
* @deprecated pointless, since suspend is deprecated |
480 |
|
*/ |
481 |
public final synchronized void resume() |
public final synchronized void resume() |
482 |
{ |
{ |
483 |
checkAccess(); |
checkAccess(); |
484 |
for (int i=0; i < threads.size(); i++) |
if (groups == null) |
485 |
{ |
return; |
486 |
Thread t = (Thread) threads.elementAt(i); |
int i = threads.size(); |
487 |
t.resume(); |
while (--i >= 0) |
488 |
} |
((Thread) threads.get(i)).resume(); |
489 |
for (int i=0; i < groups.size(); i++) |
i = groups.size(); |
490 |
{ |
while (--i >= 0) |
491 |
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
((ThreadGroup) groups.get(i)).resume(); |
492 |
tg.resume(); |
} |
493 |
} |
|
494 |
} |
/** |
495 |
|
* Destroy this ThreadGroup. The group must be empty, meaning that all |
496 |
// This is a helper that is used to implement the destroy method. |
* threads and sub-groups have completed execution. Daemon groups are |
497 |
private synchronized void checkDestroy () |
* destroyed automatically. There may be a security check, |
498 |
{ |
* <code>checkAccess</code>. |
499 |
if (! threads.isEmpty()) |
* |
500 |
throw new IllegalThreadStateException ("ThreadGroup has threads"); |
* @throws IllegalThreadStateException if the ThreadGroup is not empty, or |
501 |
for (int i=0; i < groups.size(); i++) |
* was previously destroyed |
502 |
{ |
* @throws SecurityException if permission is denied |
503 |
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
* @see #checkAccess() |
504 |
tg.checkDestroy(); |
*/ |
|
} |
|
|
} |
|
|
|
|
|
/** Destroy this ThreadGroup. There can be no Threads in it, |
|
|
* and none of its descendants (sub-groups) may have Threads in them. |
|
|
* All its descendants will be destroyed as well. |
|
|
* @exception IllegalThreadStateException if the ThreadGroup or |
|
|
* its descendants have Threads remaining in them, or |
|
|
* if the ThreadGroup in question is already destroyed. |
|
|
* @exception SecurityException if you cannot modify this |
|
|
* ThreadGroup or any of its descendants. |
|
|
*/ |
|
505 |
public final synchronized void destroy() |
public final synchronized void destroy() |
506 |
{ |
{ |
507 |
checkAccess(); |
checkAccess(); |
508 |
if (isDestroyed()) |
if (! threads.isEmpty() || groups == null) |
509 |
throw new IllegalThreadStateException("Already destroyed."); |
throw new IllegalThreadStateException(); |
510 |
checkDestroy (); |
int i = groups.size(); |
511 |
|
while (--i >= 0) |
512 |
|
((ThreadGroup) groups.get(i)).destroy(); |
513 |
|
groups = null; |
514 |
if (parent != null) |
if (parent != null) |
515 |
parent.removeGroup(this); |
parent.removeGroup(this); |
|
parent = null; |
|
|
|
|
|
for (int i=0; i < groups.size(); i++) |
|
|
{ |
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
|
|
tg.destroy(); |
|
|
} |
|
516 |
} |
} |
517 |
|
|
518 |
/** Print out information about this ThreadGroup to System.out. |
/** |
519 |
*/ |
* Print out information about this ThreadGroup to System.out. This is |
520 |
|
* meant for debugging purposes. <b>WARNING:</b> This method is not secure, |
521 |
|
* and can print the name of threads to standard out even when you cannot |
522 |
|
* otherwise get at such threads. |
523 |
|
*/ |
524 |
public void list() |
public void list() |
525 |
{ |
{ |
526 |
list(""); |
list(""); |
527 |
} |
} |
528 |
|
|
529 |
private synchronized void list(String indentation) |
/** |
530 |
{ |
* When a Thread in this ThreadGroup does not catch an exception, the |
531 |
System.out.print(indentation); |
* virtual machine calls this method. The default implementation simply |
532 |
System.out.println(toString ()); |
* passes the call to the parent; then in top ThreadGroup, it will |
533 |
String sub = indentation + " "; |
* ignore ThreadDeath and print the stack trace of any other throwable. |
534 |
for (int i=0; i < threads.size(); i++) |
* Override this method if you want to handle the exception in a different |
535 |
{ |
* manner. |
536 |
Thread t = (Thread) threads.elementAt(i); |
* |
537 |
System.out.print(sub); |
* @param thread the thread that exited |
538 |
System.out.println(t.toString()); |
* @param exception the uncaught exception |
539 |
} |
* @throws NullPointerException if t is null |
540 |
for (int i=0; i < groups.size(); i++) |
* @see ThreadDeath |
541 |
{ |
* @see System#err |
542 |
ThreadGroup tg = (ThreadGroup) groups.elementAt(i); |
* @see Throwable#printStackTrace() |
543 |
tg.list(sub); |
*/ |
|
} |
|
|
} |
|
|
|
|
|
/** When a Thread in this ThreadGroup does not catch an exception, |
|
|
* this method of the ThreadGroup is called.<P> |
|
|
* |
|
|
* ThreadGroup's implementation does the following:<BR> |
|
|
* <OL> |
|
|
* <LI>If there is a parent ThreadGroup, call uncaughtException() |
|
|
* in the parent.</LI> |
|
|
* <LI>If the Throwable passed is a ThreadDeath, don't do |
|
|
* anything.</LI> |
|
|
* <LI>Otherwise, call <CODE>exception.printStackTrace().</CODE></LI> |
|
|
* </OL> |
|
|
* |
|
|
* @param thread the thread that exited. |
|
|
* @param exception the uncaught exception. |
|
|
*/ |
|
544 |
public void uncaughtException(Thread thread, Throwable t) |
public void uncaughtException(Thread thread, Throwable t) |
545 |
{ |
{ |
546 |
if (parent != null) |
if (parent != null) |
547 |
parent.uncaughtException (thread, t); |
parent.uncaughtException(thread, t); |
548 |
else if (! (t instanceof ThreadDeath)) |
else if (! (t instanceof ThreadDeath)) |
549 |
{ |
{ |
550 |
if (thread != null) |
if (t == null) |
551 |
System.err.print ("Exception in thread \"" |
throw new NullPointerException(); |
552 |
+ thread.getName() + "\" "); |
had_uncaught_exception = true; |
553 |
try |
try |
554 |
{ |
{ |
555 |
t.printStackTrace(System.err); |
if (thread != null) |
556 |
} |
System.err.print("Exception in thread \"" + thread.name + "\" "); |
557 |
catch (Throwable x) |
t.printStackTrace(System.err); |
558 |
{ |
} |
559 |
// This means that something is badly screwed up with the runtime, |
catch (Throwable x) |
560 |
// or perhaps someone is messing with the SecurityManager. In any |
{ |
561 |
// case, try to deal with it gracefully. |
// This means that something is badly screwed up with the runtime, |
562 |
System.err.println(t); |
// or perhaps someone overloaded the Throwable.printStackTrace to |
563 |
System.err.println("*** Got " + x.toString() + |
// die. In any case, try to deal with it gracefully. |
564 |
" while trying to print stack trace"); |
try |
565 |
} |
{ |
566 |
had_uncaught_exception = true; |
System.err.println(t); |
567 |
|
System.err.println("*** Got " + x |
568 |
|
+ " while trying to print stack trace."); |
569 |
|
} |
570 |
|
catch (Throwable x2) |
571 |
|
{ |
572 |
|
// Here, someone may have overloaded t.toString() or |
573 |
|
// x.toString() to die. Give up all hope; we can't even chain |
574 |
|
// the exception, because the chain would likewise die. |
575 |
|
System.err.println("*** Catastrophic failure while handling " |
576 |
|
+ "uncaught exception."); |
577 |
|
throw new InternalError(); |
578 |
|
} |
579 |
|
} |
580 |
} |
} |
581 |
} |
} |
582 |
|
|
583 |
/** Tell the VM whether it may suspend Threads in low memory |
/** |
584 |
* situations. |
* Originally intended to tell the VM whether it may suspend Threads in |
585 |
* @deprecated This method is unimplemented, because it would rely on |
* low memory situations, this method was never implemented by Sun, and |
586 |
* suspend(), which is deprecated. There is no way for a Java |
* is hence a no-op. |
587 |
* program to determine whether this has any effect whatsoever, |
* |
588 |
* so we don't need it. |
* @param allow whether to allow low-memory thread suspension; ignored |
589 |
* @return false |
* @return false |
590 |
*/ |
* @since 1.1 |
591 |
|
* @deprecated pointless, since suspend is deprecated |
592 |
|
*/ |
593 |
public boolean allowThreadSuspension(boolean allow) |
public boolean allowThreadSuspension(boolean allow) |
594 |
{ |
{ |
595 |
return false; |
return false; |
596 |
} |
} |
597 |
|
|
598 |
/** Get a human-readable representation of this ThreadGroup. |
/** |
599 |
* @return a String representing this ThreadGroup. |
* Return a human-readable String representing this ThreadGroup. The format |
600 |
* @specnote Language Spec and Class Libraries book disagree a bit here. |
* of the string is:<br> |
601 |
* We follow the Spec, but add "ThreadGroup" per the book. We |
* <code>getClass().getName() + "[name=" + getName() + ",maxpri=" |
602 |
* include "java.lang" based on the list() example in the Class |
* + getMaxPriority() + ']'</code>. |
603 |
* Libraries book. |
* |
604 |
*/ |
* @return a human-readable String representing this ThreadGroup |
605 |
public String toString () |
*/ |
606 |
{ |
public String toString() |
|
return "java.lang.ThreadGroup[name=" + name + |
|
|
",maxpri=" + maxpri + "]"; |
|
|
} |
|
|
|
|
|
/** Find out if the current Thread can modify this ThreadGroup. |
|
|
* Calls the current SecurityManager's checkAccess() method to |
|
|
* find out. If there is none, it assumes everything's OK. |
|
|
* @exception SecurityException if the current Thread cannot |
|
|
* modify this ThreadGroup. |
|
|
*/ |
|
|
public final void checkAccess() |
|
607 |
{ |
{ |
608 |
SecurityManager sm = System.getSecurityManager(); |
return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']'; |
|
if (sm != null) |
|
|
sm.checkAccess(this); |
|
609 |
} |
} |
610 |
|
|
611 |
// This is called to add a Thread to our internal list. |
/** |
612 |
|
* Implements enumerate. |
613 |
|
* |
614 |
|
* @param list the array to put the threads into |
615 |
|
* @param next the next open slot in the array |
616 |
|
* @param recurse whether to recurse into descendent ThreadGroups |
617 |
|
* @return the number of threads put into the array |
618 |
|
* @throws SecurityException if permission was denied |
619 |
|
* @throws NullPointerException if list is null |
620 |
|
* @throws ArrayStoreException if a thread does not fit in the array |
621 |
|
* @see #enumerate(Thread[]) |
622 |
|
* @see #enumerate(Thread[], boolean) |
623 |
|
*/ |
624 |
|
private int enumerate(Thread[] list, int next, boolean recurse) |
625 |
|
{ |
626 |
|
checkAccess(); |
627 |
|
if (groups == null) |
628 |
|
return next; |
629 |
|
int i = threads.size(); |
630 |
|
while (--i >= 0 && next < list.length) |
631 |
|
{ |
632 |
|
Thread t = (Thread) threads.get(i); |
633 |
|
if (t.isAlive()) |
634 |
|
list[next++] = t; |
635 |
|
} |
636 |
|
if (recurse) |
637 |
|
{ |
638 |
|
i = groups.size(); |
639 |
|
while (--i >= 0 && next < list.length) |
640 |
|
{ |
641 |
|
ThreadGroup g = (ThreadGroup) threads.get(i); |
642 |
|
next = g.enumerate(list, next, true); |
643 |
|
} |
644 |
|
} |
645 |
|
return next; |
646 |
|
} |
647 |
|
|
648 |
|
/** |
649 |
|
* Implements enumerate. |
650 |
|
* |
651 |
|
* @param list the array to put the groups into |
652 |
|
* @param next the next open slot in the array |
653 |
|
* @param recurse whether to recurse into descendent ThreadGroups |
654 |
|
* @return the number of groups put into the array |
655 |
|
* @throws SecurityException if permission was denied |
656 |
|
* @throws NullPointerException if list is null |
657 |
|
* @throws ArrayStoreException if a group does not fit in the array |
658 |
|
* @see #enumerate(ThreadGroup[]) |
659 |
|
* @see #enumerate(ThreadGroup[], boolean) |
660 |
|
*/ |
661 |
|
private int enumerate(ThreadGroup[] list, int next, boolean recurse) |
662 |
|
{ |
663 |
|
checkAccess(); |
664 |
|
if (groups == null) |
665 |
|
return next; |
666 |
|
int i = groups.size(); |
667 |
|
while (--i >= 0 && next < list.length) |
668 |
|
{ |
669 |
|
ThreadGroup g = (ThreadGroup) groups.get(i); |
670 |
|
list[next++] = g; |
671 |
|
if (recurse && next != list.length) |
672 |
|
next = g.enumerate(list, next, true); |
673 |
|
} |
674 |
|
return next; |
675 |
|
} |
676 |
|
|
677 |
|
/** |
678 |
|
* Implements list. |
679 |
|
* |
680 |
|
* @param indentation the current level of indentation |
681 |
|
* @see #list() |
682 |
|
*/ |
683 |
|
private void list(String indentation) |
684 |
|
{ |
685 |
|
if (groups == null) |
686 |
|
return; |
687 |
|
System.out.print(indentation + this); |
688 |
|
indentation += " "; |
689 |
|
int i = threads.size(); |
690 |
|
while (--i >= 0) |
691 |
|
System.out.println(indentation + threads.get(i)); |
692 |
|
i = groups.size(); |
693 |
|
while (--i >= 0) |
694 |
|
((ThreadGroup) groups.get(i)).list(indentation); |
695 |
|
} |
696 |
|
|
697 |
|
/** |
698 |
|
* Add a thread to the group. Called by Thread constructors. |
699 |
|
* |
700 |
|
* @param t the thread to add, non-null |
701 |
|
* @throws IllegalThreadStateException if the group is destroyed |
702 |
|
*/ |
703 |
final synchronized void addThread(Thread t) |
final synchronized void addThread(Thread t) |
704 |
{ |
{ |
705 |
if (isDestroyed()) |
if (groups == null) |
706 |
throw new IllegalThreadStateException ("ThreadGroup is destroyed"); |
throw new IllegalThreadStateException("ThreadGroup is destroyed"); |
707 |
|
threads.add(t); |
|
threads.addElement(t); |
|
708 |
} |
} |
709 |
|
|
710 |
// This is called to remove a Thread from our internal list. |
/** |
711 |
|
* Called by the VM to remove a thread that has died. |
712 |
|
* |
713 |
|
* @param t the thread to remove, non-null |
714 |
|
* @XXX A ThreadListener to call this might be nice. |
715 |
|
*/ |
716 |
final synchronized void removeThread(Thread t) |
final synchronized void removeThread(Thread t) |
717 |
{ |
{ |
718 |
if (isDestroyed()) |
if (groups == null) |
719 |
throw new IllegalThreadStateException (); |
return; |
720 |
|
threads.remove(t); |
|
threads.removeElement(t); |
|
721 |
// Daemon groups are automatically destroyed when all their threads die. |
// Daemon groups are automatically destroyed when all their threads die. |
722 |
if (daemon_flag && groups.size() == 0 && threads.size() == 0) |
if (daemon_flag && groups.size() == 0 && threads.size() == 0) |
723 |
{ |
{ |
724 |
// We inline destroy to avoid the access check. |
// We inline destroy to avoid the access check. |
725 |
if (parent != null) |
groups = null; |
726 |
parent.removeGroup(this); |
if (parent != null) |
727 |
parent = null; |
parent.removeGroup(this); |
728 |
} |
} |
729 |
} |
} |
730 |
|
|
731 |
// This is called to add a ThreadGroup to our internal list. |
/** |
732 |
final synchronized void addGroup(ThreadGroup g) |
* Called when a group is destroyed, to remove it from its parent. |
733 |
{ |
* |
734 |
groups.addElement(g); |
* @param g the destroyed group, non-null |
735 |
} |
*/ |
|
|
|
|
// This is called to remove a ThreadGroup from our internal list. |
|
736 |
final synchronized void removeGroup(ThreadGroup g) |
final synchronized void removeGroup(ThreadGroup g) |
737 |
{ |
{ |
738 |
groups.removeElement(g); |
groups.remove(g); |
739 |
// Daemon groups are automatically destroyed when all their threads die. |
// Daemon groups are automatically destroyed when all their threads die. |
740 |
if (daemon_flag && groups.size() == 0 && threads.size() == 0) |
if (daemon_flag && groups.size() == 0 && threads.size() == 0) |
741 |
{ |
{ |
742 |
// We inline destroy to avoid the access check. |
// We inline destroy to avoid the access check. |
743 |
if (parent != null) |
groups = null; |
744 |
parent.removeGroup(this); |
if (parent != null) |
745 |
parent = null; |
parent.removeGroup(this); |
746 |
} |
} |
747 |
} |
} |
748 |
} |
} // class ThreadGroup |