750 lines
23 KiB
Java
750 lines
23 KiB
Java
/* ThreadGroup -- a group of Threads
|
|
Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
package java.lang;
|
|
|
|
import java.util.Vector;
|
|
|
|
/**
|
|
* ThreadGroup allows you to group Threads together. There is a hierarchy
|
|
* of ThreadGroups, and only the initial ThreadGroup has no parent. A Thread
|
|
* may access information about its own ThreadGroup, but not its parents or
|
|
* others outside the tree.
|
|
*
|
|
* @author John Keiser
|
|
* @author Tom Tromey
|
|
* @author Bryce McKinlay
|
|
* @author Eric Blake <ebb9@email.byu.edu>
|
|
* @see Thread
|
|
* @since 1.0
|
|
* @status updated to 1.4
|
|
*/
|
|
public class ThreadGroup
|
|
{
|
|
/** The Initial, top-level ThreadGroup. */
|
|
static ThreadGroup root = new ThreadGroup();
|
|
|
|
/**
|
|
* This flag is set if an uncaught exception occurs. The runtime should
|
|
* check this and exit with an error status if it is set.
|
|
*/
|
|
static boolean had_uncaught_exception;
|
|
|
|
/** The parent thread group. */
|
|
private final ThreadGroup parent;
|
|
|
|
/** The group name, non-null. */
|
|
final String name;
|
|
|
|
/** The threads in the group. */
|
|
private final Vector threads = new Vector();
|
|
|
|
/** Child thread groups, or null when this group is destroyed. */
|
|
private Vector groups = new Vector();
|
|
|
|
/** If all threads in the group are daemons. */
|
|
private boolean daemon_flag = false;
|
|
|
|
/** The maximum group priority. */
|
|
private int maxpri;
|
|
|
|
/**
|
|
* Hidden constructor to build the root node.
|
|
*/
|
|
private ThreadGroup()
|
|
{
|
|
name = "main";
|
|
parent = null;
|
|
maxpri = Thread.MAX_PRIORITY;
|
|
}
|
|
|
|
/**
|
|
* Create a new ThreadGroup using the given name and the current thread's
|
|
* ThreadGroup as a parent. There may be a security check,
|
|
* <code>checkAccess</code>.
|
|
*
|
|
* @param name the name to use for the ThreadGroup
|
|
* @throws SecurityException if the current thread cannot create a group
|
|
* @see #checkAccess()
|
|
*/
|
|
public ThreadGroup(String name)
|
|
{
|
|
this(Thread.currentThread().group, name);
|
|
}
|
|
|
|
/**
|
|
* Create a new ThreadGroup using the given name and parent group. The new
|
|
* group inherits the maximum priority and daemon status of its parent
|
|
* group. There may be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @param name the name to use for the ThreadGroup
|
|
* @param parent the ThreadGroup to use as a parent
|
|
* @throws NullPointerException if parent is null
|
|
* @throws SecurityException if the current thread cannot create a group
|
|
* @throws IllegalThreadStateException if the parent is destroyed
|
|
* @see #checkAccess()
|
|
*/
|
|
public ThreadGroup(ThreadGroup parent, String name)
|
|
{
|
|
parent.checkAccess();
|
|
this.parent = parent;
|
|
this.name = name;
|
|
maxpri = parent.maxpri;
|
|
daemon_flag = parent.daemon_flag;
|
|
synchronized (parent)
|
|
{
|
|
if (parent.groups == null)
|
|
throw new IllegalThreadStateException();
|
|
parent.groups.add(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the name of this ThreadGroup.
|
|
*
|
|
* @return the name of this ThreadGroup
|
|
*/
|
|
public final String getName()
|
|
{
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Get the parent of this ThreadGroup. If the parent is not null, there
|
|
* may be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @return the parent of this ThreadGroup
|
|
* @throws SecurityException if permission is denied
|
|
*/
|
|
public final ThreadGroup getParent()
|
|
{
|
|
if (parent != null)
|
|
parent.checkAccess();
|
|
return parent;
|
|
}
|
|
|
|
/**
|
|
* Get the maximum priority of Threads in this ThreadGroup. Threads created
|
|
* after this call in this group may not exceed this priority.
|
|
*
|
|
* @return the maximum priority of Threads in this ThreadGroup
|
|
*/
|
|
public final int getMaxPriority()
|
|
{
|
|
return maxpri;
|
|
}
|
|
|
|
/**
|
|
* Tell whether this ThreadGroup is a daemon group. A daemon group will
|
|
* be automatically destroyed when its last thread is stopped and
|
|
* its last thread group is destroyed.
|
|
*
|
|
* @return whether this ThreadGroup is a daemon group
|
|
*/
|
|
public final boolean isDaemon()
|
|
{
|
|
return daemon_flag;
|
|
}
|
|
|
|
/**
|
|
* Tell whether this ThreadGroup has been destroyed or not.
|
|
*
|
|
* @return whether this ThreadGroup has been destroyed or not
|
|
* @since 1.1
|
|
*/
|
|
public synchronized boolean isDestroyed()
|
|
{
|
|
return groups == null;
|
|
}
|
|
|
|
/**
|
|
* Set 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. There may be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @param daemon whether this ThreadGroup should be a daemon group
|
|
* @throws SecurityException if you cannot modify this ThreadGroup
|
|
* @see #checkAccess()
|
|
*/
|
|
public final void setDaemon(boolean daemon)
|
|
{
|
|
checkAccess();
|
|
daemon_flag = daemon;
|
|
}
|
|
|
|
/**
|
|
* Set the maximum priority for Threads in this ThreadGroup. setMaxPriority
|
|
* can only be used to reduce the current maximum. If maxpri is greater
|
|
* than the current Maximum of the parent group, the current value is not
|
|
* changed. Otherwise, all groups which belong to this have their priority
|
|
* adjusted as well. Calling this does not affect threads already in this
|
|
* ThreadGroup. There may be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @param maxpri the new maximum priority for this ThreadGroup
|
|
* @throws SecurityException if you cannot modify this ThreadGroup
|
|
* @see #getMaxPriority()
|
|
* @see #checkAccess()
|
|
*/
|
|
public final synchronized void setMaxPriority(int maxpri)
|
|
{
|
|
checkAccess();
|
|
if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY)
|
|
return;
|
|
if (parent != null && maxpri > parent.maxpri)
|
|
maxpri = parent.maxpri;
|
|
this.maxpri = maxpri;
|
|
if (groups == null)
|
|
return;
|
|
int i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).setMaxPriority(maxpri);
|
|
}
|
|
|
|
/**
|
|
* Check whether this ThreadGroup is an ancestor of the specified
|
|
* ThreadGroup, or if they are the same.
|
|
*
|
|
* @param group the group to test on
|
|
* @return whether this ThreadGroup is a parent of the specified group
|
|
*/
|
|
public final boolean parentOf(ThreadGroup group)
|
|
{
|
|
while (group != null)
|
|
{
|
|
if (group == this)
|
|
return true;
|
|
group = group.parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Find out if the current Thread can modify this ThreadGroup. This passes
|
|
* the check on to <code>SecurityManager.checkAccess(this)</code>.
|
|
*
|
|
* @throws SecurityException if the current Thread cannot modify this
|
|
* ThreadGroup
|
|
* @see SecurityManager#checkAccess(ThreadGroup)
|
|
*/
|
|
public final void checkAccess()
|
|
{
|
|
// Bypass System.getSecurityManager, for bootstrap efficiency.
|
|
SecurityManager sm = Runtime.securityManager;
|
|
if (sm != null)
|
|
sm.checkAccess(this);
|
|
}
|
|
|
|
/**
|
|
* Return an estimate of the total number of active threads in this
|
|
* ThreadGroup and all its descendants. This cannot return an exact number,
|
|
* since the status of threads may change after they were counted; but it
|
|
* should be pretty close. Based on a JDC bug,
|
|
* <a href="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html">
|
|
* 4089701</a>, we take active to mean isAlive().
|
|
*
|
|
* @return count of active threads in this ThreadGroup and its descendants
|
|
*/
|
|
public int activeCount()
|
|
{
|
|
int total = 0;
|
|
if (groups == null)
|
|
return total;
|
|
int i = threads.size();
|
|
while (--i >= 0)
|
|
if (((Thread) threads.get(i)).isAlive())
|
|
total++;
|
|
i = groups.size();
|
|
while (--i >= 0)
|
|
total += ((ThreadGroup) groups.get(i)).activeCount();
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* Copy all of the active Threads from this ThreadGroup and its descendants
|
|
* into the specified array. If the array is not big enough to hold all
|
|
* the Threads, extra Threads will simply not be copied. There may be a
|
|
* security check, <code>checkAccess</code>.
|
|
*
|
|
* @param array the array to put the threads into
|
|
* @return the number of threads put into the array
|
|
* @throws SecurityException if permission was denied
|
|
* @throws NullPointerException if array is null
|
|
* @throws ArrayStoreException if a thread does not fit in the array
|
|
* @see #activeCount()
|
|
* @see #checkAccess()
|
|
* @see #enumerate(Thread[], boolean)
|
|
*/
|
|
public int enumerate(Thread[] array)
|
|
{
|
|
return enumerate(array, 0, true);
|
|
}
|
|
|
|
/**
|
|
* Copy all of the active Threads from this ThreadGroup and, if desired,
|
|
* from its descendants, into the specified array. If the array is not big
|
|
* enough to hold all the Threads, extra Threads will simply not be copied.
|
|
* There may be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @param array the array to put the threads into
|
|
* @param recurse whether to recurse into descendent ThreadGroups
|
|
* @return the number of threads put into the array
|
|
* @throws SecurityException if permission was denied
|
|
* @throws NullPointerException if array is null
|
|
* @throws ArrayStoreException if a thread does not fit in the array
|
|
* @see #activeCount()
|
|
* @see #checkAccess()
|
|
*/
|
|
public int enumerate(Thread[] array, boolean recurse)
|
|
{
|
|
return enumerate(array, 0, recurse);
|
|
}
|
|
|
|
/**
|
|
* Get the number of active groups in this ThreadGroup. This group itself
|
|
* is not included in the count. A sub-group is active if it has not been
|
|
* destroyed. This cannot return an exact number, since the status of
|
|
* threads may change after they were counted; but it should be pretty close.
|
|
*
|
|
* @return the number of active groups in this ThreadGroup
|
|
*/
|
|
public int activeGroupCount()
|
|
{
|
|
if (groups == null)
|
|
return 0;
|
|
int total = groups.size();
|
|
int i = total;
|
|
while (--i >= 0)
|
|
total += ((ThreadGroup) groups.get(i)).activeGroupCount();
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* Copy all active ThreadGroups that are descendants of this ThreadGroup
|
|
* into the specified array. If the array is not large enough to hold all
|
|
* active ThreadGroups, extra ThreadGroups simply will not be copied. There
|
|
* may be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @param array the array to put the ThreadGroups into
|
|
* @return the number of ThreadGroups copied into the array
|
|
* @throws SecurityException if permission was denied
|
|
* @throws NullPointerException if array is null
|
|
* @throws ArrayStoreException if a group does not fit in the array
|
|
* @see #activeCount()
|
|
* @see #checkAccess()
|
|
* @see #enumerate(ThreadGroup[], boolean)
|
|
*/
|
|
public int enumerate(ThreadGroup[] array)
|
|
{
|
|
return enumerate(array, 0, true);
|
|
}
|
|
|
|
/**
|
|
* Copy all active ThreadGroups that are children of this ThreadGroup into
|
|
* the specified array, and if desired, also all descendents. If the array
|
|
* is not large enough to hold all active ThreadGroups, extra ThreadGroups
|
|
* simply will not be copied. There may be a security check,
|
|
* <code>checkAccess</code>.
|
|
*
|
|
* @param array the array to put the ThreadGroups into
|
|
* @param recurse whether to recurse into descendent ThreadGroups
|
|
* @return the number of ThreadGroups copied into the array
|
|
* @throws SecurityException if permission was denied
|
|
* @throws NullPointerException if array is null
|
|
* @throws ArrayStoreException if a group does not fit in the array
|
|
* @see #activeCount()
|
|
* @see #checkAccess()
|
|
*/
|
|
public int enumerate(ThreadGroup[] array, boolean recurse)
|
|
{
|
|
return enumerate(array, 0, recurse);
|
|
}
|
|
|
|
/**
|
|
* Stop all Threads in this ThreadGroup and its descendants.
|
|
*
|
|
* <p>This is inherently unsafe, as it can interrupt synchronized blocks and
|
|
* leave data in bad states. Hence, there is a security check:
|
|
* <code>checkAccess()</code>, followed by further checks on each thread
|
|
* being stopped.
|
|
*
|
|
* @throws SecurityException if permission is denied
|
|
* @see #checkAccess()
|
|
* @see Thread#stop(Throwable)
|
|
* @deprecated unsafe operation, try not to use
|
|
*/
|
|
public final synchronized void stop()
|
|
{
|
|
checkAccess();
|
|
if (groups == null)
|
|
return;
|
|
int i = threads.size();
|
|
while (--i >= 0)
|
|
((Thread) threads.get(i)).stop();
|
|
i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).stop();
|
|
}
|
|
|
|
/**
|
|
* Interrupt all Threads in this ThreadGroup and its sub-groups. There may
|
|
* be a security check, <code>checkAccess</code>.
|
|
*
|
|
* @throws SecurityException if permission is denied
|
|
* @see #checkAccess()
|
|
* @see Thread#interrupt()
|
|
* @since 1.2
|
|
*/
|
|
public final synchronized void interrupt()
|
|
{
|
|
checkAccess();
|
|
if (groups == null)
|
|
return;
|
|
int i = threads.size();
|
|
while (--i >= 0)
|
|
((Thread) threads.get(i)).interrupt();
|
|
i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).interrupt();
|
|
}
|
|
|
|
/**
|
|
* Suspend all Threads in this ThreadGroup and its descendants.
|
|
*
|
|
* <p>This is inherently unsafe, as suspended threads still hold locks,
|
|
* which can lead to deadlock. Hence, there is a security check:
|
|
* <code>checkAccess()</code>, followed by further checks on each thread
|
|
* being suspended.
|
|
*
|
|
* @throws SecurityException if permission is denied
|
|
* @see #checkAccess()
|
|
* @see Thread#suspend()
|
|
* @deprecated unsafe operation, try not to use
|
|
*/
|
|
public final synchronized void suspend()
|
|
{
|
|
checkAccess();
|
|
if (groups == null)
|
|
return;
|
|
int i = threads.size();
|
|
while (--i >= 0)
|
|
((Thread) threads.get(i)).suspend();
|
|
i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).suspend();
|
|
}
|
|
|
|
/**
|
|
* Resume all suspended Threads in this ThreadGroup and its descendants.
|
|
* To mirror suspend(), there is a security check:
|
|
* <code>checkAccess()</code>, followed by further checks on each thread
|
|
* being resumed.
|
|
*
|
|
* @throws SecurityException if permission is denied
|
|
* @see #checkAccess()
|
|
* @see Thread#suspend()
|
|
* @deprecated pointless, since suspend is deprecated
|
|
*/
|
|
public final synchronized void resume()
|
|
{
|
|
checkAccess();
|
|
if (groups == null)
|
|
return;
|
|
int i = threads.size();
|
|
while (--i >= 0)
|
|
((Thread) threads.get(i)).resume();
|
|
i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).resume();
|
|
}
|
|
|
|
/**
|
|
* Destroy this ThreadGroup. The group must be empty, meaning that all
|
|
* threads and sub-groups have completed execution. Daemon groups are
|
|
* destroyed automatically. There may be a security check,
|
|
* <code>checkAccess</code>.
|
|
*
|
|
* @throws IllegalThreadStateException if the ThreadGroup is not empty, or
|
|
* was previously destroyed
|
|
* @throws SecurityException if permission is denied
|
|
* @see #checkAccess()
|
|
*/
|
|
public final synchronized void destroy()
|
|
{
|
|
checkAccess();
|
|
if (! threads.isEmpty() || groups == null)
|
|
throw new IllegalThreadStateException();
|
|
int i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).destroy();
|
|
groups = null;
|
|
if (parent != null)
|
|
parent.removeGroup(this);
|
|
}
|
|
|
|
/**
|
|
* Print out information about this ThreadGroup to System.out. This is
|
|
* meant for debugging purposes. <b>WARNING:</b> This method is not secure,
|
|
* and can print the name of threads to standard out even when you cannot
|
|
* otherwise get at such threads.
|
|
*/
|
|
public void list()
|
|
{
|
|
list("");
|
|
}
|
|
|
|
/**
|
|
* When a Thread in this ThreadGroup does not catch an exception, the
|
|
* virtual machine calls this method. The default implementation simply
|
|
* passes the call to the parent; then in top ThreadGroup, it will
|
|
* ignore ThreadDeath and print the stack trace of any other throwable.
|
|
* Override this method if you want to handle the exception in a different
|
|
* manner.
|
|
*
|
|
* @param thread the thread that exited
|
|
* @param t the uncaught throwable
|
|
* @throws NullPointerException if t is null
|
|
* @see ThreadDeath
|
|
* @see System#err
|
|
* @see Throwable#printStackTrace()
|
|
*/
|
|
public void uncaughtException(Thread thread, Throwable t)
|
|
{
|
|
if (parent != null)
|
|
parent.uncaughtException(thread, t);
|
|
else if (! (t instanceof ThreadDeath))
|
|
{
|
|
if (t == null)
|
|
throw new NullPointerException();
|
|
had_uncaught_exception = true;
|
|
try
|
|
{
|
|
if (thread != null)
|
|
System.err.print("Exception in thread \"" + thread.name + "\" ");
|
|
t.printStackTrace(System.err);
|
|
}
|
|
catch (Throwable x)
|
|
{
|
|
// This means that something is badly screwed up with the runtime,
|
|
// or perhaps someone overloaded the Throwable.printStackTrace to
|
|
// die. In any case, try to deal with it gracefully.
|
|
try
|
|
{
|
|
System.err.println(t);
|
|
System.err.println("*** Got " + x
|
|
+ " while trying to print stack trace.");
|
|
}
|
|
catch (Throwable x2)
|
|
{
|
|
// Here, someone may have overloaded t.toString() or
|
|
// x.toString() to die. Give up all hope; we can't even chain
|
|
// the exception, because the chain would likewise die.
|
|
System.err.println("*** Catastrophic failure while handling "
|
|
+ "uncaught exception.");
|
|
throw new InternalError();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Originally intended to tell the VM whether it may suspend Threads in
|
|
* low memory situations, this method was never implemented by Sun, and
|
|
* is hence a no-op.
|
|
*
|
|
* @param allow whether to allow low-memory thread suspension; ignored
|
|
* @return false
|
|
* @since 1.1
|
|
* @deprecated pointless, since suspend is deprecated
|
|
*/
|
|
public boolean allowThreadSuspension(boolean allow)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return a human-readable String representing this ThreadGroup. The format
|
|
* of the string is:<br>
|
|
* <code>getClass().getName() + "[name=" + getName() + ",maxpri="
|
|
* + getMaxPriority() + ']'</code>.
|
|
*
|
|
* @return a human-readable String representing this ThreadGroup
|
|
*/
|
|
public String toString()
|
|
{
|
|
return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']';
|
|
}
|
|
|
|
/**
|
|
* Implements enumerate.
|
|
*
|
|
* @param list the array to put the threads into
|
|
* @param next the next open slot in the array
|
|
* @param recurse whether to recurse into descendent ThreadGroups
|
|
* @return the number of threads put into the array
|
|
* @throws SecurityException if permission was denied
|
|
* @throws NullPointerException if list is null
|
|
* @throws ArrayStoreException if a thread does not fit in the array
|
|
* @see #enumerate(Thread[])
|
|
* @see #enumerate(Thread[], boolean)
|
|
*/
|
|
private int enumerate(Thread[] list, int next, boolean recurse)
|
|
{
|
|
checkAccess();
|
|
if (groups == null)
|
|
return next;
|
|
int i = threads.size();
|
|
while (--i >= 0 && next < list.length)
|
|
{
|
|
Thread t = (Thread) threads.get(i);
|
|
if (t.isAlive())
|
|
list[next++] = t;
|
|
}
|
|
if (recurse)
|
|
{
|
|
i = groups.size();
|
|
while (--i >= 0 && next < list.length)
|
|
{
|
|
ThreadGroup g = (ThreadGroup) groups.get(i);
|
|
next = g.enumerate(list, next, true);
|
|
}
|
|
}
|
|
return next;
|
|
}
|
|
|
|
/**
|
|
* Implements enumerate.
|
|
*
|
|
* @param list the array to put the groups into
|
|
* @param next the next open slot in the array
|
|
* @param recurse whether to recurse into descendent ThreadGroups
|
|
* @return the number of groups put into the array
|
|
* @throws SecurityException if permission was denied
|
|
* @throws NullPointerException if list is null
|
|
* @throws ArrayStoreException if a group does not fit in the array
|
|
* @see #enumerate(ThreadGroup[])
|
|
* @see #enumerate(ThreadGroup[], boolean)
|
|
*/
|
|
private int enumerate(ThreadGroup[] list, int next, boolean recurse)
|
|
{
|
|
checkAccess();
|
|
if (groups == null)
|
|
return next;
|
|
int i = groups.size();
|
|
while (--i >= 0 && next < list.length)
|
|
{
|
|
ThreadGroup g = (ThreadGroup) groups.get(i);
|
|
list[next++] = g;
|
|
if (recurse && next != list.length)
|
|
next = g.enumerate(list, next, true);
|
|
}
|
|
return next;
|
|
}
|
|
|
|
/**
|
|
* Implements list.
|
|
*
|
|
* @param indentation the current level of indentation
|
|
* @see #list()
|
|
*/
|
|
private void list(String indentation)
|
|
{
|
|
if (groups == null)
|
|
return;
|
|
System.out.println(indentation + this);
|
|
indentation += " ";
|
|
int i = threads.size();
|
|
while (--i >= 0)
|
|
System.out.println(indentation + threads.get(i));
|
|
i = groups.size();
|
|
while (--i >= 0)
|
|
((ThreadGroup) groups.get(i)).list(indentation);
|
|
}
|
|
|
|
/**
|
|
* Add a thread to the group. Called by Thread constructors.
|
|
*
|
|
* @param t the thread to add, non-null
|
|
* @throws IllegalThreadStateException if the group is destroyed
|
|
*/
|
|
final synchronized void addThread(Thread t)
|
|
{
|
|
if (groups == null)
|
|
throw new IllegalThreadStateException("ThreadGroup is destroyed");
|
|
threads.add(t);
|
|
}
|
|
|
|
/**
|
|
* Called by the VM to remove a thread that has died.
|
|
*
|
|
* @param t the thread to remove, non-null
|
|
* @XXX A ThreadListener to call this might be nice.
|
|
*/
|
|
final synchronized void removeThread(Thread t)
|
|
{
|
|
if (groups == null)
|
|
return;
|
|
threads.remove(t);
|
|
t.group = null;
|
|
// Daemon groups are automatically destroyed when all their threads die.
|
|
if (daemon_flag && groups.size() == 0 && threads.size() == 0)
|
|
{
|
|
// We inline destroy to avoid the access check.
|
|
groups = null;
|
|
if (parent != null)
|
|
parent.removeGroup(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when a group is destroyed, to remove it from its parent.
|
|
*
|
|
* @param g the destroyed group, non-null
|
|
*/
|
|
final synchronized void removeGroup(ThreadGroup g)
|
|
{
|
|
groups.remove(g);
|
|
// Daemon groups are automatically destroyed when all their threads die.
|
|
if (daemon_flag && groups.size() == 0 && threads.size() == 0)
|
|
{
|
|
// We inline destroy to avoid the access check.
|
|
groups = null;
|
|
if (parent != null)
|
|
parent.removeGroup(this);
|
|
}
|
|
}
|
|
} // class ThreadGroup
|