diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/java/lang/ThreadGroup.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/lang/ThreadGroup.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,1093 @@ +/* + * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.lang; + +import java.io.PrintStream; +import java.util.Arrays; +import sun.misc.VM; + +/** + * A thread group represents a set of threads. In addition, a thread + * group can also include other thread groups. The thread groups form + * a tree in which every thread group except the initial thread group + * has a parent. + *

+ * A thread is allowed to access information about its own thread + * group, but not to access information about its thread group's + * parent thread group or any other thread groups. + * + * @author unascribed + * @since JDK1.0 + */ +/* The locking strategy for this code is to try to lock only one level of the + * tree wherever possible, but otherwise to lock from the bottom up. + * That is, from child thread groups to parents. + * This has the advantage of limiting the number of locks that need to be held + * and in particular avoids having to grab the lock for the root thread group, + * (or a global lock) which would be a source of contention on a + * multi-processor system with many thread groups. + * This policy often leads to taking a snapshot of the state of a thread group + * and working off of that snapshot, rather than holding the thread group locked + * while we work on the children. + */ +public +class ThreadGroup implements Thread.UncaughtExceptionHandler { + ThreadGroup parent; + String name; + int maxPriority; + boolean destroyed; + boolean daemon; + boolean vmAllowSuspension; + + int nUnstartedThreads = 0; + int nthreads; + Thread threads[]; + + int ngroups; + ThreadGroup groups[]; + + /** + * Creates an empty Thread group that is not in any Thread group. + * This method is used to create the system Thread group. + */ + private ThreadGroup() { // called from C code + this.name = "system"; + this.maxPriority = Thread.MAX_PRIORITY; + } + + /** + * Constructs a new thread group. The parent of this new group is + * the thread group of the currently running thread. + *

+ * The checkAccess method of the parent thread group is + * called with no arguments; this may result in a security exception. + * + * @param name the name of the new thread group. + * @exception SecurityException if the current thread cannot create a + * thread in the specified thread group. + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + */ + public ThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + /** + * Creates a new thread group. The parent of this new group is the + * specified thread group. + *

+ * The checkAccess method of the parent thread group is + * called with no arguments; this may result in a security exception. + * + * @param parent the parent thread group. + * @param name the name of the new thread group. + * @exception NullPointerException if the thread group argument is + * null. + * @exception SecurityException if the current thread cannot create a + * thread in the specified thread group. + * @see java.lang.SecurityException + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + */ + public ThreadGroup(ThreadGroup parent, String name) { + if (parent == null) { + throw new NullPointerException(); + } + parent.checkAccess(); + this.name = name; + this.maxPriority = parent.maxPriority; + this.daemon = parent.daemon; + this.vmAllowSuspension = parent.vmAllowSuspension; + this.parent = parent; + parent.add(this); + } + + /** + * Returns the name of this thread group. + * + * @return the name of this thread group. + * @since JDK1.0 + */ + public final String getName() { + return name; + } + + /** + * Returns the parent of this thread group. + *

+ * First, if the parent is not null, the + * checkAccess method of the parent thread group is + * called with no arguments; this may result in a security exception. + * + * @return the parent of this thread group. The top-level thread group + * is the only thread group whose parent is null. + * @exception SecurityException if the current thread cannot modify + * this thread group. + * @see java.lang.ThreadGroup#checkAccess() + * @see java.lang.SecurityException + * @see java.lang.RuntimePermission + * @since JDK1.0 + */ + public final ThreadGroup getParent() { + if (parent != null) + parent.checkAccess(); + return parent; + } + + /** + * Returns the maximum priority of this thread group. Threads that are + * part of this group cannot have a higher priority than the maximum + * priority. + * + * @return the maximum priority that a thread in this thread group + * can have. + * @see #setMaxPriority + * @since JDK1.0 + */ + public final int getMaxPriority() { + return maxPriority; + } + + /** + * Tests if this thread group is a daemon thread group. A + * daemon thread group is automatically destroyed when its last + * thread is stopped or its last thread group is destroyed. + * + * @return true if this thread group is a daemon thread group; + * false otherwise. + * @since JDK1.0 + */ + public final boolean isDaemon() { + return daemon; + } + + /** + * Tests if this thread group has been destroyed. + * + * @return true if this object is destroyed + * @since JDK1.1 + */ + public synchronized boolean isDestroyed() { + return destroyed; + } + + /** + * Changes the daemon status of this thread group. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + *

+ * A daemon thread group is automatically destroyed when its last + * thread is stopped or its last thread group is destroyed. + * + * @param daemon if true, marks this thread group as + * a daemon thread group; otherwise, marks this + * thread group as normal. + * @exception SecurityException if the current thread cannot modify + * this thread group. + * @see java.lang.SecurityException + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + */ + public final void setDaemon(boolean daemon) { + checkAccess(); + this.daemon = daemon; + } + + /** + * Sets the maximum priority of the group. Threads in the thread + * group that already have a higher priority are not affected. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + *

+ * If the pri argument is less than + * {@link Thread#MIN_PRIORITY} or greater than + * {@link Thread#MAX_PRIORITY}, the maximum priority of the group + * remains unchanged. + *

+ * Otherwise, the priority of this ThreadGroup object is set to the + * smaller of the specified pri and the maximum permitted + * priority of the parent of this thread group. (If this thread group + * is the system thread group, which has no parent, then its maximum + * priority is simply set to pri.) Then this method is + * called recursively, with pri as its argument, for + * every thread group that belongs to this thread group. + * + * @param pri the new priority of the thread group. + * @exception SecurityException if the current thread cannot modify + * this thread group. + * @see #getMaxPriority + * @see java.lang.SecurityException + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + */ + public final void setMaxPriority(int pri) { + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + checkAccess(); + if (pri < Thread.MIN_PRIORITY || pri > Thread.MAX_PRIORITY) { + return; + } + maxPriority = (parent != null) ? Math.min(pri, parent.maxPriority) : pri; + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + groupsSnapshot[i].setMaxPriority(pri); + } + } + + /** + * Tests if this thread group is either the thread group + * argument or one of its ancestor thread groups. + * + * @param g a thread group. + * @return true if this thread group is the thread group + * argument or one of its ancestor thread groups; + * false otherwise. + * @since JDK1.0 + */ + public final boolean parentOf(ThreadGroup g) { + for (; g != null ; g = g.parent) { + if (g == this) { + return true; + } + } + return false; + } + + /** + * Determines if the currently running thread has permission to + * modify this thread group. + *

+ * If there is a security manager, its checkAccess method + * is called with this thread group as its argument. This may result + * in throwing a SecurityException. + * + * @exception SecurityException if the current thread is not allowed to + * access this thread group. + * @see java.lang.SecurityManager#checkAccess(java.lang.ThreadGroup) + * @since JDK1.0 + */ + public final void checkAccess() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkAccess(this); + } + } + + /** + * Returns an estimate of the number of active threads in this thread + * group and its subgroups. Recursively iterates over all subgroups in + * this thread group. + * + *

The value returned is only an estimate because the number of + * threads may change dynamically while this method traverses internal + * data structures, and might be affected by the presence of certain + * system threads. This method is intended primarily for debugging + * and monitoring purposes. + * + * @return an estimate of the number of active threads in this thread + * group and in any other thread group that has this thread + * group as an ancestor + * + * @since JDK1.0 + */ + public int activeCount() { + int result; + // Snapshot sub-group data so we don't hold this lock + // while our children are computing. + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + if (destroyed) { + return 0; + } + result = nthreads; + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + result += groupsSnapshot[i].activeCount(); + } + return result; + } + + /** + * Copies into the specified array every active thread in this + * thread group and its subgroups. + * + *

An invocation of this method behaves in exactly the same + * way as the invocation + * + *

+ * {@linkplain #enumerate(Thread[], boolean) enumerate}{@code (list, true)} + *
+ * + * @param list + * an array into which to put the list of threads + * + * @return the number of threads put into the array + * + * @throws SecurityException + * if {@linkplain #checkAccess checkAccess} determines that + * the current thread cannot access this thread group + * + * @since JDK1.0 + */ + public int enumerate(Thread list[]) { + checkAccess(); + return enumerate(list, 0, true); + } + + /** + * Copies into the specified array every active thread in this + * thread group. If {@code recurse} is {@code true}, + * this method recursively enumerates all subgroups of this + * thread group and references to every active thread in these + * subgroups are also included. If the array is too short to + * hold all the threads, the extra threads are silently ignored. + * + *

An application might use the {@linkplain #activeCount activeCount} + * method to get an estimate of how big the array should be, however + * if the array is too short to hold all the threads, the extra threads + * are silently ignored. If it is critical to obtain every active + * thread in this thread group, the caller should verify that the returned + * int value is strictly less than the length of {@code list}. + * + *

Due to the inherent race condition in this method, it is recommended + * that the method only be used for debugging and monitoring purposes. + * + * @param list + * an array into which to put the list of threads + * + * @param recurse + * if {@code true}, recursively enumerate all subgroups of this + * thread group + * + * @return the number of threads put into the array + * + * @throws SecurityException + * if {@linkplain #checkAccess checkAccess} determines that + * the current thread cannot access this thread group + * + * @since JDK1.0 + */ + public int enumerate(Thread list[], boolean recurse) { + checkAccess(); + return enumerate(list, 0, recurse); + } + + private int enumerate(Thread list[], int n, boolean recurse) { + int ngroupsSnapshot = 0; + ThreadGroup[] groupsSnapshot = null; + synchronized (this) { + if (destroyed) { + return 0; + } + int nt = nthreads; + if (nt > list.length - n) { + nt = list.length - n; + } + for (int i = 0; i < nt; i++) { + if (threads[i].isAlive()) { + list[n++] = threads[i]; + } + } + if (recurse) { + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + } + if (recurse) { + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + n = groupsSnapshot[i].enumerate(list, n, true); + } + } + return n; + } + + /** + * Returns an estimate of the number of active groups in this + * thread group and its subgroups. Recursively iterates over + * all subgroups in this thread group. + * + *

The value returned is only an estimate because the number of + * thread groups may change dynamically while this method traverses + * internal data structures. This method is intended primarily for + * debugging and monitoring purposes. + * + * @return the number of active thread groups with this thread group as + * an ancestor + * + * @since JDK1.0 + */ + public int activeGroupCount() { + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + if (destroyed) { + return 0; + } + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + int n = ngroupsSnapshot; + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + n += groupsSnapshot[i].activeGroupCount(); + } + return n; + } + + /** + * Copies into the specified array references to every active + * subgroup in this thread group and its subgroups. + * + *

An invocation of this method behaves in exactly the same + * way as the invocation + * + *

+ * {@linkplain #enumerate(ThreadGroup[], boolean) enumerate}{@code (list, true)} + *
+ * + * @param list + * an array into which to put the list of thread groups + * + * @return the number of thread groups put into the array + * + * @throws SecurityException + * if {@linkplain #checkAccess checkAccess} determines that + * the current thread cannot access this thread group + * + * @since JDK1.0 + */ + public int enumerate(ThreadGroup list[]) { + checkAccess(); + return enumerate(list, 0, true); + } + + /** + * Copies into the specified array references to every active + * subgroup in this thread group. If {@code recurse} is + * {@code true}, this method recursively enumerates all subgroups of this + * thread group and references to every active thread group in these + * subgroups are also included. + * + *

An application might use the + * {@linkplain #activeGroupCount activeGroupCount} method to + * get an estimate of how big the array should be, however if the + * array is too short to hold all the thread groups, the extra thread + * groups are silently ignored. If it is critical to obtain every + * active subgroup in this thread group, the caller should verify that + * the returned int value is strictly less than the length of + * {@code list}. + * + *

Due to the inherent race condition in this method, it is recommended + * that the method only be used for debugging and monitoring purposes. + * + * @param list + * an array into which to put the list of thread groups + * + * @param recurse + * if {@code true}, recursively enumerate all subgroups + * + * @return the number of thread groups put into the array + * + * @throws SecurityException + * if {@linkplain #checkAccess checkAccess} determines that + * the current thread cannot access this thread group + * + * @since JDK1.0 + */ + public int enumerate(ThreadGroup list[], boolean recurse) { + checkAccess(); + return enumerate(list, 0, recurse); + } + + private int enumerate(ThreadGroup list[], int n, boolean recurse) { + int ngroupsSnapshot = 0; + ThreadGroup[] groupsSnapshot = null; + synchronized (this) { + if (destroyed) { + return 0; + } + int ng = ngroups; + if (ng > list.length - n) { + ng = list.length - n; + } + if (ng > 0) { + System.arraycopy(groups, 0, list, n, ng); + n += ng; + } + if (recurse) { + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + } + if (recurse) { + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + n = groupsSnapshot[i].enumerate(list, n, true); + } + } + return n; + } + + /** + * Stops all threads in this thread group. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + *

+ * This method then calls the stop method on all the + * threads in this thread group and in all of its subgroups. + * + * @exception SecurityException if the current thread is not allowed + * to access this thread group or any of the threads in + * the thread group. + * @see java.lang.SecurityException + * @see java.lang.Thread#stop() + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + * @deprecated This method is inherently unsafe. See + * {@link Thread#stop} for details. + */ + @Deprecated + public final void stop() { + if (stopOrSuspend(false)) + Thread.currentThread().stop(); + } + + /** + * Interrupts all threads in this thread group. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + *

+ * This method then calls the interrupt method on all the + * threads in this thread group and in all of its subgroups. + * + * @exception SecurityException if the current thread is not allowed + * to access this thread group or any of the threads in + * the thread group. + * @see java.lang.Thread#interrupt() + * @see java.lang.SecurityException + * @see java.lang.ThreadGroup#checkAccess() + * @since 1.2 + */ + public final void interrupt() { + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + checkAccess(); + for (int i = 0 ; i < nthreads ; i++) { + threads[i].interrupt(); + } + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + groupsSnapshot[i].interrupt(); + } + } + + /** + * Suspends all threads in this thread group. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + *

+ * This method then calls the suspend method on all the + * threads in this thread group and in all of its subgroups. + * + * @exception SecurityException if the current thread is not allowed + * to access this thread group or any of the threads in + * the thread group. + * @see java.lang.Thread#suspend() + * @see java.lang.SecurityException + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + * @deprecated This method is inherently deadlock-prone. See + * {@link Thread#suspend} for details. + */ + @Deprecated + public final void suspend() { + if (stopOrSuspend(true)) + Thread.currentThread().suspend(); + } + + /** + * Helper method: recursively stops or suspends (as directed by the + * boolean argument) all of the threads in this thread group and its + * subgroups, except the current thread. This method returns true + * if (and only if) the current thread is found to be in this thread + * group or one of its subgroups. + */ + private boolean stopOrSuspend(boolean suspend) { + boolean suicide = false; + Thread us = Thread.currentThread(); + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot = null; + synchronized (this) { + checkAccess(); + for (int i = 0 ; i < nthreads ; i++) { + if (threads[i]==us) + suicide = true; + else if (suspend) + threads[i].suspend(); + else + threads[i].stop(); + } + + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i++) + suicide = groupsSnapshot[i].stopOrSuspend(suspend) || suicide; + + return suicide; + } + + /** + * Resumes all threads in this thread group. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + *

+ * This method then calls the resume method on all the + * threads in this thread group and in all of its sub groups. + * + * @exception SecurityException if the current thread is not allowed to + * access this thread group or any of the threads in the + * thread group. + * @see java.lang.SecurityException + * @see java.lang.Thread#resume() + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + * @deprecated This method is used solely in conjunction with + * Thread.suspend and ThreadGroup.suspend, + * both of which have been deprecated, as they are inherently + * deadlock-prone. See {@link Thread#suspend} for details. + */ + @Deprecated + public final void resume() { + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + checkAccess(); + for (int i = 0 ; i < nthreads ; i++) { + threads[i].resume(); + } + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + groupsSnapshot[i].resume(); + } + } + + /** + * Destroys this thread group and all of its subgroups. This thread + * group must be empty, indicating that all threads that had been in + * this thread group have since stopped. + *

+ * First, the checkAccess method of this thread group is + * called with no arguments; this may result in a security exception. + * + * @exception IllegalThreadStateException if the thread group is not + * empty or if the thread group has already been destroyed. + * @exception SecurityException if the current thread cannot modify this + * thread group. + * @see java.lang.ThreadGroup#checkAccess() + * @since JDK1.0 + */ + public final void destroy() { + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + checkAccess(); + if (destroyed || (nthreads > 0)) { + throw new IllegalThreadStateException(); + } + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + if (parent != null) { + destroyed = true; + ngroups = 0; + groups = null; + nthreads = 0; + threads = null; + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i += 1) { + groupsSnapshot[i].destroy(); + } + if (parent != null) { + parent.remove(this); + } + } + + /** + * Adds the specified Thread group to this group. + * @param g the specified Thread group to be added + * @exception IllegalThreadStateException If the Thread group has been destroyed. + */ + private final void add(ThreadGroup g){ + synchronized (this) { + if (destroyed) { + throw new IllegalThreadStateException(); + } + if (groups == null) { + groups = new ThreadGroup[4]; + } else if (ngroups == groups.length) { + groups = Arrays.copyOf(groups, ngroups * 2); + } + groups[ngroups] = g; + + // This is done last so it doesn't matter in case the + // thread is killed + ngroups++; + } + } + + /** + * Removes the specified Thread group from this group. + * @param g the Thread group to be removed + * @return if this Thread has already been destroyed. + */ + private void remove(ThreadGroup g) { + synchronized (this) { + if (destroyed) { + return; + } + for (int i = 0 ; i < ngroups ; i++) { + if (groups[i] == g) { + ngroups -= 1; + System.arraycopy(groups, i + 1, groups, i, ngroups - i); + // Zap dangling reference to the dead group so that + // the garbage collector will collect it. + groups[ngroups] = null; + break; + } + } + if (nthreads == 0) { + notifyAll(); + } + if (daemon && (nthreads == 0) && + (nUnstartedThreads == 0) && (ngroups == 0)) + { + destroy(); + } + } + } + + + /** + * Increments the count of unstarted threads in the thread group. + * Unstarted threads are not added to the thread group so that they + * can be collected if they are never started, but they must be + * counted so that daemon thread groups with unstarted threads in + * them are not destroyed. + */ + void addUnstarted() { + synchronized(this) { + if (destroyed) { + throw new IllegalThreadStateException(); + } + nUnstartedThreads++; + } + } + + /** + * Notifies the group that the thread {@code t} is about to be + * started and adds the thread to this thread group. + */ + void threadStarting(Thread t) { + add(t); + } + + /** + * Adds the specified thread to this thread group. + * + *

Note: This method is called from both library code + * and the Virtual Machine. It is called from VM to add + * certain system threads to the system thread group. + * + * @param t + * the Thread to be added + * + * @throws IllegalThreadStateException + * if the Thread group has been destroyed + */ + void add(Thread t) { + synchronized (this) { + if (destroyed) { + throw new IllegalThreadStateException(); + } + if (threads == null) { + threads = new Thread[4]; + } else if (nthreads == threads.length) { + threads = Arrays.copyOf(threads, nthreads * 2); + } + threads[nthreads] = t; + + // This is done last so it doesn't matter in case the + // thread is killed + nthreads++; + } + } + + /** + * Notifies the group that the thread {@code t} has completed + * an attempt to start. + * + *

If the thread has been started successfully + * then the group has its unstarted Threads count decremented. + * Otherwise the state of this thread group is rolled back as if the + * attempt to start the thread has never occurred. The thread is again + * considered an unstarted member of the thread group, and a subsequent + * attempt to start the thread is permitted. + * + * @param t + * the Thread whose start method was invoked + * + * @param failed + * true if the thread could not be started successfully + */ + void threadStarted(Thread t, boolean failed) { + synchronized(this) { + if (failed) { + remove(t); + } else { + if (destroyed) { + return; + } + nUnstartedThreads--; + } + } + } + + /** + * Notifies the group that the thread {@code t} has terminated. + * + *

Destroy the group if all of the following conditions are + * true: this is a daemon thread group; there are no more alive + * or unstarted threads in the group; there are no subgroups in + * this thread group. + * + * @param t + * the Thread that has terminated + */ + void threadTerminated(Thread t) { + synchronized (this) { + remove(t); + + if (nthreads == 0) { + notifyAll(); + } + if (daemon && (nthreads == 0) && + (nUnstartedThreads == 0) && (ngroups == 0)) + { + destroy(); + } + } + } + + /** + * Removes the specified Thread from this group. Invoking this method + * on a thread group that has been destroyed has no effect. + * + * @param t + * the Thread to be removed + */ + private void remove(Thread t) { + synchronized (this) { + if (destroyed) { + return; + } + for (int i = 0 ; i < nthreads ; i++) { + if (threads[i] == t) { + System.arraycopy(threads, i + 1, threads, i, --nthreads - i); + // Zap dangling reference to the dead thread so that + // the garbage collector will collect it. + threads[nthreads] = null; + break; + } + } + } + } + + /** + * Prints information about this thread group to the standard + * output. This method is useful only for debugging. + * + * @since JDK1.0 + */ + public void list() { + list(System.out, 0); + } + void list(PrintStream out, int indent) { + int ngroupsSnapshot; + ThreadGroup[] groupsSnapshot; + synchronized (this) { + for (int j = 0 ; j < indent ; j++) { + out.print(" "); + } + out.println(this); + indent += 4; + for (int i = 0 ; i < nthreads ; i++) { + for (int j = 0 ; j < indent ; j++) { + out.print(" "); + } + out.println(threads[i]); + } + ngroupsSnapshot = ngroups; + if (groups != null) { + groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); + } else { + groupsSnapshot = null; + } + } + for (int i = 0 ; i < ngroupsSnapshot ; i++) { + groupsSnapshot[i].list(out, indent); + } + } + + /** + * Called by the Java Virtual Machine when a thread in this + * thread group stops because of an uncaught exception, and the thread + * does not have a specific {@link Thread.UncaughtExceptionHandler} + * installed. + *

+ * The uncaughtException method of + * ThreadGroup does the following: + *

+ *

+ * Applications can override this method in subclasses of + * ThreadGroup to provide alternative handling of + * uncaught exceptions. + * + * @param t the thread that is about to exit. + * @param e the uncaught exception. + * @since JDK1.0 + */ + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else { + Thread.UncaughtExceptionHandler ueh = + Thread.getDefaultUncaughtExceptionHandler(); + if (ueh != null) { + ueh.uncaughtException(t, e); + } else if (!(e instanceof ThreadDeath)) { + System.err.print("Exception in thread \"" + + t.getName() + "\" "); + e.printStackTrace(System.err); + } + } + } + + /** + * Used by VM to control lowmem implicit suspension. + * + * @param b boolean to allow or disallow suspension + * @return true on success + * @since JDK1.1 + * @deprecated The definition of this call depends on {@link #suspend}, + * which is deprecated. Further, the behavior of this call + * was never specified. + */ + @Deprecated + public boolean allowThreadSuspension(boolean b) { + this.vmAllowSuspension = b; + if (!b) { + VM.unsuspendSomeThreads(); + } + return true; + } + + /** + * Returns a string representation of this Thread group. + * + * @return a string representation of this thread group. + * @since JDK1.0 + */ + public String toString() { + return getClass().getName() + "[name=" + getName() + ",maxpri=" + maxPriority + "]"; + } +}