ThreadGroup.java 19.6 KB
Newer Older
1
/* java.lang.ThreadGroup
2
   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
Tom Tromey committed
3

4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */
37
 
Tom Tromey committed
38 39 40
package java.lang;

import java.util.Vector;
41
import java.util.Enumeration;
Tom Tromey committed
42 43 44

/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
 * "The Java Language Specification", ISBN 0-201-63451-1
45 46
 * plus online API docs for JDK 1.2 from http://www.javasoft.com.
 * Status:  Complete for 1.2.  Some parts from the JDK 1.0 spec only are
47 48 49 50 51 52 53 54 55 56 57
 * not implemented. 
 */
 
/**
 * ThreadGroup allows you to group Threads together.  There is a
 * hierarchy of ThreadGroups, and only the initial ThreadGroup has
 * no parent.  A Thread may access information about its own
 * ThreadGroup, but not its parents or others outside the tree.
 *
 * @author John Keiser
 * @author Tom Tromey
58 59
 * @author Bryce McKinlay
 * @version 1.2.0
60
 * @since JDK1.0
Tom Tromey committed
61 62 63 64
 */

public class ThreadGroup
{
65 66
  /* The Initial, top-level ThreadGroup. */
  static ThreadGroup root = new ThreadGroup();
67 68 69
  /* This flag is set if an uncaught exception occurs. The runtime should 
  check this and exit with an error status if it is set. */
  static boolean had_uncaught_exception = false;
Tom Tromey committed
70

71 72 73 74 75
  private ThreadGroup parent;
  private String name;
  private Vector threads = new Vector();
  private Vector groups = new Vector();
  private boolean daemon_flag = false;
76
  private int maxpri = Thread.MAX_PRIORITY;
Tom Tromey committed
77

78
  private ThreadGroup()
Tom Tromey committed
79
  {
80
    name = "main";    
Tom Tromey committed
81 82
  }

83 84 85 86 87
  /** Create a new ThreadGroup using the given name and the
   *  current thread's ThreadGroup as a parent.
   *  @param name the name to use for the ThreadGroup.
   */
  public ThreadGroup(String name)
Tom Tromey committed
88
  {
89
    this (Thread.currentThread().getThreadGroup(), name);
Tom Tromey committed
90 91
  }

92 93 94 95 96 97 98 99 100
  /** Create a new ThreadGroup using the given name and
   *  parent group.
   *  @param name the name to use for the ThreadGroup.
   *  @param parent the ThreadGroup to use as a parent.
   *  @exception NullPointerException if parent is null.
   *  @exception SecurityException if you cannot change
   *             the intended parent group.
   */
  public ThreadGroup(ThreadGroup parent, String name)
Tom Tromey committed
101
  {
102 103
    parent.checkAccess();
    this.parent = parent;
104
    if (parent.isDestroyed())
105 106 107 108
      throw new IllegalArgumentException ();
    this.name = name;
    maxpri = parent.maxpri;
    daemon_flag = parent.daemon_flag;
109
    parent.addGroup(this);
Tom Tromey committed
110 111
  }

112 113 114 115
  /** Get the name of this ThreadGroup.
   *  @return the name of this ThreadGroup.
   */
  public final String getName()
Tom Tromey committed
116
  {
117
    return name;
Tom Tromey committed
118 119
  }

120 121 122 123
  /** Get the parent of this ThreadGroup.
   *  @return the parent of this ThreadGroup.
   */
  public final ThreadGroup getParent()
Tom Tromey committed
124
  {
125
    return parent;
Tom Tromey committed
126 127
  }

128 129 130 131 132 133 134
  /** Set the maximum priority for Threads in this ThreadGroup. setMaxPriority
   *  can only be used to reduce the current maximum. If maxpri
   *  is greater than the current Maximum, the current value is not changed.
   *  Calling this does not effect threads already in this ThreadGroup.
   *  @param maxpri the new maximum priority for this ThreadGroup.
   *  @exception SecurityException if you cannoy modify this ThreadGroup.
   */
135
  public final synchronized void setMaxPriority(int maxpri)
Tom Tromey committed
136
  {
137 138 139 140
    checkAccess();
    if (maxpri < this.maxpri
        && maxpri >= Thread.MIN_PRIORITY
	&& maxpri <= Thread.MAX_PRIORITY)
Tom Tromey committed
141
      {
142 143
	this.maxpri = maxpri;        
      }  
Tom Tromey committed
144 145
  }

146 147 148 149
  /** Get the maximum priority of Threads in this ThreadGroup.
   *  @return the maximum priority of Threads in this ThreadGroup.
   */
  public final int getMaxPriority()
Tom Tromey committed
150
  {
151
    return maxpri;
Tom Tromey committed
152 153
  }

154 155 156 157 158 159 160 161 162 163
  /** Set whether this ThreadGroup is a daemon group.  A daemon
   *  group will be destroyed when its last thread is stopped and
   *  its last thread group is destroyed.
   *  @specnote The Java API docs indicate that the group is destroyed
   * 		when either of those happen, but that doesn't make
   * 		sense.
   *  @param daemon whether this ThreadGroup should be a daemon group.
   *  @exception SecurityException if you cannoy modify this ThreadGroup.
   */
  public final void setDaemon (boolean daemon)
Tom Tromey committed
164
  {
165 166
    checkAccess();
    daemon_flag = daemon;
Tom Tromey committed
167
  }
168 169 170 171 172 173 174 175 176 177
   
