/*
* 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);
}
}
}