Source for java.lang.ThreadGroup

   1: /* ThreadGroup -- a group of Threads
   2:    Copyright (C) 1998, 2000, 2001, 2002, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package java.lang;
  39: 
  40: import java.util.Vector;
  41: 
  42: /**
  43:  * ThreadGroup allows you to group Threads together.  There is a hierarchy
  44:  * of ThreadGroups, and only the initial ThreadGroup has no parent.  A Thread
  45:  * may access information about its own ThreadGroup, but not its parents or
  46:  * others outside the tree.
  47:  *
  48:  * @author John Keiser
  49:  * @author Tom Tromey
  50:  * @author Bryce McKinlay
  51:  * @author Eric Blake (ebb9@email.byu.edu)
  52:  * @see Thread
  53:  * @since 1.0
  54:  * @status updated to 1.4
  55:  */
  56: public class ThreadGroup
  57: {
  58:   /** The Initial, top-level ThreadGroup. */
  59:   static ThreadGroup root = new ThreadGroup();
  60: 
  61:   /**
  62:    * This flag is set if an uncaught exception occurs. The runtime should
  63:    * check this and exit with an error status if it is set.
  64:    */
  65:   static boolean had_uncaught_exception;
  66: 
  67:   /** The parent thread group. */
  68:   private final ThreadGroup parent;
  69: 
  70:   /** The group name, non-null. */
  71:   final String name;
  72: 
  73:   /** The threads in the group. */
  74:   private final Vector threads = new Vector();
  75: 
  76:   /** Child thread groups, or null when this group is destroyed. */
  77:   private Vector groups = new Vector();
  78: 
  79:   /** If all threads in the group are daemons. */
  80:   private boolean daemon_flag = false;
  81: 
  82:   /** The maximum group priority. */
  83:   private int maxpri;
  84: 
  85:   /**
  86:    * Hidden constructor to build the root node.
  87:    */
  88:   private ThreadGroup()
  89:   {
  90:     name = "main";
  91:     parent = null;
  92:     maxpri = Thread.MAX_PRIORITY;
  93:   }
  94: 
  95:   /**
  96:    * Create a new ThreadGroup using the given name and the current thread's
  97:    * ThreadGroup as a parent. There may be a security check,
  98:    * <code>checkAccess</code>.
  99:    *
 100:    * @param name the name to use for the ThreadGroup
 101:    * @throws SecurityException if the current thread cannot create a group
 102:    * @see #checkAccess()
 103:    */
 104:   public ThreadGroup(String name)
 105:   {
 106:     this(Thread.currentThread().group, name);
 107:   }
 108: 
 109:   /**
 110:    * Create a new ThreadGroup using the given name and parent group. The new
 111:    * group inherits the maximum priority and daemon status of its parent
 112:    * group. There may be a security check, <code>checkAccess</code>.
 113:    *
 114:    * @param name the name to use for the ThreadGroup
 115:    * @param parent the ThreadGroup to use as a parent
 116:    * @throws NullPointerException if parent is null
 117:    * @throws SecurityException if the current thread cannot create a group
 118:    * @throws IllegalThreadStateException if the parent is destroyed
 119:    * @see #checkAccess()
 120:    */
 121:   public ThreadGroup(ThreadGroup parent, String name)
 122:   {
 123:     parent.checkAccess();
 124:     this.parent = parent;
 125:     this.name = name;
 126:     maxpri = parent.maxpri;
 127:     daemon_flag = parent.daemon_flag;
 128:     synchronized (parent)
 129:       {
 130:         if (parent.groups == null)
 131:           throw new IllegalThreadStateException();
 132:         parent.groups.add(this);
 133:       }
 134:   }
 135: 
 136:   /**
 137:    * Get the name of this ThreadGroup.
 138:    *
 139:    * @return the name of this ThreadGroup
 140:    */
 141:   public final String getName()
 142:   {
 143:     return name;
 144:   }
 145: 
 146:   /**
 147:    * Get the parent of this ThreadGroup. If the parent is not null, there
 148:    * may be a security check, <code>checkAccess</code>.
 149:    *
 150:    * @return the parent of this ThreadGroup
 151:    * @throws SecurityException if permission is denied
 152:    */
 153:   public final ThreadGroup getParent()
 154:   {
 155:     if (parent != null)
 156:       parent.checkAccess();
 157:     return parent;
 158:   }
 159: 
 160:   /**
 161:    * Get the maximum priority of Threads in this ThreadGroup. Threads created
 162:    * after this call in this group may not exceed this priority.
 163:    *
 164:    * @return the maximum priority of Threads in this ThreadGroup
 165:    */
 166:   public final int getMaxPriority()
 167:   {
 168:     return maxpri;
 169:   }
 170: 
 171:   /**
 172:    * Tell whether this ThreadGroup is a daemon group.  A daemon group will
 173:    * be automatically destroyed when its last thread is stopped and
 174:    * its last thread group is destroyed.
 175:    *
 176:    * @return whether this ThreadGroup is a daemon group
 177:    */
 178:   public final boolean isDaemon()
 179:   {
 180:     return daemon_flag;
 181:   }
 182: 
 183:   /**
 184:    * Tell whether this ThreadGroup has been destroyed or not.
 185:    *
 186:    * @return whether this ThreadGroup has been destroyed or not
 187:    * @since 1.1
 188:    */
 189:   public synchronized boolean isDestroyed()
 190:   {
 191:     return groups == null;
 192:   }
 193: 
 194:   /**
 195:    * Set whether this ThreadGroup is a daemon group.  A daemon group will be
 196:    * destroyed when its last thread is stopped and its last thread group is
 197:    * destroyed. There may be a security check, <code>checkAccess</code>.
 198:    *
 199:    * @param daemon whether this ThreadGroup should be a daemon group
 200:    * @throws SecurityException if you cannot modify this ThreadGroup
 201:    * @see #checkAccess()
 202:    */
 203:   public final void setDaemon(boolean daemon)
 204:   {
 205:     checkAccess();
 206:     daemon_flag = daemon;
 207:   }
 208: 
 209:   /**
 210:    * Set the maximum priority for Threads in this ThreadGroup. setMaxPriority
 211:    * can only be used to reduce the current maximum. If maxpri is greater
 212:    * than the current Maximum of the parent group, the current value is not
 213:    * changed. Otherwise, all groups which belong to this have their priority
 214:    * adjusted as well. Calling this does not affect threads already in this
 215:    * ThreadGroup. There may be a security check, <code>checkAccess</code>.
 216:    *
 217:    * @param maxpri the new maximum priority for this ThreadGroup
 218:    * @throws SecurityException if you cannot modify this ThreadGroup
 219:    * @see #getMaxPriority()
 220:    * @see #checkAccess()
 221:    */
 222:   public final synchronized void setMaxPriority(int maxpri)
 223:   {
 224:     checkAccess();
 225:     if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY)
 226:       return;
 227:     if (parent != null && maxpri > parent.maxpri)
 228:       maxpri = parent.maxpri;
 229:     this.maxpri = maxpri;
 230:     if (groups == null)
 231:       return;
 232:     int i = groups.size();
 233:     while (--i >= 0)
 234:       ((ThreadGroup) groups.get(i)).setMaxPriority(maxpri);
 235:   }
 236: 
 237:   /**
 238:    * Check whether this ThreadGroup is an ancestor of the specified
 239:    * ThreadGroup, or if they are the same.
 240:    *
 241:    * @param group the group to test on
 242:    * @return whether this ThreadGroup is a parent of the specified group
 243:    */
 244:   public final boolean parentOf(ThreadGroup group)
 245:   {
 246:     while (group != null)
 247:       {
 248:         if (group == this)
 249:           return true;
 250:         group = group.parent;
 251:       }
 252:     return false;
 253:   }
 254: 
 255:   /**
 256:    * Find out if the current Thread can modify this ThreadGroup. This passes
 257:    * the check on to <code>SecurityManager.checkAccess(this)</code>.
 258:    *
 259:    * @throws SecurityException if the current Thread cannot modify this
 260:    *         ThreadGroup
 261:    * @see SecurityManager#checkAccess(ThreadGroup)
 262:    */
 263:   public final void checkAccess()
 264:   {
 265:     // Bypass System.getSecurityManager, for bootstrap efficiency.
 266:     SecurityManager sm = SecurityManager.current;
 267:     if (sm != null)
 268:       sm.checkAccess(this);
 269:   }
 270: 
 271:   /**
 272:    * Return an estimate of the total number of active threads in this
 273:    * ThreadGroup and all its descendants. This cannot return an exact number,
 274:    * since the status of threads may change after they were counted; but it
 275:    * should be pretty close. Based on a JDC bug,
 276:    * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html">
 277:    * 4089701</a>, we take active to mean isAlive().
 278:    *
 279:    * @return count of active threads in this ThreadGroup and its descendants
 280:    */
 281:   public int activeCount()
 282:   {
 283:     int total = 0;
 284:     if (groups == null)
 285:       return total;
 286:     int i = threads.size();
 287:     while (--i >= 0)
 288:       if (((Thread) threads.get(i)).isAlive())
 289:         total++;
 290:     i = groups.size();
 291:     while (--i >= 0)
 292:       total += ((ThreadGroup) groups.get(i)).activeCount();
 293:     return total;
 294:   }
 295: 
 296:   /**
 297:    * Copy all of the active Threads from this ThreadGroup and its descendants
 298:    * into the specified array.  If the array is not big enough to hold all
 299:    * the Threads, extra Threads will simply not be copied. There may be a
 300:    * security check, <code>checkAccess</code>.
 301:    *
 302:    * @param array the array to put the threads into
 303:    * @return the number of threads put into the array
 304:    * @throws SecurityException if permission was denied
 305:    * @throws NullPointerException if array is null
 306:    * @throws ArrayStoreException if a thread does not fit in the array
 307:    * @see #activeCount()
 308:    * @see #checkAccess()
 309:    * @see #enumerate(Thread[], boolean)
 310:    */
 311:   public int enumerate(Thread[] array)
 312:   {
 313:     return enumerate(array, 0, true);
 314:   }
 315: 
 316:   /**
 317:    * Copy all of the active Threads from this ThreadGroup and, if desired,
 318:    * from its descendants, into the specified array. If the array is not big
 319:    * enough to hold all the Threads, extra Threads will simply not be copied.
 320:    * There may be a security check, <code>checkAccess</code>.
 321:    *
 322:    * @param array the array to put the threads into
 323:    * @param recurse whether to recurse into descendent ThreadGroups
 324:    * @return the number of threads put into the array
 325:    * @throws SecurityException if permission was denied
 326:    * @throws NullPointerException if array is null
 327:    * @throws ArrayStoreException if a thread does not fit in the array
 328:    * @see #activeCount()
 329:    * @see #checkAccess()
 330:    */
 331:   public int enumerate(Thread[] array, boolean recurse)
 332:   {
 333:     return enumerate(array, 0, recurse);
 334:   }
 335: 
 336:   /**
 337:    * Get the number of active groups in this ThreadGroup.  This group itself
 338:    * is not included in the count. A sub-group is active if it has not been
 339:    * destroyed. This cannot return an exact number, since the status of
 340:    * threads may change after they were counted; but it should be pretty close.
 341:    *
 342:    * @return the number of active groups in this ThreadGroup
 343:    */
 344:   public int activeGroupCount()
 345:   {
 346:     if (groups == null)
 347:       return 0;
 348:     int total = groups.size();
 349:     int i = total;
 350:     while (--i >= 0)
 351:       total += ((ThreadGroup) groups.get(i)).activeGroupCount();
 352:     return total;
 353:   }
 354: 
 355:   /**
 356:    * Copy all active ThreadGroups that are descendants of this ThreadGroup
 357:    * into the specified array.  If the array is not large enough to hold all
 358:    * active ThreadGroups, extra ThreadGroups simply will not be copied. There
 359:    * may be a security check, <code>checkAccess</code>.
 360:    *
 361:    * @param array the array to put the ThreadGroups into
 362:    * @return the number of ThreadGroups copied into the array
 363:    * @throws SecurityException if permission was denied
 364:    * @throws NullPointerException if array is null
 365:    * @throws ArrayStoreException if a group does not fit in the array
 366:    * @see #activeCount()
 367:    * @see #checkAccess()
 368:    * @see #enumerate(ThreadGroup[], boolean)
 369:    */
 370:   public int enumerate(ThreadGroup[] array)
 371:   {
 372:     return enumerate(array, 0, true);
 373:   }
 374: 
 375:   /**
 376:    * Copy all active ThreadGroups that are children of this ThreadGroup into
 377:    * the specified array, and if desired, also all descendents.  If the array
 378:    * is not large enough to hold all active ThreadGroups, extra ThreadGroups
 379:    * simply will not be copied. There may be a security check,
 380:    * <code>checkAccess</code>.
 381:    *
 382:    * @param array the array to put the ThreadGroups into
 383:    * @param recurse whether to recurse into descendent ThreadGroups
 384:    * @return the number of ThreadGroups copied into the array
 385:    * @throws SecurityException if permission was denied
 386:    * @throws NullPointerException if array is null
 387:    * @throws ArrayStoreException if a group does not fit in the array
 388:    * @see #activeCount()
 389:    * @see #checkAccess()
 390:    */
 391:   public int enumerate(ThreadGroup[] array, boolean recurse)
 392:   {
 393:     return enumerate(array, 0, recurse);
 394:   }
 395: 
 396:   /**
 397:    * Stop all Threads in this ThreadGroup and its descendants.
 398:    *
 399:    * <p>This is inherently unsafe, as it can interrupt synchronized blocks and
 400:    * leave data in bad states.  Hence, there is a security check:
 401:    * <code>checkAccess()</code>, followed by further checks on each thread
 402:    * being stopped.
 403:    *
 404:    * @throws SecurityException if permission is denied
 405:    * @see #checkAccess()
 406:    * @see Thread#stop(Throwable)
 407:    * @deprecated unsafe operation, try not to use
 408:    */
 409:   public final synchronized void stop()
 410:   {
 411:     checkAccess();
 412:     if (groups == null)
 413:       return;
 414:     int i = threads.size();
 415:     while (--i >= 0)
 416:       ((Thread) threads.get(i)).stop();
 417:     i = groups.size();
 418:     while (--i >= 0)
 419:       ((ThreadGroup) groups.get(i)).stop();
 420:   }
 421: 
 422:   /**
 423:    * Interrupt all Threads in this ThreadGroup and its sub-groups. There may
 424:    * be a security check, <code>checkAccess</code>.
 425:    *
 426:    * @throws SecurityException if permission is denied
 427:    * @see #checkAccess()
 428:    * @see Thread#interrupt()
 429:    * @since 1.2
 430:    */
 431:   public final synchronized void interrupt()
 432:   {
 433:     checkAccess();
 434:     if (groups == null)
 435:       return;
 436:     int i = threads.size();
 437:     while (--i >= 0)
 438:       ((Thread) threads.get(i)).interrupt();
 439:     i = groups.size();
 440:     while (--i >= 0)
 441:       ((ThreadGroup) groups.get(i)).interrupt();
 442:   }
 443: 
 444:   /**
 445:    * Suspend all Threads in this ThreadGroup and its descendants.
 446:    *
 447:    * <p>This is inherently unsafe, as suspended threads still hold locks,
 448:    * which can lead to deadlock.  Hence, there is a security check:
 449:    * <code>checkAccess()</code>, followed by further checks on each thread
 450:    * being suspended.
 451:    *
 452:    * @throws SecurityException if permission is denied
 453:    * @see #checkAccess()
 454:    * @see Thread#suspend()
 455:    * @deprecated unsafe operation, try not to use
 456:    */
 457:   public final synchronized void suspend()
 458:   {
 459:     checkAccess();
 460:     if (groups == null)
 461:       return;
 462:     int i = threads.size();
 463:     while (--i >= 0)
 464:       ((Thread) threads.get(i)).suspend();
 465:     i = groups.size();
 466:     while (--i >= 0)
 467:       ((ThreadGroup) groups.get(i)).suspend();
 468:   }
 469: 
 470:   /**
 471:    * Resume all suspended Threads in this ThreadGroup and its descendants.
 472:    * To mirror suspend(), there is a security check:
 473:    * <code>checkAccess()</code>, followed by further checks on each thread
 474:    * being resumed.
 475:    *
 476:    * @throws SecurityException if permission is denied
 477:    * @see #checkAccess()
 478:    * @see Thread#suspend()
 479:    * @deprecated pointless, since suspend is deprecated
 480:    */
 481:   public final synchronized void resume()
 482:   {
 483:     checkAccess();
 484:     if (groups == null)
 485:       return;
 486:     int i = threads.size();
 487:     while (--i >= 0)
 488:       ((Thread) threads.get(i)).resume();
 489:     i = groups.size();
 490:     while (--i >= 0)
 491:       ((ThreadGroup) groups.get(i)).resume();
 492:   }
 493: 
 494:   /**
 495:    * Destroy this ThreadGroup.  The group must be empty, meaning that all
 496:    * threads and sub-groups have completed execution. Daemon groups are
 497:    * destroyed automatically. There may be a security check,
 498:    * <code>checkAccess</code>.
 499:    *
 500:    * @throws IllegalThreadStateException if the ThreadGroup is not empty, or
 501:    *         was previously destroyed
 502:    * @throws SecurityException if permission is denied
 503:    * @see #checkAccess()
 504:    */
 505:   public final synchronized void destroy()
 506:   {
 507:     checkAccess();
 508:     if (! threads.isEmpty() || groups == null)
 509:       throw new IllegalThreadStateException();
 510:     int i = groups.size();
 511:     while (--i >= 0)
 512:       ((ThreadGroup) groups.get(i)).destroy();
 513:     groups = null;
 514:     if (parent != null)
 515:       parent.removeGroup(this);
 516:   }
 517: 
 518:   /**
 519:    * Print out information about this ThreadGroup to System.out. This is
 520:    * meant for debugging purposes. <b>WARNING:</b> This method is not secure,
 521:    * and can print the name of threads to standard out even when you cannot
 522:    * otherwise get at such threads.
 523:    */
 524:   public void list()
 525:   {
 526:     list("");
 527:   }
 528: 
 529:   /**
 530:    * When a Thread in this ThreadGroup does not catch an exception, the
 531:    * virtual machine calls this method. The default implementation simply
 532:    * passes the call to the parent; then in top ThreadGroup, it will
 533:    * ignore ThreadDeath and print the stack trace of any other throwable.
 534:    * Override this method if you want to handle the exception in a different
 535:    * manner.
 536:    *
 537:    * @param thread the thread that exited
 538:    * @param t the uncaught throwable
 539:    * @throws NullPointerException if t is null
 540:    * @see ThreadDeath
 541:    * @see System#err
 542:    * @see Throwable#printStackTrace()
 543:    */
 544:   public void uncaughtException(Thread thread, Throwable t)
 545:   {
 546:     if (parent != null)
 547:       parent.uncaughtException(thread, t);
 548:     else if (! (t instanceof ThreadDeath))
 549:       {
 550:         if (t == null)
 551:           throw new NullPointerException();
 552:         had_uncaught_exception = true;
 553:         try
 554:           {
 555:             if (thread != null)
 556:               System.err.print("Exception in thread \"" + thread.name + "\" ");
 557:             t.printStackTrace(System.err);
 558:           }
 559:         catch (Throwable x)
 560:           {
 561:             // This means that something is badly screwed up with the runtime,
 562:             // or perhaps someone overloaded the Throwable.printStackTrace to
 563:             // die. In any case, try to deal with it gracefully.
 564:             try
 565:               {
 566:                 System.err.println(t);
 567:                 System.err.println("*** Got " + x
 568:                                    + " while trying to print stack trace.");
 569:               }
 570:             catch (Throwable x2)
 571:               {
 572:                 // Here, someone may have overloaded t.toString() or
 573:                 // x.toString() to die. Give up all hope; we can't even chain
 574:                 // the exception, because the chain would likewise die.
 575:                 System.err.println("*** Catastrophic failure while handling "
 576:                                    + "uncaught exception.");
 577:                 throw new InternalError();
 578:               }
 579:           }
 580:       }
 581:   }
 582: 
 583:   /**
 584:    * Originally intended to tell the VM whether it may suspend Threads in
 585:    * low memory situations, this method was never implemented by Sun, and
 586:    * is hence a no-op.
 587:    *
 588:    * @param allow whether to allow low-memory thread suspension; ignored
 589:    * @return false
 590:    * @since 1.1
 591:    * @deprecated pointless, since suspend is deprecated
 592:    */
 593:   public boolean allowThreadSuspension(boolean allow)
 594:   {
 595:     return false;
 596:   }
 597: 
 598:   /**
 599:    * Return a human-readable String representing this ThreadGroup. The format
 600:    * of the string is:<br>
 601:    * <code>getClass().getName() + "[name=" + getName() + ",maxpri="
 602:    * + getMaxPriority() + ']'</code>.
 603:    *
 604:    * @return a human-readable String representing this ThreadGroup
 605:    */
 606:   public String toString()
 607:   {
 608:     return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']';
 609:   }
 610: 
 611:   /**
 612:    * Implements enumerate.
 613:    *
 614:    * @param list the array to put the threads into
 615:    * @param next the next open slot in the array
 616:    * @param recurse whether to recurse into descendent ThreadGroups
 617:    * @return the number of threads put into the array
 618:    * @throws SecurityException if permission was denied
 619:    * @throws NullPointerException if list is null
 620:    * @throws ArrayStoreException if a thread does not fit in the array
 621:    * @see #enumerate(Thread[])
 622:    * @see #enumerate(Thread[], boolean)
 623:    */
 624:   private int enumerate(Thread[] list, int next, boolean recurse)
 625:   {
 626:     checkAccess();
 627:     if (groups == null)
 628:       return next;
 629:     int i = threads.size();
 630:     while (--i >= 0 && next < list.length)
 631:       {
 632:         Thread t = (Thread) threads.get(i);
 633:         if (t.isAlive())
 634:           list[next++] = t;
 635:       }
 636:     if (recurse)
 637:       {
 638:         i = groups.size();
 639:         while (--i >= 0 && next < list.length)
 640:           {
 641:             ThreadGroup g = (ThreadGroup) groups.get(i);
 642:             next = g.enumerate(list, next, true);
 643:           }
 644:       }
 645:     return next;
 646:   }
 647: 
 648:   /**
 649:    * Implements enumerate.
 650:    *
 651:    * @param list the array to put the groups into
 652:    * @param next the next open slot in the array
 653:    * @param recurse whether to recurse into descendent ThreadGroups
 654:    * @return the number of groups put into the array
 655:    * @throws SecurityException if permission was denied
 656:    * @throws NullPointerException if list is null
 657:    * @throws ArrayStoreException if a group does not fit in the array
 658:    * @see #enumerate(ThreadGroup[])
 659:    * @see #enumerate(ThreadGroup[], boolean)
 660:    */
 661:   private int enumerate(ThreadGroup[] list, int next, boolean recurse)
 662:   {
 663:     checkAccess();
 664:     if (groups == null)
 665:       return next;
 666:     int i = groups.size();
 667:     while (--i >= 0 && next < list.length)
 668:       {
 669:         ThreadGroup g = (ThreadGroup) groups.get(i);
 670:         list[next++] = g;
 671:         if (recurse && next != list.length)
 672:           next = g.enumerate(list, next, true);
 673:       }
 674:     return next;
 675:   }
 676: 
 677:   /**
 678:    * Implements list.
 679:    *
 680:    * @param indentation the current level of indentation
 681:    * @see #list()
 682:    */
 683:   private void list(String indentation)
 684:   {
 685:     if (groups == null)
 686:       return;
 687:     System.out.println(indentation + this);
 688:     indentation += "    ";
 689:     int i = threads.size();
 690:     while (--i >= 0)
 691:       System.out.println(indentation + threads.get(i));
 692:     i = groups.size();
 693:     while (--i >= 0)
 694:       ((ThreadGroup) groups.get(i)).list(indentation);
 695:   }
 696: 
 697:   /**
 698:    * Add a thread to the group. Called by Thread constructors.
 699:    *
 700:    * @param t the thread to add, non-null
 701:    * @throws IllegalThreadStateException if the group is destroyed
 702:    */
 703:   final synchronized void addThread(Thread t)
 704:   {
 705:     if (groups == null)
 706:       throw new IllegalThreadStateException("ThreadGroup is destroyed");
 707:     threads.add(t);
 708:   }
 709: 
 710:   /**
 711:    * Called by the VM to remove a thread that has died.
 712:    *
 713:    * @param t the thread to remove, non-null
 714:    * @XXX A ThreadListener to call this might be nice.
 715:    */
 716:   final synchronized void removeThread(Thread t)
 717:   {
 718:     if (groups == null)
 719:       return;
 720:     threads.remove(t);
 721:     t.group = null;
 722:     // Daemon groups are automatically destroyed when all their threads die.
 723:     if (daemon_flag && groups.size() == 0 && threads.size() == 0)
 724:       {
 725:         // We inline destroy to avoid the access check.
 726:         groups = null;
 727:         if (parent != null)
 728:           parent.removeGroup(this);
 729:       }
 730:   }
 731: 
 732:   /**
 733:    * Called when a group is destroyed, to remove it from its parent.
 734:    *
 735:    * @param g the destroyed group, non-null
 736:    */
 737:   final synchronized void removeGroup(ThreadGroup g)
 738:   {
 739:     groups.remove(g);
 740:     // Daemon groups are automatically destroyed when all their threads die.
 741:     if (daemon_flag && groups.size() == 0 && threads.size() == 0)
 742:       {
 743:         // We inline destroy to avoid the access check.
 744:         groups = null;
 745:         if (parent != null)
 746:           parent.removeGroup(this);
 747:       }
 748:   }
 749: } // class ThreadGroup