  /** Tell whether this ThreadGroup is a daemon group.  A daemon
    * group will be destroyed when its last thread is stopped and
    * its last thread group is destroyed.
    * @specnote The Java API docs indicate that the group is destroyed
    *		when either of those happen, but that doesn't make
    *		sense.
    * @return whether this ThreadGroup is a daemon group.
    */
  public final boolean isDaemon()
Tom Tromey committed
178
  {
179
    return daemon_flag;
Tom Tromey committed
180 181
  }

182 183 184
  /** Tell whether this ThreadGroup has been destroyed or not.
    * @return whether this ThreadGroup has been destroyed or not.
    */
185
  public synchronized boolean isDestroyed()
Tom Tromey committed
186
  {
187
    return parent == null && this != root;
Tom Tromey committed
188 189
  }

190 191 192 193 194 195 196 197
  /** Check whether this ThreadGroup is an ancestor of the
    * specified ThreadGroup, or if they are the same.
    *
    * @param g the group to test on.
    * @return whether this ThreadGroup is a parent of the
    *	      specified group.
    */
  public final boolean parentOf(ThreadGroup tg)
Tom Tromey committed
198
  {
199
    while (tg != null)
Tom Tromey committed
200
      {
201 202 203
        if (tg == this)
          return true;
        tg = tg.parent;
Tom Tromey committed
204
      }
205
    return false;
Tom Tromey committed
206 207
  }

208 209 210 211 212 213 214 215 216
  /** Return the total number of active threads in this ThreadGroup
    * and all its descendants.<P>
    *
    * This cannot return an exact number, since the status of threads
    * may change after they were counted.  But it should be pretty
    * close.<P>
    *
    * @return the number of active threads in this ThreadGroup and
    *	      its descendants.
217
    * @specnote it isn't clear what the definition of an "Active" thread is.
218 219 220
    *           Current JDKs regard a thread as active if has been
    *           started and not finished.  We implement this behaviour.
    *           There is a JDC bug, <A HREF="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html">
221 222
    *           4089701</A>, regarding this issue.
    *           
223 224 225
    */
  public synchronized int activeCount()
  {
226 227 228 229 230 231 232
    int total = 0;
    for (int i = 0; i < threads.size(); ++i)
      {
	if (((Thread) threads.elementAt(i)).isAlive ())
	  ++total;
      }

233 234 235 236 237 238
    for (int i=0; i < groups.size(); i++)
      {
        ThreadGroup g = (ThreadGroup) groups.elementAt(i);
        total += g.activeCount();
      }
    return total;
Tom Tromey committed
239 240
  }

241 242 243 244
  /** Get the number of active groups in this ThreadGroup.  This group
    * itself is not included in the count.
    * @specnote it is unclear what exactly constitutes an
    *		active ThreadGroup.  Currently we assume that
245
    *		all sub-groups are active, per current JDKs.
246 247
    * @return the number of active groups in this ThreadGroup.
    */
248
  public synchronized int activeGroupCount()
Tom Tromey committed
249
  {
250 251 252 253 254
    int total = groups.size();
    for (int i=0; i < groups.size(); i++)
      {
	ThreadGroup g = (ThreadGroup) groups.elementAt(i);
	total += g.activeGroupCount();
255
      }
256
    return total;
Tom Tromey committed
257 258
  }

259 260 261 262 263 264 265 266 267
  /** Copy all of the active Threads from this ThreadGroup and
    * its descendants into the specified array.  If the array is
    * not big enough to hold all the Threads, extra Threads will
    * simply not be copied.
    *
    * @param threads the array to put the threads into.
    * @return the number of threads put into the array.
    */
  public int enumerate(Thread[] threads)
Tom Tromey committed
268
  {
269
    return enumerate(threads, 0, true);
Tom Tromey committed
270 271
  }

272 273 274 275 276 277 278 279 280 281 282
  /** Copy all of the active Threads from this ThreadGroup and,
    * if desired, from its descendants, into the specified array.
    * If the array is not big enough to hold all the Threads,
    * extra Threads will simply not be copied.
    *
    * @param threads the array to put the threads into.
    * @param useDescendants whether to count Threads in this
    *	     ThreadGroup's descendants or not.
    * @return the number of threads put into the array.
    */
  public int enumerate(Thread[] threads, boolean useDescendants)
Tom Tromey committed
283
  {
284
    return enumerate(threads, 0, useDescendants);
Tom Tromey committed
285 286
  }

287
  // This actually implements enumerate.
288 289
  private synchronized int enumerate(Thread[] list, int next_index, 
				     boolean recurse)
Tom Tromey committed
290
  {
291 292
    Enumeration e = threads.elements();
    while (e.hasMoreElements() && next_index < list.length)
293 294 295 296 297
      {
	Thread t = (Thread) e.nextElement();
	if (t.isAlive ())
	  list[next_index++] = t;
      }
298 299 300 301 302 303 304 305 306 307
    if (recurse && next_index != list.length)
      {
	e = groups.elements();
	while (e.hasMoreElements() && next_index < list.length)
	  {
	    ThreadGroup g = (ThreadGroup) e.nextElement();
	    next_index = g.enumerate(list, next_index, true);
	  }
      }
    return next_index;
Tom Tromey committed
308 309
  }

310 311 312 313 314 315 316 317 318 319
  /** Copy all active ThreadGroups that are descendants of this
    * ThreadGroup into the specified array.  If the array is not
    * large enough to hold all active ThreadGroups, extra
    * ThreadGroups simply will not be copied.
    *
    * @param groups the array to put the ThreadGroups into.
    * @return the number of ThreadGroups copied into the array.
    */
  public int enumerate(ThreadGroup[] groups)
  {
320
    return enumerate(groups, 0, true);
321
  }
Tom Tromey committed
322

323 324 325 326 327 328 329
  /** Copy all active ThreadGroups that are children of this
    * ThreadGroup into the specified array, and if desired, also
    * copy all active descendants into the array.  If the array
    * is not large enough to hold all active ThreadGroups, extra
    * ThreadGroups simply will not be copied.
    *
    * @param groups the array to put the ThreadGroups into.
330
    * @param recurse whether to include all descendants
331 332 333 334
    *	     of this ThreadGroup's children in determining
    *	     activeness.
    * @return the number of ThreadGroups copied into the array.
    */
335
  public int enumerate(ThreadGroup[] groups, boolean recurse)
Tom Tromey committed
336
  {
337
    return enumerate(groups, 0, recurse);
Tom Tromey committed
338 339
  }

340
  // This actually implements enumerate.
341 342
  private synchronized int enumerate (ThreadGroup[] list, int next_index, 
				      boolean recurse)
Tom Tromey committed
343
  {
344 345 346 347 348 349 350 351 352
    Enumeration e = groups.elements();
    while (e.hasMoreElements() && next_index < list.length)
      {
	ThreadGroup g = (ThreadGroup) e.nextElement();
	list[next_index++] = g;
	if (recurse && next_index != list.length)
	  next_index = g.enumerate(list, next_index, true);
      }
    return next_index;
Tom Tromey committed
353 354
  }

355 356 357 358 359 360
  /** Interrupt all Threads in this ThreadGroup and its sub-groups.
    * @exception SecurityException if you cannot modify this
    *		 ThreadGroup or any of its Threads or children
    *		 ThreadGroups.
    * @since JDK1.2
    */
361
  public final synchronized void interrupt()
Tom Tromey committed
362
  {
363 364
    checkAccess();
    for (int i=0; i < threads.size(); i++)
Tom Tromey committed
365
      {
366 367
        Thread t = (Thread) threads.elementAt(i);
        t.interrupt();
Tom Tromey committed
368
      }
369
    for (int i=0; i < groups.size(); i++)
Tom Tromey committed
370
      {
371 372
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
        tg.interrupt();
Tom Tromey committed
373 374 375
      }
  }

376 377 378 379 380 381
  /** Stop all Threads in this ThreadGroup and its descendants.
    * @exception SecurityException if you cannot modify this
    *		 ThreadGroup or any of its Threads or children
    *		 ThreadGroups.
    * @deprecated This method calls Thread.stop(), which is dangerous.
    */
382
  public final synchronized void stop()
Tom Tromey committed
383
  {
384 385 386 387 388 389 390 391 392 393 394
    checkAccess();
    for (int i=0; i<threads.size(); i++)
      {
        Thread t = (Thread) threads.elementAt(i);
	t.stop();
      }
    for (int i=0; i < groups.size(); i++)
      {
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
        tg.stop();
      }
Tom Tromey committed
395 396
  }

397 398 399 400 401 402
  /** Suspend all Threads in this ThreadGroup and its descendants.
    * @exception SecurityException if you cannot modify this
    *		 ThreadGroup or any of its Threads or children
    *		 ThreadGroups.
    * @deprecated This method calls Thread.suspend(), which is dangerous.
    */
403
  public final synchronized void suspend()
Tom Tromey committed
404
  {
405 406
    checkAccess();
    for (int i=0; i<threads.size(); i++)
Tom Tromey committed
407
      {
408 409 410 411 412 413 414
        Thread t = (Thread) threads.elementAt(i);
        t.suspend();
      }
    for (int i=0; i < groups.size(); i++)
      {
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
        tg.suspend();
Tom Tromey committed
415 416 417
      }
  }

418 419 420 421 422 423
  /** Resume all Threads in this ThreadGroup and its descendants.
    * @exception SecurityException if you cannot modify this
    *		 ThreadGroup or any of its Threads or children
    *		 ThreadGroups.
    * @deprecated This method relies on Thread.suspend(), which is dangerous.
    */
424
  public final synchronized void resume()
Tom Tromey committed
425
  {
426 427
    checkAccess();
    for (int i=0; i < threads.size(); i++)
Tom Tromey committed
428
      {
429
        Thread t = (Thread) threads.elementAt(i);
Tom Tromey committed
430 431
	t.resume();
      }
432
    for (int i=0; i < groups.size(); i++)
Tom Tromey committed
433
      {
434 435
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
        tg.resume();
Tom Tromey committed
436 437 438
      }
  }

439
  // This is a helper that is used to implement the destroy method.
440
  private synchronized void checkDestroy ()
Tom Tromey committed
441
  {
442 443 444 445 446 447 448
    if (! threads.isEmpty())
      throw new IllegalThreadStateException ("ThreadGroup has threads");
    for (int i=0; i < groups.size(); i++)
      {
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
	tg.checkDestroy();
      }
Tom Tromey committed
449 450
  }

451 452 453 454 455 456 457 458 459
  /** Destroy this ThreadGroup.  There can be no Threads in it,
    * and none of its descendants (sub-groups) may have Threads in them.
    * All its descendants will be destroyed as well.
    * @exception IllegalThreadStateException if the ThreadGroup or
    *		 its descendants have Threads remaining in them, or
    *		 if the ThreadGroup in question is already destroyed.
    * @exception SecurityException if you cannot modify this
    *		 ThreadGroup or any of its descendants.
    */
460
  public final synchronized void destroy()
Tom Tromey committed
461
  {
462
    checkAccess();
463
    if (isDestroyed())
464 465 466
      throw new IllegalThreadStateException("Already destroyed.");
    checkDestroy ();
    if (parent != null)
467
      parent.removeGroup(this);
468
    parent = null;
Tom Tromey committed
469

470
    for (int i=0; i < groups.size(); i++)
Tom Tromey committed
471
      {
472 473
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
	tg.destroy();
Tom Tromey committed
474 475
      }
  }
476 477 478 479 480 481 482
  
