ce2ab205ae
* java/lang/ThreadGroup.java (uncaughtException): Print thread name with stack dump. From-SVN: r37047
595 lines
19 KiB
Java
595 lines
19 KiB
Java
/* java.lang.ThreadGroup
|
|
Copyright (C) 1998, 2000 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.
|
|
|
|
As a special exception, if you link this library with other files to
|
|
produce an executable, this library does not by itself cause the
|
|
resulting executable to be covered by the GNU General Public License.
|
|
This exception does not however invalidate any other reasons why the
|
|
executable file might be covered by the GNU General Public License. */
|
|
|
|
package java.lang;
|
|
|
|
import java.util.Vector;
|
|
import java.util.Enumeration;
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
/**
|
|
* 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
|
|
* @version 1.2.0
|
|
* @since JDK1.0
|
|
*/
|
|
|
|
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 = false;
|
|
|
|
private ThreadGroup parent;
|
|
private String name;
|
|
private Vector threads = new Vector();
|
|
private Vector groups = new Vector();
|
|
private boolean daemon_flag = false;
|
|
private int maxpri = Thread.MAX_PRIORITY;
|
|
|
|
private ThreadGroup()
|
|
{
|
|
name = "main";
|
|
}
|
|
|
|
/** Create a new ThreadGroup using the given name and the
|
|
* current thread's ThreadGroup as a parent.
|
|
* @param name the name to use for the ThreadGroup.
|
|
*/
|
|
public ThreadGroup(String name)
|
|
{
|
|
this (Thread.currentThread().getThreadGroup(), name);
|
|
}
|
|
|
|
/** Create a new ThreadGroup using the given name and
|
|
* parent group.
|
|
* @param name the name to use for the ThreadGroup.
|
|
* @param parent the ThreadGroup to use as a parent.
|
|
* @exception NullPointerException if parent is null.
|
|
* @exception SecurityException if you cannot change
|
|
* the intended parent group.
|
|
*/
|
|
public ThreadGroup(ThreadGroup parent, String name)
|
|
{
|
|
parent.checkAccess();
|
|
this.parent = parent;
|
|
if (parent.isDestroyed())
|
|
throw new IllegalArgumentException ();
|
|
this.name = name;
|
|
maxpri = parent.maxpri;
|
|
daemon_flag = parent.daemon_flag;
|
|
parent.addGroup(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.
|
|
* @return the parent of this ThreadGroup.
|
|
*/
|
|
public final ThreadGroup getParent()
|
|
{
|
|
return parent;
|
|
}
|
|
|
|
/** 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, the current value is not changed.
|
|
* Calling this does not effect threads already in this ThreadGroup.
|
|
* @param maxpri the new maximum priority for 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.
|
|
*/
|
|
public final int getMaxPriority()
|
|
{
|
|
return maxpri;
|
|
}
|
|
|
|
/** 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.
|
|
* @specnote The Java API docs indicate that the group is destroyed
|
|
* when either of those happen, but that doesn't make
|
|
* sense.
|
|
* @param daemon whether this ThreadGroup should be a daemon group.
|
|
* @exception SecurityException if you cannoy modify this ThreadGroup.
|
|
*/
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
public synchronized boolean isDestroyed()
|
|
{
|
|
return parent == null && this != root;
|
|
}
|
|
|
|
/** Check whether this ThreadGroup is an ancestor of the
|
|
* specified ThreadGroup, or if they are the same.
|
|
*
|
|
* @param g the group to test on.
|
|
* @return whether this ThreadGroup is a parent of the
|
|
* specified group.
|
|
*/
|
|
public final boolean parentOf(ThreadGroup tg)
|
|
{
|
|
while (tg != null)
|
|
{
|
|
if (tg == this)
|
|
return true;
|
|
tg = tg.parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Return the total number of active threads in this ThreadGroup
|
|
* and all its descendants.<P>
|
|
*
|
|
* This cannot return an exact number, since the status of threads
|
|
* may change after they were counted. But it should be pretty
|
|
* close.<P>
|
|
*
|
|
* @return the number of active threads in this ThreadGroup and
|
|
* its descendants.
|
|
* @specnote it isn't clear what the definition of an "Active" thread is.
|
|
* Current JDKs regard all threads as active until they are
|
|
* finished, regardless of whether the thread has been started
|
|
* or not. We implement this behaviour.
|
|
* There is open JDC bug, <A HREF="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html">
|
|
* 4089701</A>, regarding this issue.
|
|
*
|
|
*/
|
|
public synchronized int activeCount()
|
|
{
|
|
int total = threads.size();
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup g = (ThreadGroup) groups.elementAt(i);
|
|
total += g.activeCount();
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/** Get the number of active groups in this ThreadGroup. This group
|
|
* itself is not included in the count.
|
|
* @specnote it is unclear what exactly constitutes an
|
|
* active ThreadGroup. Currently we assume that
|
|
* all sub-groups are active, per current JDKs.
|
|
* @return the number of active groups in this ThreadGroup.
|
|
*/
|
|
public synchronized int activeGroupCount()
|
|
{
|
|
int total = groups.size();
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup g = (ThreadGroup) groups.elementAt(i);
|
|
total += g.activeGroupCount();
|
|
}
|
|
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.
|
|
*
|
|
* @param threads the array to put the threads into.
|
|
* @return the number of threads put into the array.
|
|
*/
|
|
public int enumerate(Thread[] threads)
|
|
{
|
|
return enumerate(threads, 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.
|
|
*
|
|
* @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)
|
|
{
|
|
Enumeration e = threads.elements();
|
|
while (e.hasMoreElements() && next_index < list.length)
|
|
list[next_index++] = (Thread) e.nextElement();
|
|
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;
|
|
}
|
|
|
|
/** 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.
|
|
*
|
|
* @param groups the array to put the ThreadGroups into.
|
|
* @return the number of ThreadGroups copied into the array.
|
|
*/
|
|
public int enumerate(ThreadGroup[] groups)
|
|
{
|
|
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)
|
|
{
|
|
Enumeration e = groups.elements();
|
|
while (e.hasMoreElements() && next_index < list.length)
|
|
{
|
|
ThreadGroup g = (ThreadGroup) e.nextElement();
|
|
list[next_index++] = g;
|
|
if (recurse && next_index != list.length)
|
|
next_index = g.enumerate(list, next_index, true);
|
|
}
|
|
return next_index;
|
|
}
|
|
|
|
/** Interrupt all Threads in this ThreadGroup and its sub-groups.
|
|
* @exception SecurityException if you cannot modify this
|
|
* ThreadGroup or any of its Threads or children
|
|
* ThreadGroups.
|
|
* @since JDK1.2
|
|
*/
|
|
public final synchronized void interrupt()
|
|
{
|
|
checkAccess();
|
|
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();
|
|
}
|
|
}
|
|
|
|
/** Stop all Threads in this ThreadGroup and its descendants.
|
|
* @exception SecurityException if you cannot modify this
|
|
* ThreadGroup or any of its Threads or children
|
|
* ThreadGroups.
|
|
* @deprecated This method calls Thread.stop(), which is dangerous.
|
|
*/
|
|
public final synchronized void stop()
|
|
{
|
|
checkAccess();
|
|
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();
|
|
}
|
|
}
|
|
|
|
/** Suspend all Threads in this ThreadGroup and its descendants.
|
|
* @exception SecurityException if you cannot modify this
|
|
* ThreadGroup or any of its Threads or children
|
|
* ThreadGroups.
|
|
* @deprecated This method calls Thread.suspend(), which is dangerous.
|
|
*/
|
|
public final synchronized void suspend()
|
|
{
|
|
checkAccess();
|
|
for (int i=0; i<threads.size(); i++)
|
|
{
|
|
Thread t = (Thread) threads.elementAt(i);
|
|
t.suspend();
|
|
}
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
|
|
tg.suspend();
|
|
}
|
|
}
|
|
|
|
/** Resume all Threads in this ThreadGroup and its descendants.
|
|
* @exception SecurityException if you cannot modify this
|
|
* ThreadGroup or any of its Threads or children
|
|
* ThreadGroups.
|
|
* @deprecated This method relies on Thread.suspend(), which is dangerous.
|
|
*/
|
|
public final synchronized void resume()
|
|
{
|
|
checkAccess();
|
|
for (int i=0; i < threads.size(); i++)
|
|
{
|
|
Thread t = (Thread) threads.elementAt(i);
|
|
t.resume();
|
|
}
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
|
|
tg.resume();
|
|
}
|
|
}
|
|
|
|
// This is a helper that is used to implement the destroy method.
|
|
private synchronized void checkDestroy ()
|
|
{
|
|
if (! threads.isEmpty())
|
|
throw new IllegalThreadStateException ("ThreadGroup has threads");
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
|
|
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.
|
|
*/
|
|
public final synchronized void destroy()
|
|
{
|
|
checkAccess();
|
|
if (isDestroyed())
|
|
throw new IllegalThreadStateException("Already destroyed.");
|
|
checkDestroy ();
|
|
if (parent != null)
|
|
parent.removeGroup(this);
|
|
parent = null;
|
|
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
|
|
tg.destroy();
|
|
}
|
|
}
|
|
|
|
/** Print out information about this ThreadGroup to System.out.
|
|
*/
|
|
public void list()
|
|
{
|
|
list("");
|
|
}
|
|
|
|
private synchronized void list(String indentation)
|
|
{
|
|
System.out.print(indentation);
|
|
System.out.println(toString ());
|
|
String sub = indentation + " ";
|
|
for (int i=0; i < threads.size(); i++)
|
|
{
|
|
Thread t = (Thread) threads.elementAt(i);
|
|
System.out.print(sub);
|
|
System.out.println(t.toString());
|
|
}
|
|
for (int i=0; i < groups.size(); i++)
|
|
{
|
|
ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
|
|
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.
|
|
*/
|
|
public void uncaughtException(Thread thread, Throwable t)
|
|
{
|
|
if (parent != null)
|
|
parent.uncaughtException (thread, t);
|
|
else if (! (t instanceof ThreadDeath))
|
|
{
|
|
if (thread != null)
|
|
System.out.print("Exception in thread \"" + thread.getName() + "\" ");
|
|
t.printStackTrace();
|
|
had_uncaught_exception = true;
|
|
}
|
|
}
|
|
|
|
/** Tell the VM whether it may suspend Threads in low memory
|
|
* situations.
|
|
* @deprecated This method is unimplemented, because it would rely on
|
|
* suspend(), which is deprecated. There is no way for a Java
|
|
* program to determine whether this has any effect whatsoever,
|
|
* so we don't need it.
|
|
* @return false
|
|
*/
|
|
public boolean allowThreadSuspension(boolean allow)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/** Get a human-readable representation of this ThreadGroup.
|
|
* @return a String representing this ThreadGroup.
|
|
* @specnote Language Spec and Class Libraries book disagree a bit here.
|
|
* We follow the Spec, but add "ThreadGroup" per the book. We
|
|
* include "java.lang" based on the list() example in the Class
|
|
* Libraries book.
|
|
*/
|
|
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()
|
|
{
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null)
|
|
sm.checkAccess(this);
|
|
}
|
|
|
|
// This is called to add a Thread to our internal list.
|
|
final synchronized void addThread(Thread t)
|
|
{
|
|
if (isDestroyed())
|
|
throw new IllegalThreadStateException ("ThreadGroup is destroyed");
|
|
|
|
threads.addElement(t);
|
|
}
|
|
|
|
// This is called to remove a Thread from our internal list.
|
|
final synchronized void removeThread(Thread t)
|
|
{
|
|
if (isDestroyed())
|
|
throw new IllegalThreadStateException ();
|
|
|
|
threads.removeElement(t);
|
|
// 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.
|
|
if (parent != null)
|
|
parent.removeGroup(this);
|
|
parent = null;
|
|
}
|
|
}
|
|
|
|
// This is called to add a ThreadGroup to our internal list.
|
|
final synchronized void addGroup(ThreadGroup g)
|
|
{
|
|
groups.addElement(g);
|
|
}
|
|
|
|
// This is called to remove a ThreadGroup from our internal list.
|
|
final synchronized void removeGroup(ThreadGroup g)
|
|
{
|
|
groups.removeElement(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.
|
|
if (parent != null)
|
|
parent.removeGroup(this);
|
|
parent = null;
|
|
}
|
|
}
|
|
}
|