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