/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.lang; /** * An implementation of this class is provided, but the documented constructors * are used by the vm specific implementation to create the required "system" * and "main" ThreadGroups. The documented methods are used by java.lang.Thread * to add and remove Threads from their ThreadGroups. * * ThreadGroups are containers of Threads and ThreadGroups, therefore providing * a tree-like structure to organize Threads. The root ThreadGroup name is * "system" and it has no parent ThreadGroup. All other ThreadGroups have * exactly one parent ThreadGroup. All Threads belong to exactly one * ThreadGroup. * * @see Thread * @see SecurityManager */ public class ThreadGroup implements Thread.UncaughtExceptionHandler { // Name of this ThreadGroup private String name; // Maximum priority for Threads inside this ThreadGroup private int maxPriority = Thread.MAX_PRIORITY; // The ThreadGroup to which this ThreadGroup belongs ThreadGroup parent; int numThreads; // The Threads this ThreadGroup contains private Thread[] childrenThreads = new Thread[5]; // The number of children groups int numGroups; // The ThreadGroups this ThreadGroup contains private ThreadGroup[] childrenGroups = new ThreadGroup[3]; // Locked when using the childrenGroups field private class ChildrenGroupsLock {} private Object childrenGroupsLock = new ChildrenGroupsLock(); // Locked when using the childrenThreads field private class ChildrenThreadsLock {} private Object childrenThreadsLock = new ChildrenThreadsLock(); // Whether this ThreadGroup is a daemon ThreadGroup or not private boolean isDaemon; // Whether this ThreadGroup has already been destroyed or not private boolean isDestroyed; /** * Used by the JVM to create the "system" ThreadGroup. Construct a * ThreadGroup instance, and assign the name "system". */ ThreadGroup() { this.name = "system"; } /** * Constructs a new ThreadGroup with the name provided. The new ThreadGroup * will be child of the ThreadGroup to which the * <code>Thread.currentThread()</code> belongs. * * @param name Name for the ThreadGroup being created * * @throws SecurityException if <code>checkAccess()</code> for the parent * group fails with a SecurityException * * @see java.lang.Thread#currentThread */ public ThreadGroup(String name) { this(Thread.currentThread().getThreadGroup(), name); } /** * Constructs a new ThreadGroup with the name provided, as child of the * ThreadGroup <code>parent</code> * * @param parent Parent ThreadGroup * @param name Name for the ThreadGroup being created * * @throws NullPointerException if <code>parent</code> is * <code>null</code> * @throws SecurityException if <code>checkAccess()</code> for the parent * group fails with a SecurityException * @throws IllegalThreadStateException if <code>parent</code> has been * destroyed already */ public ThreadGroup(ThreadGroup parent, String name) { super(); if (Thread.currentThread() != null) { // If parent is null we must throw NullPointerException, but that // will be done "for free" with the message send below parent.checkAccess(); } this.name = name; this.setParent(parent); if (parent != null) { this.setMaxPriority(parent.getMaxPriority()); if (parent.isDaemon()) { this.setDaemon(true); } } } /** * Initialize the "main" ThreadGroup */ ThreadGroup(ThreadGroup parent) { this.name = "main"; this.setParent(parent); } /** * Returns the number of Threads which are children of the receiver, * directly or indirectly. * * @return Number of children Threads */ public int activeCount() { int count = numThreads; // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { count += this.childrenGroups[i].activeCount(); } } return count; } /** * Returns the number of ThreadGroups which are children of the receiver, * directly or indirectly. * * @return Number of children ThreadGroups */ public int activeGroupCount() { int count = 0; // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { // One for this group & the subgroups count += 1 + this.childrenGroups[i].activeGroupCount(); } } return count; } /** * Adds a Thread to the receiver. This should only be visible to class * java.lang.Thread, and should only be called when a new Thread is created * and initialized by the constructor. * * @param thread Thread to add to the receiver * * @throws IllegalThreadStateException if the receiver has been destroyed * already * * @see #remove(java.lang.Thread) */ final void add(Thread thread) throws IllegalThreadStateException { synchronized (this.childrenThreadsLock) { if (!isDestroyed) { if (childrenThreads.length == numThreads) { Thread[] newThreads = new Thread[childrenThreads.length * 2]; System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads); newThreads[numThreads++] = thread; childrenThreads = newThreads; } else { childrenThreads[numThreads++] = thread; } } else { throw new IllegalThreadStateException(); } } } /** * Adds a ThreadGroup to the receiver. * * @param g ThreadGroup to add to the receiver * * @throws IllegalThreadStateException if the receiver has been destroyed * already */ private void add(ThreadGroup g) throws IllegalThreadStateException { synchronized (this.childrenGroupsLock) { if (!isDestroyed) { if (childrenGroups.length == numGroups) { ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2]; System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups); newGroups[numGroups++] = g; childrenGroups = newGroups; } else { childrenGroups[numGroups++] = g; } } else { throw new IllegalThreadStateException(); } } } /** * The definition of this method depends on the deprecated method * <code>suspend()</code>. The behavior of this call was never specified. * * @param b Used to control low memory implicit suspension * * @deprecated Required deprecated method suspend(). */ @Deprecated public boolean allowThreadSuspension(boolean b) { // Does not apply to this VM, no-op return true; } /** * If there is a SecurityManager installed, call <code>checkAccess</code> * in it passing the receiver as parameter, otherwise do nothing. */ public final void checkAccess() { // Forwards the message to the SecurityManager (if there's one) passing // the receiver as parameter SecurityManager currentManager = System.getSecurityManager(); if (currentManager != null) { currentManager.checkAccess(this); } } /** * Destroys the receiver and recursively all its subgroups. It is only legal * to destroy a ThreadGroup that has no Threads. Any daemon ThreadGroup is * destroyed automatically when it becomes empty (no Threads and no * ThreadGroups in it). * * @throws IllegalThreadStateException if the receiver or any of its * subgroups has been destroyed already * @throws SecurityException if <code>this.checkAccess()</code> fails with * a SecurityException */ public final void destroy() { checkAccess(); // Lock this subpart of the tree as we walk synchronized (this.childrenThreadsLock) { synchronized (this.childrenGroupsLock) { int toDestroy = numGroups; // Call recursively for subgroups for (int i = 0; i < toDestroy; i++) { // We always get the first element - remember, when the // child dies it removes itself from our collection. See // below. this.childrenGroups[0].destroy(); } if (parent != null) { parent.remove(this); } // Now that the ThreadGroup is really destroyed it can be tagged // as so this.isDestroyed = true; } } } /* * Auxiliary method that destroys the receiver and recursively all its * subgroups if the receiver is a daemon ThreadGroup. * * @see #destroy * @see #setDaemon * @see #isDaemon */ private void destroyIfEmptyDaemon() { // Has to be non-destroyed daemon to make sense synchronized (this.childrenThreadsLock) { if (isDaemon && !isDestroyed && numThreads == 0) { synchronized (this.childrenGroupsLock) { if (numGroups == 0) { destroy(); } } } } } /** * Copies an array with all Threads which are children of the receiver * (directly or indirectly) into the array <code>threads</code> passed as * parameters. If the array passed as parameter is too small no exception is * thrown - the extra elements are simply not copied. * * @param threads Thread array into which the Threads will be copied * @return How many Threads were copied over * */ public int enumerate(Thread[] threads) { return enumerate(threads, true); } /** * Copies an array with all Threads which are children of the receiver into * the array <code>threads</code> passed as parameter. Children Threads of * subgroups are recursively copied as well if parameter * <code>recurse</code> is <code>true</code>. * * If the array passed as parameter is too small no exception is thrown - * the extra elements are simply not copied. * * @param threads array into which the Threads will be copied * @param recurse Indicates whether Threads in subgroups should be * recursively copied as well or not * @return How many Threads were copied over * */ public int enumerate(Thread[] threads, boolean recurse) { return enumerateGeneric(threads, recurse, 0, true); } /** * Copies an array with all ThreadGroups which are children of the receiver * (directly or indirectly) into the array <code>groups</code> passed as * parameters. If the array passed as parameter is too small no exception is * thrown - the extra elements are simply not copied. * * @param groups array into which the ThreadGroups will be copied * @return How many ThreadGroups were copied over * */ public int enumerate(ThreadGroup[] groups) { return enumerate(groups, true); } /** * Copies an array with all ThreadGroups which are children of the receiver * into the array <code>groups</code> passed as parameter. Children * ThreadGroups of subgroups are recursively copied as well if parameter * <code>recurse</code> is <code>true</code>. * * If the array passed as parameter is too small no exception is thrown - * the extra elements are simply not copied. * * @param groups array into which the ThreadGroups will be copied * @param recurse Indicates whether ThreadGroups in subgroups should be * recursively copied as well or not * @return How many ThreadGroups were copied over * */ public int enumerate(ThreadGroup[] groups, boolean recurse) { return enumerateGeneric(groups, recurse, 0, false); } /** * Copies into <param>enumeration</param> starting at * <param>enumerationIndex</param> all Threads or ThreadGroups in the * receiver. If <param>recurse</param> is true, recursively enumerate the * elements in subgroups. * * If the array passed as parameter is too small no exception is thrown - * the extra elements are simply not copied. * * @param enumeration array into which the elements will be copied * @param recurse Indicates whether subgroups should be enumerated or not * @param enumerationIndex Indicates in which position of the enumeration * array we are * @param enumeratingThreads Indicates whether we are enumerating Threads or * ThreadGroups * @return How many elements were enumerated/copied over */ private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, boolean enumeratingThreads) { checkAccess(); Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads : (Object[]) childrenGroups; Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock; synchronized (syncLock) { // Lock this subpart of the tree as we walk for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) { if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) { if (enumerationIndex >= enumeration.length) { return enumerationIndex; } enumeration[enumerationIndex++] = immediateCollection[i]; } } } if (recurse) { // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { if (enumerationIndex >= enumeration.length) { return enumerationIndex; } enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse, enumerationIndex, enumeratingThreads); } } } return enumerationIndex; } /** * Answers the maximum allowed priority for a Thread in the receiver. * * @return the maximum priority (an <code>int</code>) * * @see #setMaxPriority */ public final int getMaxPriority() { return maxPriority; } /** * Answers the name of the receiver. * * @return the receiver's name (a java.lang.String) */ public final String getName() { return name; } /** * Answers the receiver's parent ThreadGroup. It can be null if the receiver * is the the root ThreadGroup. * * @return the parent ThreadGroup * */ public final ThreadGroup getParent() { if (parent != null) { parent.checkAccess(); } return parent; } /** * Interrupts every Thread in the receiver and recursively in all its * subgroups. * * @throws SecurityException if <code>this.checkAccess()</code> fails with * a SecurityException * * @see Thread#interrupt */ public final void interrupt() { checkAccess(); // Lock this subpart of the tree as we walk synchronized (this.childrenThreadsLock) { for (int i = 0; i < numThreads; i++) { this.childrenThreads[i].interrupt(); } } // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { this.childrenGroups[i].interrupt(); } } } /** * Answers true if the receiver is a daemon ThreadGroup, false otherwise. * * @return if the receiver is a daemon ThreadGroup * * @see #setDaemon * @see #destroy */ public final boolean isDaemon() { return isDaemon; } /** * Answers true if the receiver has been destroyed already, false otherwise. * * @return if the receiver has been destroyed already * * @see #destroy */ public boolean isDestroyed() { return isDestroyed; } /** * Outputs to <code>System.out</code> a text representation of the * hierarchy of Threads and ThreadGroups in the receiver (and recursively). * Proper indentation is done to suggest the nesting of groups inside groups * and threads inside groups. */ public void list() { // We start in a fresh line System.out.println(); list(0); } /* * Outputs to <code>System.out</code>a text representation of the * hierarchy of Threads and ThreadGroups in the receiver (and recursively). * The indentation will be four spaces per level of nesting. * * @param levels How many levels of nesting, so that proper indentation can * be output. */ private void list(int levels) { for (int i = 0; i < levels; i++) { System.out.print(" "); // 4 spaces for each level } // Print the receiver System.out.println(this.toString()); // Print the children threads, with 1 extra indentation synchronized (this.childrenThreadsLock) { for (int i = 0; i < numThreads; i++) { // children get an extra indentation, 4 spaces for each level for (int j = 0; j <= levels; j++) { System.out.print(" "); } System.out.println(this.childrenThreads[i]); } } synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { this.childrenGroups[i].list(levels + 1); } } } /** * Answers true if the receiver is a direct or indirect parent group of * ThreadGroup <code>g</code>, false otherwise. * * @param g ThreadGroup to test * * @return if the receiver is parent of the ThreadGroup passed as parameter * */ public final boolean parentOf(ThreadGroup g) { while (g != null) { if (this == g) { return true; } g = g.parent; } return false; } /** * Removes a Thread from the receiver. This should only be visible to class * java.lang.Thread, and should only be called when a Thread dies. * * @param thread Thread to remove from the receiver * * @see #add(Thread) */ final void remove(java.lang.Thread thread) { synchronized (this.childrenThreadsLock) { for (int i = 0; i < numThreads; i++) { if (childrenThreads[i].equals(thread)) { numThreads--; System .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads - i); childrenThreads[numThreads] = null; break; } } } destroyIfEmptyDaemon(); } /** * Removes an immediate subgroup from the receiver. * * @param g Threadgroup to remove from the receiver * * @see #add(Thread) * @see #add(ThreadGroup) */ private void remove(ThreadGroup g) { synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { if (childrenGroups[i].equals(g)) { numGroups--; System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i); childrenGroups[numGroups] = null; break; } } } destroyIfEmptyDaemon(); } /** * Resumes every Thread in the receiver and recursively in all its * subgroups. * * @throws SecurityException if <code>this.checkAccess()</code> fails with * a SecurityException * * @see Thread#resume * @see #suspend * * @deprecated Requires deprecated method Thread.resume(). */ @SuppressWarnings("deprecation") @Deprecated public final void resume() { checkAccess(); // Lock this subpart of the tree as we walk synchronized (this.childrenThreadsLock) { for (int i = 0; i < numThreads; i++) { this.childrenThreads[i].resume(); } } // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { this.childrenGroups[i].resume(); } } } /** * Configures the receiver to be a daemon ThreadGroup or not. Daemon * ThreadGroups are automatically destroyed when they become empty. * * @param isDaemon new value defining if receiver should be daemon or not * * @throws SecurityException if <code>checkAccess()</code> for the parent * group fails with a SecurityException * * @see #isDaemon * @see #destroy */ public final void setDaemon(boolean isDaemon) { checkAccess(); this.isDaemon = isDaemon; } /** * Configures the maximum allowed priority for a Thread in the receiver and * recursively in all its subgroups. * * One can never change the maximum priority of a ThreadGroup to be higher * than it was. Such an attempt will not result in an exception, it will * simply leave the ThreadGroup with its current maximum priority. * * @param newMax the new maximum priority to be set * * @throws SecurityException if <code>checkAccess()</code> fails with a * SecurityException * @throws IllegalArgumentException if the new priority is greater than * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY * * @see #getMaxPriority */ public final void setMaxPriority(int newMax) { checkAccess(); if (newMax <= this.maxPriority) { if (newMax < Thread.MIN_PRIORITY) { newMax = Thread.MIN_PRIORITY; } int parentPriority = parent == null ? newMax : parent.getMaxPriority(); this.maxPriority = parentPriority <= newMax ? parentPriority : newMax; // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { // ??? why not maxPriority for (int i = 0; i < numGroups; i++) { this.childrenGroups[i].setMaxPriority(newMax); } } } } /** * Sets the parent ThreadGroup of the receiver, and adds the receiver to the * parent's collection of immediate children (if <code>parent</code> is * not <code>null</code>). * * @param parent The parent ThreadGroup, or null if the receiver is to be * the root ThreadGroup * * @see #getParent * @see #parentOf */ private void setParent(ThreadGroup parent) { if (parent != null) { parent.add(this); } this.parent = parent; } /** * Stops every Thread in the receiver and recursively in all its subgroups. * * @throws SecurityException if <code>this.checkAccess()</code> fails with * a SecurityException * * @see Thread#stop() * @see Thread#stop(Throwable) * @see ThreadDeath * * @deprecated Requires deprecated method Thread.stop(). */ @SuppressWarnings("deprecation") @Deprecated public final void stop() { if (stopHelper()) { Thread.currentThread().stop(); } } /** * @deprecated Requires deprecated method Thread.suspend(). */ @SuppressWarnings("deprecation") @Deprecated private final boolean stopHelper() { checkAccess(); boolean stopCurrent = false; // Lock this subpart of the tree as we walk synchronized (this.childrenThreadsLock) { Thread current = Thread.currentThread(); for (int i = 0; i < numThreads; i++) { if (this.childrenThreads[i] == current) { stopCurrent = true; } else { this.childrenThreads[i].stop(); } } } // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { stopCurrent |= this.childrenGroups[i].stopHelper(); } } return stopCurrent; } /** * Suspends every Thread in the receiver and recursively in all its * subgroups. * * @throws SecurityException if <code>this.checkAccess()</code> fails with * a SecurityException * * @see Thread#suspend * @see #resume * * @deprecated Requires deprecated method Thread.suspend(). */ @SuppressWarnings("deprecation") @Deprecated public final void suspend() { if (suspendHelper()) { Thread.currentThread().suspend(); } } /** * @deprecated Requires deprecated method Thread.suspend(). */ @SuppressWarnings("deprecation") @Deprecated private final boolean suspendHelper() { checkAccess(); boolean suspendCurrent = false; // Lock this subpart of the tree as we walk synchronized (this.childrenThreadsLock) { Thread current = Thread.currentThread(); for (int i = 0; i < numThreads; i++) { if (this.childrenThreads[i] == current) { suspendCurrent = true; } else { this.childrenThreads[i].suspend(); } } } // Lock this subpart of the tree as we walk synchronized (this.childrenGroupsLock) { for (int i = 0; i < numGroups; i++) { suspendCurrent |= this.childrenGroups[i].suspendHelper(); } } return suspendCurrent; } /** * Answers a string containing a concise, human-readable description of the * receiver. * * @return a printable representation for the receiver. */ @Override public String toString() { return getClass().getName() + "[name=" + this.getName() + ",maxpri=" + this.getMaxPriority() + "]"; } /** * Any uncaught exception in any Thread has to be forwarded (by the VM) to * the Thread's ThreadGroup by sending this message (uncaughtException). * This allows users to define custom ThreadGroup classes and custom * behavior for when a Thread has an uncaughtException or when it does * (ThreadDeath). * * @param t Thread with an uncaught exception * @param e The uncaught exception itself * * @see Thread#stop() * @see Thread#stop(Throwable) * @see ThreadDeath */ public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { // No parent group, has to be 'system' Thread Group e.printStackTrace(System.err); } } }