/**************************************************************************
* Parts copyright (c) 2001 by Punch Telematix. All rights reserved. *
* Parts copyright (c) 2007, 2009 by Chris Gray, /k/ Embedded Java *
* Solutions. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix or of /k/ Embedded Java Solutions*
* nor the names of other contributors may be used to endorse or promote*
* products derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL PUNCH TELEMATIX, /K/ EMBEDDED JAVA SOLUTIONS OR OTHER *
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.lang;
import java.util.Enumeration;
import java.util.Vector;
/**
** A ThreadGroup can contain Threads (its "flock") and ThreadGroups
** (its "children"). The children have of course their own flock
** and children, and so ad infinitum.
**
** T h r e a d G r o u p
** flock children
** | | | | | |
** T T T TG TG TG
** /|\ /|\ /|\
**
** Membership of the Vectors "flock" and "children" is updated by
** the methods [de]registerThread[Group].
*/
public class ThreadGroup {
/*
** Note: this class is initialized "by hand" before the VM is fully
** initialized. Consequently it must not have a static initializer.
** (It can have static variables, and even constant initial values
** for those variables, but nothing fancier and certainly no static{}
** clause.)
*/
/**
** The name of this ThreadGroup.
*/
private final String name;
/**
** The number of threads created in this ThreadGroup and not yet terminated
** (includes yet-to-be-started threads).
*/
int totalCount;
/**
** The maximum thread priority permitted within this group.
*/
private int maxPriority;
/**
** Set true iff ThreadGroup has already been destroy()ed.
*/
private boolean destroyed;
/**
** Set tru iff this is a daemon ThreadGroup.
*/
private boolean daemon;
/**
** The parent ThreadGroup of this group.
*/
private ThreadGroup parent;
/**
** The child ThreadGroups of this group.
*/
private Vector children;
/**
** The member Threads of this group (= only those started and not stopped).
*/
private Vector flock;
/**
** Constructor ThreadGroup(String)
** - parent is the ThreadGroup of which the current (calling) Thread
** is a member. See ThreadGroup(ThreadGroup,String) below.
*/
public ThreadGroup(String name) throws SecurityException {
this(Thread.currentThread().getThreadGroup(),name);
}
/**
** Constructor ThreadGroup(ThreadGroup parent, String name)
** constructs a ThreadGroup with the given name and parent.
** The current (calling) thread must have the necessary access rights.
** The parent ThreadGroup must not be null, and must not have its
** "destroyed" flag set (this flag is set when destroy() is called).
**
** Note that no instance of Vector is yet allocated to the fields
** "children" and "flock": one will be allocated the first time
** registerThreadGroup() or registerThread() is called. This
** enables us to create the systemThreadGroup and systemInitThread
** without calling a constructor (and without having to initialize
** class Vector).
*/
public ThreadGroup(ThreadGroup parent, String name)
throws NullPointerException, SecurityException, IllegalThreadStateException
{
Thread.currentThread().getThreadGroup().checkAccess();
if(parent==null) throw new NullPointerException();
if (parent.destroyed) {
throw new IllegalThreadStateException();
}
this.name = name;
this.parent = parent;
parent.registerThreadGroup(this);
}
/**
** toString()
** creates a String describing the ThreadGroup..
*/
public String toString() {
return "[name="+name+",maxpri="+maxPriority+"]";
}
/**
** checkAccess()
** checks that the current (calling) thread is allowed to modify this
** ThreadGroup.
*/
public final void checkAccess()
throws SecurityException
{
if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) {
SecurityManager sm = System.theSecurityManager;
if (sm != null) {
sm.checkAccess(this);
}
}
}
/**
* * registerThread(Thread t) * adds t to Vector flock, the list of member
* Threads. * The Vector is created on demand (see
* ThreadGroup(ThreadGroup,String)).
*/
synchronized void registerThread(Thread t) {
if (flock == null) {
flock = new Vector();
}
totalCount++;
flock.add(t);
}
/**
** deregisterThread(Thread t)
** removes t from Vector flock, the list of member Threads.
*/
synchronized void deregisterThread(Thread t) {
flock.remove(t);
totalCount--;
checkForTermination();
}
/**
** registerThreadGroup(ThreadGroup tg)
** adds tg to Vector children, the list of child ThreadGroups.
** The Vector is created on demand (see ThreadGroup(ThreadGroup,String)).
*/
private void registerThreadGroup(ThreadGroup tg) {
synchronized (this) {
if (children==null) {
children = new Vector();
}
}
children.add(tg);
}
/**
** deregisterThreadGroup(ThreadGroup tg)
** removes tg from Vector children, the list of child ThreadGroups.
*/
private synchronized void deregisterThreadGroup(ThreadGroup tg) {
children.remove(tg);
checkForTermination();
}
/**
** If the `daemon' flag is set and the both `children' and `flock' are
** now empty, detach this ThreadGroup from its parent (and hence become
** GC-able).
*/
private void checkForTermination() {
if (daemon
&& (children == null || children.size() == 0)
&& (totalCount == 0)
&& parent != null
) {
parent.deregisterThreadGroup(this);
parent = null;
}
}
/**
** activeCount()
** estimates the number of active threads in this ThreadGroup.
** We first count the threads which are direct members of this
** ThreadGroup (Vector flock), and then apply this function
** recursively to every child ThreadGroup (Vector children).
*/
public int activeCount() {
Enumeration e;
int count = 0;
if (flock != null) {
e = flock.elements();
while (e.hasMoreElements()) {
if (((Thread)e.nextElement()).isAlive()) ++count;
}
}
if (children != null) {
e = children.elements();
while (e.hasMoreElements()) {
count += ((ThreadGroup)e.nextElement()).activeCount();
}
}
return count;
}
/**
** activeGroupCount()
** estimates the number of thread groups in this ThreadGroup.
** We first count the thread groups which are direct members of this
** ThreadGroup (Vector children), and then apply this function
** recursively to every child ThreadGroup.
*/
public int activeGroupCount() {
if (children == null) return 0;
int count = children.size();
Enumeration e = children.elements();
while (e.hasMoreElements()) {
count += ((ThreadGroup)e.nextElement()).activeGroupCount();
}
return count;
}
/**
** totalCount()
** Same as activeCount, but includes yet-to-be-started threads.
** For internal use.
*/
private int totalCount() {
Enumeration e;
int count = totalCount;
if (children != null) {
e = children.elements();
while (e.hasMoreElements()) {
count += ((ThreadGroup)e.nextElement()).totalCount();
}
}
return count;
}
/*
** nonDaemonCount()
** Same as activeCount, but excludes daemon threads.
** For internal use.
private int nonDaemonCount() {
Enumeration e;
int count = 0;
if (flock != null) {
e = flock.elements();
while (e.hasMoreElements()) {
if (((Thread)e.nextElement()).isAlive()) ++count;
}
}
if (children != null) {
e = children.elements();
while (e.hasMoreElements()) {
count += ((ThreadGroup)e.nextElement()).activeCount();
}
}
return count;
}
*/
public int enumerate(ThreadGroup[] list) {
if (children == null) return 0;
int max = list.length;
int i = children.size();
if(max > i){
max = i;
}
for(i=0 ; i < max ; i++){
list[i] = (ThreadGroup)children.get(i);
}
return max;
}
public int enumerate(ThreadGroup[] list, boolean recursive){
if(recursive){
//TODO also do this recursive !!!
return enumerate(list);
}
else {
return enumerate(list);
}
}
/**
** enumerate(Thread[] threads) --> enumerate(Thread[] threads, false).
** enumerate(Thread[] threads, boolean recurse) enumerates the Threads
** in this ThreadGroup only (recurse==false) or in this ThreadGroup
** and all its children, recursively. In both cases you have to kind
** of guess the size of the array, and the result might not mean anything
** very much ...
**/
public int enumerate(Thread[] threads) {
return enumerate(threads,false);
}
public int enumerate(Thread[] threads, boolean recurse) {
Enumeration et;
int i = 0;
/*
** First copy our own threads into the array.
*/
if (flock != null) {
et = flock.elements();
while (i<threads.length && et.hasMoreElements()) {
threads[i++] = (Thread)(et.nextElement());
}
}
if (!recurse) return i;
/*
** To enumerate recursively, iterate over all our children
** and call enumerate(...,true) on each. Append the results
** to what we already have: if the threads[] array gets full,
** pack up and go home early.
*/
if (children != null) {
Enumeration echild = children.elements();
while (i<threads.length && echild.hasMoreElements()) {
ThreadGroup nextTG = (ThreadGroup)(echild.nextElement());
Thread[] temp = new Thread[nextTG.activeCount()];
int l = enumerate(temp,true);
if (i+l>threads.length) {
System.arraycopy(temp,0,threads,i,threads.length-i);
return threads.length;
}
else {
System.arraycopy(temp,0,threads,i,l);
i += l;
}
}
}
return i;
}
/**
** getName()
** returns the name of this ThreadGroup.
*/
public final String getName() {
return name;
}
/**
** getParent()
** returns the parent of this ThreadGroup.
*/
public final ThreadGroup getParent() {
return parent;
}
/**
** isDestroyed()
** returns true iff destroy() has already been called on this ThreadGroup.
*/
public boolean isDestroyed() {
return destroyed;
}
/**
** parentOf(ThreadGroup group)
** returns true iff the group in question is a direct or indirect child
** of this ThreadGroup.
*/
public final boolean parentOf(ThreadGroup group) {
if (children == null) {
return false;
}
if (children.contains(group)) {
return true;
}
Enumeration e = children.elements();
while (e.hasMoreElements()) {
if (((ThreadGroup)e.nextElement()).parentOf(group)) {
return true;
}
}
return false;
}
/**
** stop()
** This deprecated method calls the stop() method of every thread in
** this group and in all of its child groups, recursively.
** The calling thread must have access permissions to modify this group.
*/
public final synchronized void stop()
throws SecurityException
{
Enumeration e;
checkAccess();
if (children != null) {
e = children.elements();
while (e.hasMoreElements()) {
((ThreadGroup)e.nextElement()).stop();
}
children = null;
}
if (flock != null) {
Thread ownthread = Thread.currentThread();
boolean suicide = false;
Vector snapshot = (Vector)flock.clone();
e = snapshot.elements();
while (e.hasMoreElements()) {
Thread t = (Thread)e.nextElement();
if (t == ownthread) {
suicide = true;
}
else {
t.stop();
}
}
if (suicide) {
ownthread.stop();
}
}
if (daemon && parent != null) {
parent.deregisterThreadGroup(this);
parent = null;
}
}
/**
** interrupt()
** This deprecated method calls the interrupt() method of every thread in
** this group and in all of its child groups, recursively.
** The calling thread must have access permissions to modify this group.
*/
public final synchronized void interrupt()
throws SecurityException
{
Enumeration e;
checkAccess();
if (children != null) {
e = children.elements();
while (e.hasMoreElements()) {
((ThreadGroup)e.nextElement()).interrupt();
}
children = null;
}
if (flock != null) {
Thread ownthread = Thread.currentThread();
Vector snapshot = (Vector)flock.clone();
e = snapshot.elements();
while (e.hasMoreElements()) {
Thread t = (Thread)e.nextElement();
if (t == ownthread) {
}
else {
t.interrupt();
}
}
}
}
/**
** suspend()
** This deprecated method calls the suspend() method of every thread in
** this group and in all of its child groups, recursively.
** Fails if destroy() was already called on this ThreadGroup.
** The calling thread must have access permissions to modify this group.
*/
public final synchronized void suspend()
throws SecurityException
{
checkAccess();
if(destroyed) {
throw new IllegalThreadStateException();
}
if (children != null) {
Enumeration e = children.elements();
while (e.hasMoreElements()) {
((ThreadGroup)e.nextElement()).suspend();
}
}
if (flock != null) {
Enumeration e = flock.elements();
while (e.hasMoreElements()) {
((Thread)e.nextElement()).suspend();
}
}
}
/**
** resume()
** This deprecated method calls the resume() method of every thread in
** this group and in all of its child groups, recursively.
** Fails if destroy() was already called on this ThreadGroup.
** The calling thread must have access permissions to modify this group.
*/
public final synchronized void resume()
throws SecurityException
{
checkAccess();
if(destroyed) {
throw new IllegalThreadStateException();
}
if (children != null) {
Enumeration e = children.elements();
while (e.hasMoreElements()) {
((ThreadGroup)e.nextElement()).suspend();
}
}
if (flock != null) {
Enumeration e = flock.elements();
while (e.hasMoreElements()) {
((Thread)e.nextElement()).suspend();
}
}
}
/**
** destroy()
** deregisters this ThreadGroup and all its direct and indirect children
** from their respective parents (thereby rendering them eligible for
** garbage collection). May only be called if
** - the calling thread has permission to modify this ThreadGroup;
** - destroy() was not already called on this ThreadGroup; and
** - none of the ThreadGroups affected contains any threads.
*/
public final synchronized void destroy()
throws SecurityException,
IllegalThreadStateException
{
checkAccess();
if (destroyed) {
throw new IllegalThreadStateException();
}
if (totalCount() != 0) {
throw new IllegalThreadStateException();
}
if (children != null) {
while (children.size()>0) {
synchronized (children) {
((ThreadGroup)children.elementAt(children.size()-1)).destroy();
}
}
}
destroyed = true;
if (parent != null) {
parent.deregisterThreadGroup(this);
parent = null;
}
}
public final int getMaxPriority() {
return maxPriority;
}
/**
** setMaxPriority(int newMaxPriority)
** checks its argument and the caller's permissions befor calling setMaxPriority0.
*/
public final void setMaxPriority(int newMaxPriority)
throws SecurityException, IllegalArgumentException
{
checkAccess();
if(newMaxPriority<Thread.MIN_PRIORITY || newMaxPriority>Thread.MAX_PRIORITY) {
return;
}
maxPriority = newMaxPriority;
}
/**
** isDaemon()
** returns the value of the `daemon' flag.
*/
public final boolean isDaemon() {
return daemon;
}
/**
** setDaemon()
** checks the caller's permissions before setting the `daemon' flag.
*/
public final void setDaemon(boolean daemon)
throws SecurityException
{
checkAccess();
this.daemon = daemon;
}
/**
** list()
** prints all the threads in this ThreadGroup and its descendants
** on System.out.
** Our implementation uses a private method list0(int indent) in
** order to give the desired indentation:
** java.lang.ThreadGroup[name=SystemThreadGroup,maxpri=1:]
** Thread[systemInitThread,5,SystemThreadGroup]
** java.lang.ThreadGroup...
*/
public void list() {
list0(0);
}
/**
** list0(int indent)
** prints out the name of this ThreadGroup (indented by 4*indent spaces),
** then the name of each member thread ((indented by 4*indent+4 spaces),
** and then calls list0(indent+1) on every direct child ThreadGroup.
*/
private void list0(int indent) {
int i;
StringBuffer b = new StringBuffer();
for (i=0; i<indent; ++i) b.append(" ");
b.append(this.toString());
System.out.println(b.toString());
if (flock != null) {
b.setLength(indent*4);
b.append(" ");
Enumeration e = flock.elements();
while (e.hasMoreElements()) {
for (i=0; i<indent*4; ++i) b.append(' ');
b.append(((Thread)e.nextElement()).toString());
System.out.println(b.toString());
b.setLength(indent*4+4);
}
}
if (children != null) {
Enumeration e = children.elements();
while (e.hasMoreElements()) {
((ThreadGroup)e.nextElement()).list0(indent+1);
}
}
}
/**
** uncaughtException(Thread t, Throwable e)
** handles exceptions thrown by any thread in this group and not caught
** by any exception handler in that thread's execution stack.
** If this method is not overridden, the exception will be passed all
** the way up to the systemThreadGroup and then a stack dump will be
** produced (unless the exception was ThreadDeath, in which case it is
** silently ignored).
*/
public void uncaughtException(Thread t, Throwable e) {
if(parent!=null) {
parent.uncaughtException(t,e);
} else {
if (!(e instanceof ThreadDeath)) {
e.printStackTrace(System.err);
if (e instanceof Error && "true".equalsIgnoreCase(System.systemProperties.getProperty("mika.terminate.on.error"))) {
System.err.println("VM terminated due to " + e);
System.exit(1);
}
}
}
// If t isn't a direct member of this TG then following does nothing
flock.remove(t);
checkForTermination();
}
public boolean allowThreadSuspension(boolean b) {
return false;
}
}