| /* java.lang.ThreadGroup | |
| Copyright (C) 1998, 2000, 2001 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 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() | |
| { | |
| int total = 0; | |
| for (int i = 0; i < threads.size(); ++i) | |
| { | |
| if (((Thread) threads.elementAt(i)).isAlive ()) | |
| ++total; | |
| } | |
| 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) | |
| { | |
| 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; | |
| } | |
| /** 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.err.print ("Exception in thread \"" | |
| + thread.getName() + "\" "); | |
| try | |
| { | |
| t.printStackTrace(System.err); | |
| } | |
| catch (Throwable x) | |
| { | |
| // This means that something is badly screwed up with the runtime, | |
| // or perhaps someone is messing with the SecurityManager. In any | |
| // case, try to deal with it gracefully. | |
| System.err.println(t); | |
| System.err.println("*** Got " + x.toString() + | |
| " while trying to print stack trace"); | |
| } | |
| 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; | |
| } | |
| } | |
| } |