  /** Print out information about this ThreadGroup to System.out.
    */
  public void list()
  {
    list("");
  }
Tom Tromey committed
483

484
  private synchronized void list(String indentation)
Tom Tromey committed
485
  {
486 487 488 489
    System.out.print(indentation);
    System.out.println(toString ());
    String sub = indentation + "    ";
    for (int i=0; i < threads.size(); i++)
Tom Tromey committed
490
      {
491 492 493
        Thread t = (Thread) threads.elementAt(i);
	System.out.print(sub);
	System.out.println(t.toString());
Tom Tromey committed
494
      }
495
    for (int i=0; i < groups.size(); i++)
Tom Tromey committed
496
      {
497 498
        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
	tg.list(sub);
Tom Tromey committed
499 500 501
      }
  }

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
  /** When a Thread in this ThreadGroup does not catch an exception,
    * this method of the ThreadGroup is called.<P>
    *
    * ThreadGroup's implementation does the following:<BR>
    * <OL>
    * <LI>If there is a parent ThreadGroup, call uncaughtException()
    *	  in the parent.</LI>
    * <LI>If the Throwable passed is a ThreadDeath, don't do
    *	  anything.</LI>
    * <LI>Otherwise, call <CODE>exception.printStackTrace().</CODE></LI>
    * </OL>
    *
    * @param thread the thread that exited.
    * @param exception the uncaught exception.
    */
  public void uncaughtException(Thread thread, Throwable t)
Tom Tromey committed
518
  {
519 520 521
    if (parent != null)
      parent.uncaughtException (thread, t);
    else if (! (t instanceof ThreadDeath))
522
      {
523
        if (thread != null)
524 525
          System.err.print ("Exception in thread \""
			    + thread.getName() + "\" ");
526 527
	try
	  {
528
	    t.printStackTrace(System.err);
529 530 531 532 533 534
	  }
	catch (Throwable x)
	  {
	    // This means that something is badly screwed up with the runtime,
	    // or perhaps someone is messing with the SecurityManager. In any
	    // case, try to deal with it gracefully.
535
	    System.err.println(t);
536 537 538
	    System.err.println("*** Got " + x.toString() + 
			       " while trying to print stack trace");
	  }
539 540
	had_uncaught_exception = true;
      }
Tom Tromey committed
541 542
  }

543 544 545 546 547 548 549 550 551
  /** Tell the VM whether it may suspend Threads in low memory
    * situations.
    * @deprecated This method is unimplemented, because it would rely on
    *		  suspend(), which is deprecated. There is no way for a Java
    *		  program to determine whether this has any effect whatsoever,
    *		  so we don't need it.
    * @return false
    */
  public boolean allowThreadSuspension(boolean allow)
Tom Tromey committed
552
  {
553
    return false;
Tom Tromey committed
554 555
  }

556 557 558 559 560 561 562 563
  /** Get a human-readable representation of this ThreadGroup.
    * @return a String representing this ThreadGroup.
    * @specnote Language Spec and Class Libraries book disagree a bit here.
    *		We follow the Spec, but add "ThreadGroup" per the book.  We
    *		include "java.lang" based on the list() example in the Class
    *		Libraries book.
    */
  public String toString ()
Tom Tromey committed
564
  {
565 566
    return "java.lang.ThreadGroup[name=" + name + 
           ",maxpri=" + maxpri + "]";
Tom Tromey committed
567 568
  }

569 570 571 572 573 574 575
  /** Find out if the current Thread can modify this ThreadGroup.
    * Calls the current SecurityManager's checkAccess() method to
    * find out.  If there is none, it assumes everything's OK.
    * @exception SecurityException if the current Thread cannot
    *		 modify this ThreadGroup.
    */
  public final void checkAccess()
Tom Tromey committed
576
  {
577 578 579 580
    SecurityManager sm = System.getSecurityManager();
    if (sm != null)
      sm.checkAccess(this);
  }
Tom Tromey committed
581

582
  // This is called to add a Thread to our internal list.
583
  final synchronized void addThread(Thread t)
584
  {
585
    if (isDestroyed())
586 587 588
      throw new IllegalThreadStateException ("ThreadGroup is destroyed");
  
    threads.addElement(t);
Tom Tromey committed
589 590
  }

591
  // This is called to remove a Thread from our internal list.
592
  final synchronized void removeThread(Thread t)
Tom Tromey committed
593
  {
594
    if (isDestroyed())
595 596 597 598 599 600 601 602
      throw new IllegalThreadStateException ();
  
    threads.removeElement(t);
    // Daemon groups are automatically destroyed when all their threads die.
    if (daemon_flag && groups.size() == 0 && threads.size() == 0)
      {
	// We inline destroy to avoid the access check.
	if (parent != null)
603
	  parent.removeGroup(this);
604
	parent = null;
605
      }
Tom Tromey committed
606 607
  }

608
  // This is called to add a ThreadGroup to our internal list.
609
  final synchronized void addGroup(ThreadGroup g)
Tom Tromey committed
610
  {
611
    groups.addElement(g);
Tom Tromey committed
612 613
  }

614
  // This is called to remove a ThreadGroup from our internal list.
615
  final synchronized void removeGroup(ThreadGroup g)
Tom Tromey committed
616
  {
617 618 619
    groups.removeElement(g);
    // Daemon groups are automatically destroyed when all their threads die.
    if (daemon_flag && groups.size() == 0 && threads.size() == 0)
Tom Tromey committed
620
      {
621 622
	// We inline destroy to avoid the access check.
	if (parent != null)
623 624
	  parent.removeGroup(this);
	parent = null;
Tom Tromey committed
625 626 627
      }
  }
}