1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.lang; 19 20 /** 21 * A {@code ThreadGroup} is a means of organizing {@link Thread}s into a 22 * hierarchical structure. A {@code ThreadGroup} can contain zero or more 23 * {@code Thread}s and zero or more other {@code ThreadGroup}s. Each {@code 24 * Thread} and each {@code ThreadGroup} (except the root group) has a unique 25 * parent {@code ThreadGroup}. The result is a tree whose inner nodes are 26 * {@code ThreadGroup}s and whose leaf nodes are {@code Threads}. The unique 27 * root of the tree is a {@code ThreadGroup} that is created at VM startup and 28 * has the name "system". The benefit of using {@code ThreadGroup}s, in addition 29 * to the mere housekeeping aspect, is that all {@code Thread}s in a {@code 30 * ThreadGroup} can be manipulated together, that is, the {@code ThreadGroup} 31 * has methods that delegate to all its all {@code Thread}s. 32 * 33 * @see Thread 34 * @see SecurityManager 35 * 36 * @since Android 1.0 37 */ 38 public class ThreadGroup implements Thread.UncaughtExceptionHandler { 39 40 // Name of this ThreadGroup 41 private String name; 42 // BEGIN android-note 43 // VM needs this field name for debugging. 44 // END android-note 45 46 // Maximum priority for Threads inside this ThreadGroup 47 private int maxPriority = Thread.MAX_PRIORITY; 48 49 // The ThreadGroup to which this ThreadGroup belongs 50 ThreadGroup parent; 51 // BEGIN android-note 52 // VM needs this field name for debugging. 53 // END android-note 54 55 int numThreads; 56 57 // The Threads this ThreadGroup contains 58 private Thread[] childrenThreads = new Thread[5]; 59 60 // The number of children groups 61 int numGroups; 62 63 // The ThreadGroups this ThreadGroup contains 64 private ThreadGroup[] childrenGroups = new ThreadGroup[3]; 65 66 // Locked when using the childrenGroups field 67 private class ChildrenGroupsLock {} 68 private Object childrenGroupsLock = new ChildrenGroupsLock(); 69 70 // Locked when using the childrenThreads field 71 private class ChildrenThreadsLock {} 72 private Object childrenThreadsLock = new ChildrenThreadsLock(); 73 74 // Whether this ThreadGroup is a daemon ThreadGroup or not 75 private boolean isDaemon; 76 77 // Whether this ThreadGroup has already been destroyed or not 78 private boolean isDestroyed; 79 80 // BEGIN android-added 81 /* the VM uses these directly; do not rename */ 82 static ThreadGroup mSystem = new ThreadGroup(); 83 static ThreadGroup mMain = new ThreadGroup(mSystem, "main"); 84 // END android-added 85 86 // BEGIN android-removed 87 // /** 88 // * Used by the JVM to create the "system" ThreadGroup. Construct a 89 // * ThreadGroup instance, and assign the name "system". 90 // */ 91 // private ThreadGroup() { 92 // name = "system"; 93 // } 94 // END android-removed 95 96 /** 97 * Constructs a new ThreadGroup with the name provided. The new ThreadGroup 98 * will be child of the ThreadGroup to which the 99 * {@code Thread.currentThread()} belongs. 100 * 101 * @param name the name for the ThreadGroup being created 102 * 103 * @throws SecurityException if {@code checkAccess()} for the parent 104 * group fails with a SecurityException 105 * 106 * @see java.lang.Thread#currentThread 107 */ 108 ThreadGroup(String name)109 public ThreadGroup(String name) { 110 this(Thread.currentThread().getThreadGroup(), name); 111 } 112 113 /** 114 * Constructs a new ThreadGroup with the name provided, as child of the 115 * ThreadGroup {@code parent}. 116 * 117 * @param parent the parent ThreadGroup 118 * @param name the name for the ThreadGroup being created 119 * 120 * @throws NullPointerException if {@code parent} is 121 * {@code null} 122 * @throws SecurityException if {@code checkAccess()} for the parent 123 * group fails with a SecurityException 124 * @throws IllegalThreadStateException if {@code parent} has been 125 * destroyed already 126 */ ThreadGroup(ThreadGroup parent, String name)127 public ThreadGroup(ThreadGroup parent, String name) { 128 super(); 129 if (Thread.currentThread() != null) { 130 // If parent is null we must throw NullPointerException, but that 131 // will be done "for free" with the message send below 132 parent.checkAccess(); 133 } 134 135 this.name = name; 136 this.setParent(parent); 137 if (parent != null) { 138 this.setMaxPriority(parent.getMaxPriority()); 139 if (parent.isDaemon()) { 140 this.setDaemon(true); 141 } 142 } 143 } 144 145 /** 146 * Initialize the special "system" ThreadGroup. Was "main" in Harmony, 147 * but we have an additional group above that in Android. 148 */ ThreadGroup()149 ThreadGroup() { 150 this.name = "system"; 151 this.setParent(null); 152 } 153 154 /** 155 * Returns the number of Threads which are children of the receiver, 156 * directly or indirectly and are running. 157 * 158 * @return the number of children Threads 159 */ 160 activeCount()161 public int activeCount() { 162 // BEGIN android-changed 163 int count = 0; 164 // Lock the children thread list 165 synchronized (this.childrenThreadsLock) { 166 for (int i = 0; i < numThreads; i++) { 167 if(childrenThreads[i].isAlive()) { 168 count++; 169 } 170 } 171 } 172 // END android-changed 173 // Lock this subpart of the tree as we walk 174 synchronized (this.childrenGroupsLock) { 175 for (int i = 0; i < numGroups; i++) { 176 count += this.childrenGroups[i].activeCount(); 177 } 178 } 179 return count; 180 } 181 182 /** 183 * Returns the number of ThreadGroups which are children of the receiver, 184 * directly or indirectly. 185 * 186 * @return the number of children ThreadGroups 187 */ activeGroupCount()188 public int activeGroupCount() { 189 int count = 0; 190 // Lock this subpart of the tree as we walk 191 synchronized (this.childrenGroupsLock) { 192 for (int i = 0; i < numGroups; i++) { 193 // One for this group & the subgroups 194 count += 1 + this.childrenGroups[i].activeGroupCount(); 195 } 196 } 197 return count; 198 } 199 200 /** 201 * Adds a Thread to the receiver. This should only be visible to class 202 * java.lang.Thread, and should only be called when a new Thread is created 203 * and initialized by the constructor. 204 * 205 * @param thread Thread to add to the receiver 206 * 207 * @throws IllegalThreadStateException if the receiver has been destroyed 208 * already 209 * 210 * @see #remove(java.lang.Thread) 211 */ add(Thread thread)212 final void add(Thread thread) throws IllegalThreadStateException { 213 synchronized (this.childrenThreadsLock) { 214 if (!isDestroyed) { 215 if (childrenThreads.length == numThreads) { 216 Thread[] newThreads = new Thread[childrenThreads.length * 2]; 217 System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads); 218 newThreads[numThreads++] = thread; 219 childrenThreads = newThreads; 220 } else { 221 childrenThreads[numThreads++] = thread; 222 } 223 } else { 224 throw new IllegalThreadStateException(); 225 } 226 } 227 } 228 229 /** 230 * Adds a ThreadGroup to the receiver. 231 * 232 * @param g ThreadGroup to add to the receiver 233 * 234 * @throws IllegalThreadStateException if the receiver has been destroyed 235 * already 236 */ add(ThreadGroup g)237 private void add(ThreadGroup g) throws IllegalThreadStateException { 238 synchronized (this.childrenGroupsLock) { 239 if (!isDestroyed) { 240 if (childrenGroups.length == numGroups) { 241 ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2]; 242 System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups); 243 newGroups[numGroups++] = g; 244 childrenGroups = newGroups; 245 } else { 246 childrenGroups[numGroups++] = g; 247 } 248 } else { 249 throw new IllegalThreadStateException(); 250 } 251 } 252 } 253 254 /** 255 * Does nothing. The definition of this method depends on the deprecated 256 * method {@link #suspend()}. The exact behavior of this call was never 257 * specified. 258 * 259 * @param b Used to control low memory implicit suspension 260 * @return {@code true} (always) 261 * 262 * @deprecated Required deprecated method suspend(). 263 */ 264 @Deprecated allowThreadSuspension(boolean b)265 public boolean allowThreadSuspension(boolean b) { 266 // Does not apply to this VM, no-op 267 return true; 268 } 269 270 /** 271 * Checks the accessibility of the ThreadGroup from the perspective of the 272 * caller. If there is a SecurityManager installed, calls 273 * {@code checkAccess} with the receiver as a parameter, otherwise does 274 * nothing. 275 */ checkAccess()276 public final void checkAccess() { 277 // Forwards the message to the SecurityManager (if there's one) passing 278 // the receiver as parameter 279 SecurityManager currentManager = System.getSecurityManager(); 280 if (currentManager != null) { 281 currentManager.checkAccess(this); 282 } 283 } 284 285 /** 286 * Destroys the receiver and recursively all its subgroups. It is only legal 287 * to destroy a ThreadGroup that has no Threads in it. Any daemon 288 * ThreadGroup is destroyed automatically when it becomes empty (no Threads 289 * and no ThreadGroups in it). 290 * 291 * @throws IllegalThreadStateException if the receiver or any of its 292 * subgroups has been destroyed already or if it still contains 293 * threads. 294 * @throws SecurityException if {@code this.checkAccess()} fails with 295 * a SecurityException 296 */ 297 destroy()298 public final void destroy() { 299 checkAccess(); 300 301 // Lock this subpart of the tree as we walk 302 synchronized (this.childrenThreadsLock) { 303 synchronized (this.childrenGroupsLock) { 304 // BEGIN android-added 305 if (this.isDestroyed) { 306 throw new IllegalThreadStateException( 307 "Thread group was already destroyed: " 308 + (this.name != null ? this.name : "n/a")); 309 } 310 if (this.numThreads > 0) { 311 throw new IllegalThreadStateException( 312 "Thread group still contains threads: " 313 + (this.name != null ? this.name : "n/a")); 314 } 315 // END android-added 316 int toDestroy = numGroups; 317 // Call recursively for subgroups 318 for (int i = 0; i < toDestroy; i++) { 319 // We always get the first element - remember, when the 320 // child dies it removes itself from our collection. See 321 // below. 322 this.childrenGroups[0].destroy(); 323 } 324 325 if (parent != null) { 326 parent.remove(this); 327 } 328 329 // Now that the ThreadGroup is really destroyed it can be tagged 330 // as so 331 this.isDestroyed = true; 332 } 333 } 334 } 335 336 /* 337 * Auxiliary method that destroys the receiver and recursively all its 338 * subgroups if the receiver is a daemon ThreadGroup. 339 * 340 * @see #destroy 341 * @see #setDaemon 342 * @see #isDaemon 343 */ destroyIfEmptyDaemon()344 private void destroyIfEmptyDaemon() { 345 // Has to be non-destroyed daemon to make sense 346 synchronized (this.childrenThreadsLock) { 347 if (isDaemon && !isDestroyed && numThreads == 0) { 348 synchronized (this.childrenGroupsLock) { 349 if (numGroups == 0) { 350 destroy(); 351 } 352 } 353 } 354 } 355 } 356 357 /** 358 * Iterates over all active threads in this group (and its sub-groups) and 359 * stores the threads in the given array. Returns when the array is full or 360 * no more threads remain, whichever happens first. 361 * 362 * @param threads the array into which the Threads will be copied 363 * @return the number of Threads that were copied 364 */ enumerate(Thread[] threads)365 public int enumerate(Thread[] threads) { 366 return enumerate(threads, true); 367 } 368 369 /** 370 * Iterates over all active threads in this group (and, optionally, its 371 * sub-groups) and stores the threads in the given array. Returns when the 372 * array is full or no more threads remain, whichever happens first. 373 * 374 * @param threads the array into which the Threads will be copied 375 * @param recurse indicates whether Threads in subgroups should be 376 * recursively copied as well 377 * @return the number of Threads that were copied 378 * 379 */ enumerate(Thread[] threads, boolean recurse)380 public int enumerate(Thread[] threads, boolean recurse) { 381 return enumerateGeneric(threads, recurse, 0, true); 382 } 383 384 /** 385 * Iterates over all thread groups in this group (and its sub-groups) and 386 * and stores the groups in the given array. Returns when the array is full 387 * or no more groups remain, whichever happens first. 388 * 389 * @param groups the array into which the ThreadGroups will be copied 390 * @return the number of ThreadGroups that were copied 391 * 392 */ enumerate(ThreadGroup[] groups)393 public int enumerate(ThreadGroup[] groups) { 394 return enumerate(groups, true); 395 } 396 397 /** 398 * Iterates over all thread groups in this group (and, optionally, its 399 * sub-groups) and and stores the groups in the given array. Returns when 400 * the array is full or no more groups remain, whichever happens first. 401 * 402 * @param groups the array into which the ThreadGroups will be copied 403 * @param recurse indicates whether ThreadGroups in subgroups should be 404 * recursively copied as well or not 405 * @return the number of ThreadGroups that were copied 406 * 407 */ enumerate(ThreadGroup[] groups, boolean recurse)408 public int enumerate(ThreadGroup[] groups, boolean recurse) { 409 return enumerateGeneric(groups, recurse, 0, false); 410 } 411 412 /** 413 * Copies into <param>enumeration</param> starting at 414 * <param>enumerationIndex</param> all Threads or ThreadGroups in the 415 * receiver. If <param>recurse</param> is true, recursively enumerate the 416 * elements in subgroups. 417 * 418 * If the array passed as parameter is too small no exception is thrown - 419 * the extra elements are simply not copied. 420 * 421 * @param enumeration array into which the elements will be copied 422 * @param recurse Indicates whether subgroups should be enumerated or not 423 * @param enumerationIndex Indicates in which position of the enumeration 424 * array we are 425 * @param enumeratingThreads Indicates whether we are enumerating Threads or 426 * ThreadGroups 427 * @return How many elements were enumerated/copied over 428 */ enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, boolean enumeratingThreads)429 private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, 430 boolean enumeratingThreads) { 431 checkAccess(); 432 433 Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads 434 : (Object[]) childrenGroups; 435 Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock; 436 437 synchronized (syncLock) { // Lock this subpart of the tree as we walk 438 for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) { 439 if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) { 440 if (enumerationIndex >= enumeration.length) { 441 return enumerationIndex; 442 } 443 enumeration[enumerationIndex++] = immediateCollection[i]; 444 } 445 } 446 } 447 448 if (recurse) { // Lock this subpart of the tree as we walk 449 synchronized (this.childrenGroupsLock) { 450 for (int i = 0; i < numGroups; i++) { 451 if (enumerationIndex >= enumeration.length) { 452 return enumerationIndex; 453 } 454 enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse, 455 enumerationIndex, enumeratingThreads); 456 } 457 } 458 } 459 return enumerationIndex; 460 } 461 462 /** 463 * Returns the maximum allowed priority for a Thread in the receiver. 464 * 465 * @return the maximum priority 466 * 467 * @see #setMaxPriority 468 */ getMaxPriority()469 public final int getMaxPriority() { 470 return maxPriority; 471 } 472 473 /** 474 * Returns the name of the receiver. 475 * 476 * @return the receiver's name 477 */ getName()478 public final String getName() { 479 return name; 480 } 481 482 /** 483 * Returns the receiver's parent ThreadGroup. It can be {@code null} if the 484 * receiver is the the root ThreadGroup. 485 * 486 * @return the parent ThreadGroup 487 * 488 */ getParent()489 public final ThreadGroup getParent() { 490 if (parent != null) { 491 parent.checkAccess(); 492 } 493 return parent; 494 } 495 496 /** 497 * Interrupts every Thread in the receiver and recursively in all its 498 * subgroups. 499 * 500 * @throws SecurityException if {@code this.checkAccess()} fails with 501 * a SecurityException 502 * 503 * @see Thread#interrupt 504 */ interrupt()505 public final void interrupt() { 506 checkAccess(); 507 // Lock this subpart of the tree as we walk 508 synchronized (this.childrenThreadsLock) { 509 for (int i = 0; i < numThreads; i++) { 510 this.childrenThreads[i].interrupt(); 511 } 512 } 513 // Lock this subpart of the tree as we walk 514 synchronized (this.childrenGroupsLock) { 515 for (int i = 0; i < numGroups; i++) { 516 this.childrenGroups[i].interrupt(); 517 } 518 } 519 } 520 521 /** 522 * Checks whether the receiver is a daemon ThreadGroup. 523 * 524 * @return true if (and only if) the receiver is a daemon ThreadGroup 525 * 526 * @see #setDaemon 527 * @see #destroy 528 */ isDaemon()529 public final boolean isDaemon() { 530 return isDaemon; 531 } 532 533 /** 534 * Checks whether the receiver has already been destroyed. 535 * 536 * @return true if (and only if) the receiver has already been destroyed 537 * 538 * @see #destroy 539 */ isDestroyed()540 public synchronized boolean isDestroyed() { 541 return isDestroyed; 542 } 543 544 /** 545 * Outputs to {@code System.out} a text representation of the 546 * hierarchy of Threads and ThreadGroups in the receiver (and recursively). 547 * Proper indentation is done to suggest the nesting of groups inside groups 548 * and threads inside groups. 549 */ list()550 public void list() { 551 // We start in a fresh line 552 System.out.println(); 553 list(0); 554 } 555 556 /* 557 * Outputs to {@code System.out}a text representation of the 558 * hierarchy of Threads and ThreadGroups in the receiver (and recursively). 559 * The indentation will be four spaces per level of nesting. 560 * 561 * @param levels How many levels of nesting, so that proper indentation can 562 * be output. 563 */ list(int levels)564 private void list(int levels) { 565 for (int i = 0; i < levels; i++) { 566 System.out.print(" "); // 4 spaces for each level 567 } 568 569 // Print the receiver 570 System.out.println(this.toString()); 571 572 // Print the children threads, with 1 extra indentation 573 synchronized (this.childrenThreadsLock) { 574 for (int i = 0; i < numThreads; i++) { 575 // children get an extra indentation, 4 spaces for each level 576 for (int j = 0; j <= levels; j++) { 577 System.out.print(" "); 578 } 579 System.out.println(this.childrenThreads[i]); 580 } 581 } 582 synchronized (this.childrenGroupsLock) { 583 for (int i = 0; i < numGroups; i++) { 584 this.childrenGroups[i].list(levels + 1); 585 } 586 } 587 } 588 589 /** 590 * Checks whether the receiver is a direct or indirect parent group of a 591 * given ThreadGroup. 592 * 593 * @param g the potential child ThreadGroup 594 * 595 * @return true if (and only if) the receiver is parent of {@code g} 596 * 597 */ parentOf(ThreadGroup g)598 public final boolean parentOf(ThreadGroup g) { 599 while (g != null) { 600 if (this == g) { 601 return true; 602 } 603 g = g.parent; 604 } 605 return false; 606 } 607 608 /** 609 * Removes a Thread from the receiver. This should only be visible to class 610 * java.lang.Thread, and should only be called when a Thread dies. 611 * 612 * @param thread Thread to remove from the receiver 613 * 614 * @see #add(Thread) 615 */ remove(java.lang.Thread thread)616 final void remove(java.lang.Thread thread) { 617 synchronized (this.childrenThreadsLock) { 618 for (int i = 0; i < numThreads; i++) { 619 if (childrenThreads[i].equals(thread)) { 620 numThreads--; 621 System 622 .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads 623 - i); 624 childrenThreads[numThreads] = null; 625 break; 626 } 627 } 628 } 629 destroyIfEmptyDaemon(); 630 } 631 632 /** 633 * Removes an immediate subgroup from the receiver. 634 * 635 * @param g ThreadGroup to remove from the receiver 636 * 637 * @see #add(Thread) 638 * @see #add(ThreadGroup) 639 */ remove(ThreadGroup g)640 private void remove(ThreadGroup g) { 641 synchronized (this.childrenGroupsLock) { 642 for (int i = 0; i < numGroups; i++) { 643 if (childrenGroups[i].equals(g)) { 644 numGroups--; 645 System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i); 646 childrenGroups[numGroups] = null; 647 break; 648 } 649 } 650 } 651 destroyIfEmptyDaemon(); 652 } 653 654 /** 655 * Resumes every Thread in the receiver and recursively in all its 656 * subgroups. 657 * 658 * @throws SecurityException if {@code this.checkAccess()} fails with 659 * a SecurityException 660 * 661 * @see Thread#resume 662 * @see #suspend 663 * 664 * @deprecated Requires deprecated method Thread.resume(). 665 */ 666 @SuppressWarnings("deprecation") 667 @Deprecated resume()668 public final void resume() { 669 checkAccess(); 670 // Lock this subpart of the tree as we walk 671 synchronized (this.childrenThreadsLock) { 672 for (int i = 0; i < numThreads; i++) { 673 this.childrenThreads[i].resume(); 674 } 675 } 676 // Lock this subpart of the tree as we walk 677 synchronized (this.childrenGroupsLock) { 678 for (int i = 0; i < numGroups; i++) { 679 this.childrenGroups[i].resume(); 680 } 681 } 682 } 683 684 /** 685 * Configures the receiver to be a daemon ThreadGroup or not. Daemon 686 * ThreadGroups are automatically destroyed when they become empty. 687 * 688 * @param isDaemon the new value defining if receiver should be daemon or 689 * not 690 * 691 * @throws SecurityException if {@code checkAccess()} for the parent 692 * group fails with a SecurityException 693 * 694 * @see #isDaemon 695 * @see #destroy 696 */ setDaemon(boolean isDaemon)697 public final void setDaemon(boolean isDaemon) { 698 checkAccess(); 699 this.isDaemon = isDaemon; 700 } 701 702 /** 703 * Configures the maximum allowed priority for a Thread in the receiver and 704 * recursively in all its subgroups. 705 * 706 * One can never change the maximum priority of a ThreadGroup to be higher 707 * than it was. Such an attempt will not result in an exception, it will 708 * simply leave the ThreadGroup with its current maximum priority. 709 * 710 * @param newMax the new maximum priority to be set 711 * 712 * @throws SecurityException if {@code checkAccess()} fails with a 713 * SecurityException 714 * @throws IllegalArgumentException if the new priority is greater than 715 * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY 716 * 717 * @see #getMaxPriority 718 */ setMaxPriority(int newMax)719 public final void setMaxPriority(int newMax) { 720 checkAccess(); 721 722 if (newMax <= this.maxPriority) { 723 if (newMax < Thread.MIN_PRIORITY) { 724 newMax = Thread.MIN_PRIORITY; 725 } 726 727 int parentPriority = parent == null ? newMax : parent.getMaxPriority(); 728 this.maxPriority = parentPriority <= newMax ? parentPriority : newMax; 729 // Lock this subpart of the tree as we walk 730 synchronized (this.childrenGroupsLock) { 731 // ??? why not maxPriority 732 for (int i = 0; i < numGroups; i++) { 733 this.childrenGroups[i].setMaxPriority(newMax); 734 } 735 } 736 } 737 } 738 739 /** 740 * Sets the parent ThreadGroup of the receiver, and adds the receiver to the 741 * parent's collection of immediate children (if {@code parent} is 742 * not {@code null}). 743 * 744 * @param parent The parent ThreadGroup, or null if the receiver is to be 745 * the root ThreadGroup 746 * 747 * @see #getParent 748 * @see #parentOf 749 */ setParent(ThreadGroup parent)750 private void setParent(ThreadGroup parent) { 751 if (parent != null) { 752 parent.add(this); 753 } 754 this.parent = parent; 755 } 756 757 /** 758 * Stops every Thread in the receiver and recursively in all its subgroups. 759 * 760 * @throws SecurityException if {@code this.checkAccess()} fails with 761 * a SecurityException 762 * 763 * @see Thread#stop() 764 * @see Thread#stop(Throwable) 765 * @see ThreadDeath 766 * 767 * @deprecated Requires deprecated method Thread.stop(). 768 */ 769 @SuppressWarnings("deprecation") 770 @Deprecated stop()771 public final void stop() { 772 if (stopHelper()) { 773 Thread.currentThread().stop(); 774 } 775 } 776 777 /** 778 * @deprecated Requires deprecated method Thread.suspend(). 779 */ 780 @SuppressWarnings("deprecation") 781 @Deprecated stopHelper()782 private final boolean stopHelper() { 783 checkAccess(); 784 785 boolean stopCurrent = false; 786 // Lock this subpart of the tree as we walk 787 synchronized (this.childrenThreadsLock) { 788 Thread current = Thread.currentThread(); 789 for (int i = 0; i < numThreads; i++) { 790 if (this.childrenThreads[i] == current) { 791 stopCurrent = true; 792 } else { 793 this.childrenThreads[i].stop(); 794 } 795 } 796 } 797 // Lock this subpart of the tree as we walk 798 synchronized (this.childrenGroupsLock) { 799 for (int i = 0; i < numGroups; i++) { 800 stopCurrent |= this.childrenGroups[i].stopHelper(); 801 } 802 } 803 return stopCurrent; 804 } 805 806 /** 807 * Suspends every Thread in the receiver and recursively in all its 808 * subgroups. 809 * 810 * @throws SecurityException if {@code this.checkAccess()} fails with 811 * a SecurityException 812 * 813 * @see Thread#suspend 814 * @see #resume 815 * 816 * @deprecated Requires deprecated method Thread.suspend(). 817 */ 818 @SuppressWarnings("deprecation") 819 @Deprecated suspend()820 public final void suspend() { 821 if (suspendHelper()) { 822 Thread.currentThread().suspend(); 823 } 824 } 825 826 /** 827 * @deprecated Requires deprecated method Thread.suspend(). 828 */ 829 @SuppressWarnings("deprecation") 830 @Deprecated suspendHelper()831 private final boolean suspendHelper() { 832 checkAccess(); 833 834 boolean suspendCurrent = false; 835 // Lock this subpart of the tree as we walk 836 synchronized (this.childrenThreadsLock) { 837 Thread current = Thread.currentThread(); 838 for (int i = 0; i < numThreads; i++) { 839 if (this.childrenThreads[i] == current) { 840 suspendCurrent = true; 841 } else { 842 this.childrenThreads[i].suspend(); 843 } 844 } 845 } 846 // Lock this subpart of the tree as we walk 847 synchronized (this.childrenGroupsLock) { 848 for (int i = 0; i < numGroups; i++) { 849 suspendCurrent |= this.childrenGroups[i].suspendHelper(); 850 } 851 } 852 return suspendCurrent; 853 } 854 855 /** 856 * Returns a string containing a concise, human-readable description of the 857 * receiver. 858 * 859 * @return a printable representation of the ThreadGroup 860 */ 861 @Override toString()862 public String toString() { 863 return getClass().getName() + "[name=" + this.getName() + ",maxpri=" 864 + this.getMaxPriority() + "]"; 865 } 866 867 /** 868 * Handles uncaught exceptions. Any uncaught exception in any Thread 869 * is forwarded (by the VM) to the Thread's ThreadGroup by sending this 870 * message (uncaughtException). This allows users to define custom 871 * ThreadGroup classes and custom behavior for when a Thread has an 872 * uncaughtException or when it does (ThreadDeath). 873 * 874 * @param t the Thread that terminated with an uncaught exception 875 * @param e the uncaught exception itself 876 * 877 * @see Thread#stop() 878 * @see Thread#stop(Throwable) 879 * @see ThreadDeath 880 */ uncaughtException(Thread t, Throwable e)881 public void uncaughtException(Thread t, Throwable e) { 882 // BEGIN android-changed 883 if (parent != null) { 884 parent.uncaughtException(t, e); 885 } else if (Thread.getDefaultUncaughtExceptionHandler() != null) { 886 // TODO The spec is unclear regarding this. What do we do? 887 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, e); 888 } else if (!(e instanceof ThreadDeath)) { 889 // No parent group, has to be 'system' Thread Group 890 e.printStackTrace(System.err); 891 } 892 // END android-changed 893 } 894 895 // BEGIN android-added 896 /** 897 * Non-standard method for adding a thread to a group, required by Dalvik. 898 * 899 * @param thread Thread to add to the receiver 900 * 901 * @throws IllegalThreadStateException if the receiver has been destroyed 902 * already 903 * 904 * @see #add(java.lang.Thread) 905 * @see #removeThread(java.lang.Thread) 906 */ addThread(Thread thread)907 void addThread(Thread thread) throws IllegalThreadStateException { 908 add(thread); 909 } 910 911 /** 912 * Non-standard method for adding a thread to a group, required by Dalvik. 913 * 914 * @param thread Thread to add to the receiver 915 * 916 * @throws IllegalThreadStateException if the receiver has been destroyed 917 * already 918 * 919 * @see #remove(java.lang.Thread) 920 * @see #addThread(java.lang.Thread) 921 */ removeThread(Thread thread)922 void removeThread(Thread thread) throws IllegalThreadStateException { 923 remove(thread); 924 } 925 // END android-added 926 } 927