GNU Classpath (0.20) | |
Frames | No Frames |
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
GNU Classpath (0.20) |