1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.concurrency.thread.ThreadEx; 13 14 import hunt.concurrency.thread.LockSupport; 15 16 import hunt.Exceptions; 17 import hunt.Functions; 18 import hunt.logging.ConsoleLogger; 19 import hunt.system.Memory; 20 import hunt.util.Common; 21 import hunt.util.DateTime; 22 23 import core.atomic; 24 import core.thread; 25 import core.time; 26 import core.sync.condition; 27 import core.sync.mutex; 28 29 import std.algorithm: min, max; 30 import std.conv: to; 31 32 33 /** 34 */ 35 interface Interruptible { 36 void interrupt(Thread t); 37 } 38 39 40 /** 41 * Interface for handlers invoked when a {@code Thread} abruptly 42 * terminates due to an uncaught exception. 43 * <p>When a thread is about to terminate due to an uncaught exception 44 * the Java Virtual Machine will query the thread for its 45 * {@code UncaughtExceptionHandler} using 46 * {@link #getUncaughtExceptionHandler} and will invoke the handler's 47 * {@code uncaughtException} method, passing the thread and the 48 * exception as arguments. 49 * If a thread has not had its {@code UncaughtExceptionHandler} 50 * explicitly set, then its {@code ThreadGroupEx} object acts as its 51 * {@code UncaughtExceptionHandler}. If the {@code ThreadGroupEx} object 52 * has no 53 * special requirements for dealing with the exception, it can forward 54 * the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler 55 * default uncaught exception handler}. 56 * 57 * @see #setDefaultUncaughtExceptionHandler 58 * @see #setUncaughtExceptionHandler 59 * @see ThreadGroupEx#uncaughtException 60 */ 61 interface UncaughtExceptionHandler { 62 /** 63 * Method invoked when the given thread terminates due to the 64 * given uncaught exception. 65 * <p>Any exception thrown by this method will be ignored by the 66 * Java Virtual Machine. 67 * @param t the thread 68 * @param e the exception 69 */ 70 void uncaughtException(Thread t, Throwable e); 71 } 72 73 74 /** 75 * A thread state. A thread can be in one of the following states: 76 * <ul> 77 * <li>{@link #NEW}<br> 78 * A thread that has not yet started is in this state. 79 * </li> 80 * <li>{@link #RUNNABLE}<br> 81 * A thread executing in the Java virtual machine is in this state. 82 * </li> 83 * <li>{@link #BLOCKED}<br> 84 * A thread that is blocked waiting for a monitor lock 85 * is in this state. 86 * </li> 87 * <li>{@link #WAITING}<br> 88 * A thread that is waiting indefinitely for another thread to 89 * perform a particular action is in this state. 90 * </li> 91 * <li>{@link #TIMED_WAITING}<br> 92 * A thread that is waiting for another thread to perform an action 93 * for up to a specified waiting time is in this state. 94 * </li> 95 * <li>{@link #TERMINATED}<br> 96 * A thread that has exited is in this state. 97 * </li> 98 * </ul> 99 * 100 * <p> 101 * A thread can be in only one state at a given point in time. 102 * These states are virtual machine states which do not reflect 103 * any operating system thread states. 104 * 105 * @see #getState 106 */ 107 enum ThreadState { 108 /** 109 * Thread state for a thread which has not yet started. 110 */ 111 NEW, 112 113 /** 114 * Thread state for a runnable thread. A thread in the runnable 115 * state is executing in the Java virtual machine but it may 116 * be waiting for other resources from the operating system 117 * such as processor. 118 */ 119 RUNNABLE, 120 121 /** 122 * Thread state for a thread blocked waiting for a monitor lock. 123 * A thread in the blocked state is waiting for a monitor lock 124 * to enter a synchronized block/method or 125 * reenter a synchronized block/method after calling 126 * {@link Object#wait() Object.wait}. 127 */ 128 BLOCKED, 129 130 /** 131 * Thread state for a waiting thread. 132 * A thread is in the waiting state due to calling one of the 133 * following methods: 134 * <ul> 135 * <li>{@link Object#wait() Object.wait} with no timeout</li> 136 * <li>{@link #join() Thread.join} with no timeout</li> 137 * <li>{@link LockSupport#park() LockSupport.park}</li> 138 * </ul> 139 * 140 * <p>A thread in the waiting state is waiting for another thread to 141 * perform a particular action. 142 * 143 * For example, a thread that has called {@code Object.wait()} 144 * on an object is waiting for another thread to call 145 * {@code Object.notify()} or {@code Object.notifyAll()} on 146 * that object. A thread that has called {@code Thread.join()} 147 * is waiting for a specified thread to terminate. 148 */ 149 WAITING, 150 151 /** 152 * Thread state for a waiting thread with a specified waiting time. 153 * A thread is in the timed waiting state due to calling one of 154 * the following methods with a specified positive waiting time: 155 * <ul> 156 * <li>{@link #sleep Thread.sleep}</li> 157 * <li>{@link Object#wait(long) Object.wait} with timeout</li> 158 * <li>{@link #join(long) Thread.join} with timeout</li> 159 * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> 160 * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> 161 * </ul> 162 */ 163 TIMED_WAITING, 164 165 /** 166 * Thread state for a terminated thread. 167 * The thread has completed execution. 168 */ 169 TERMINATED 170 } 171 172 173 /** 174 * A <i>thread</i> is a thread of execution in a program. The Java 175 * Virtual Machine allows an application to have multiple threads of 176 * execution running concurrently. 177 * <p> 178 * Every thread has a priority. Threads with higher priority are 179 * executed in preference to threads with lower priority. Each thread 180 * may or may not also be marked as a daemon. When code running in 181 * some thread creates a new {@code Thread} object, the new 182 * thread has its priority initially set equal to the priority of the 183 * creating thread, and is a daemon thread if and only if the 184 * creating thread is a daemon. 185 * <p> 186 * When a Java Virtual Machine starts up, there is usually a single 187 * non-daemon thread (which typically calls the method named 188 * {@code main} of some designated class). The Java Virtual 189 * Machine continues to execute threads until either of the following 190 * occurs: 191 * <ul> 192 * <li>The {@code exit} method of class {@code Runtime} has been 193 * called and the security manager has permitted the exit operation 194 * to take place. 195 * <li>All threads that are not daemon threads have died, either by 196 * returning from the call to the {@code run} method or by 197 * throwing an exception that propagates beyond the {@code run} 198 * method. 199 * </ul> 200 * <p> 201 * There are two ways to create a new thread of execution. One is to 202 * declare a class to be a subclass of {@code Thread}. This 203 * subclass should override the {@code run} method of class 204 * {@code Thread}. An instance of the subclass can then be 205 * allocated and started. For example, a thread that computes primes 206 * larger than a stated value could be written as follows: 207 * <hr><blockquote><pre> 208 * class PrimeThread extends Thread { 209 * long minPrime; 210 * PrimeThread(long minPrime) { 211 * this.minPrime = minPrime; 212 * } 213 * 214 * public void run() { 215 * // compute primes larger than minPrime 216 * . . . 217 * } 218 * } 219 * </pre></blockquote><hr> 220 * <p> 221 * The following code would then create a thread and start it running: 222 * <blockquote><pre> 223 * PrimeThread p = new PrimeThread(143); 224 * p.start(); 225 * </pre></blockquote> 226 * <p> 227 * The other way to create a thread is to declare a class that 228 * implements the {@code Runnable} interface. That class then 229 * implements the {@code run} method. An instance of the class can 230 * then be allocated, passed as an argument when creating 231 * {@code Thread}, and started. The same example in this other 232 * style looks like the following: 233 * <hr><blockquote><pre> 234 * class PrimeRun implements Runnable { 235 * long minPrime; 236 * PrimeRun(long minPrime) { 237 * this.minPrime = minPrime; 238 * } 239 * 240 * public void run() { 241 * // compute primes larger than minPrime 242 * . . . 243 * } 244 * } 245 * </pre></blockquote><hr> 246 * <p> 247 * The following code would then create a thread and start it running: 248 * <blockquote><pre> 249 * PrimeRun p = new PrimeRun(143); 250 * new Thread(p).start(); 251 * </pre></blockquote> 252 * <p> 253 * Every thread has a name for identification purposes. More than 254 * one thread may have the same name. If a name is not specified when 255 * a thread is created, a new name is generated for it. 256 * <p> 257 * Unless otherwise noted, passing a {@code null} argument to a constructor 258 * or method in this class will cause a {@link NullPointerException} to be 259 * thrown. 260 * 261 */ 262 class ThreadEx : Thread, Runnable { 263 264 /* What will be run. */ 265 private Runnable target; 266 267 // Object parkBlocker; 268 ThreadState state; 269 270 271 /* The object in which this thread is blocked in an interruptible I/O 272 * operation, if any. The blocker's interrupt method should be invoked 273 * after setting this thread's interrupt status. 274 */ 275 private Interruptible blocker; 276 private Object blockerLock; 277 private shared bool _interrupted; // Thread.isInterrupted state 278 279 /* For autonumbering anonymous threads. */ 280 private static shared int threadInitNumber; 281 private static int nextThreadNum() { 282 return core.atomic.atomicOp!"+="(threadInitNumber, 1); 283 } 284 285 this() { 286 this(null, null, "Thread-" ~ nextThreadNum().to!string()); 287 } 288 289 this(string name) { 290 this(null, null, name); 291 } 292 293 this(Runnable target) { 294 this(null, target, "Thread-" ~ nextThreadNum().to!string()); 295 } 296 297 this(Runnable target, string name) { 298 this(null, target, name); 299 } 300 301 this(ThreadGroupEx group, string name) { 302 this(group, null, name); 303 } 304 305 this(ThreadGroupEx group, Runnable target, string name, size_t sz = 0) { 306 this.name = name; 307 this.group = group; 308 this.target = target; 309 super(&run, sz); 310 initialize(); 311 } 312 313 this(Action dg, string name) { 314 this(new class Runnable { 315 void run() { dg();} 316 }, name); 317 } 318 319 this(Action dg) { 320 this(new class Runnable { 321 void run() { dg();} 322 }); 323 } 324 325 this(void function() fn) { 326 this(new class Runnable { 327 void run() { fn();} 328 }); 329 } 330 331 ~this() { 332 blocker = null; 333 blockerLock = null; 334 // parkBlocker = null; 335 target = null; 336 _parker = null; 337 } 338 339 private void initialize() nothrow { 340 _parker = Parker.allocate(this); 341 blockerLock = new Object(); 342 state = ThreadState.NEW; 343 } 344 345 346 /** 347 * Returns the thread group to which this thread belongs. 348 * This method returns null if this thread has died 349 * (been stopped). 350 * 351 * @return this thread's thread group. 352 */ 353 final ThreadGroupEx getThreadGroup() { 354 return group; 355 } 356 357 /* The group of this thread */ 358 private ThreadGroupEx group; 359 360 /** 361 * If this thread was constructed using a separate 362 * {@code Runnable} run object, then that 363 * {@code Runnable} object's {@code run} method is called; 364 * otherwise, this method does nothing and returns. 365 * <p> 366 * Subclasses of {@code Thread} should override this method. 367 * 368 * @see #start() 369 * @see #stop() 370 * @see #Thread(ThreadGroup, Runnable, String) 371 * See_also: 372 * https://stackoverflow.com/questions/8579657/whats-the-difference-between-thread-start-and-runnable-run 373 */ 374 void run() { 375 version(HUNT_CONCURRENCY_DEBUG_MORE) { 376 infof("trying to run a target %s...", target is null ? "(null)" : ""); 377 } 378 if (target !is null) { 379 target.run(); 380 } 381 } 382 383 384 385 /** 386 * Tests if this thread is alive. A thread is alive if it has 387 * been started and has not yet died. 388 * 389 * @return <code>true</code> if this thread is alive; 390 * <code>false</code> otherwise. 391 */ 392 final bool isAlive() { 393 // TODO: Tasks pending completion -@zxp at 11/7/2018, 10:30:43 AM 394 // 395 return isRunning(); 396 } 397 398 399 /** 400 * Marks this thread as either a {@linkplain #isDaemon daemon} thread 401 * or a user thread. The Java Virtual Machine exits when the only 402 * threads running are all daemon threads. 403 * 404 * <p> This method must be invoked before the thread is started. 405 * 406 * @param on 407 * if {@code true}, marks this thread as a daemon thread 408 * 409 * @throws IllegalThreadStateException 410 * if this thread is {@linkplain #isAlive alive} 411 * 412 * @throws SecurityException 413 * if {@link #checkAccess} determines that the current 414 * thread cannot modify this thread 415 */ 416 final void setDaemon(bool on) { 417 if (isAlive()) { 418 throw new IllegalThreadStateException(); 419 } 420 isDaemon(on); 421 } 422 423 /** 424 * Returns the state of this thread. 425 * This method is designed for use in monitoring of the system state, 426 * not for synchronization control. 427 * 428 * @return this thread's state. 429 */ 430 ThreadState getState() { 431 // get current thread state 432 // return jdk.internal.misc.VM.toThreadState(threadStatus); 433 return state; 434 } 435 436 /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code 437 */ 438 void blockedOn(Interruptible b) { 439 synchronized (blockerLock) { 440 blocker = b; 441 } 442 } 443 444 /** 445 * Interrupts this thread. 446 * 447 * <p> Unless the current thread is interrupting itself, which is 448 * always permitted, the {@link #checkAccess() checkAccess} method 449 * of this thread is invoked, which may cause a {@link 450 * SecurityException} to be thrown. 451 * 452 * <p> If this thread is blocked in an invocation of the {@link 453 * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link 454 * Object#wait(long, int) wait(long, int)} methods of the {@link Object} 455 * class, or of the {@link #join()}, {@link #join(long)}, {@link 456 * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)}, 457 * methods of this class, then its interrupt status will be cleared and it 458 * will receive an {@link InterruptedException}. 459 * 460 * <p> If this thread is blocked in an I/O operation upon an {@link 461 * java.nio.channels.InterruptibleChannel InterruptibleChannel} 462 * then the channel will be closed, the thread's interrupt 463 * status will be set, and the thread will receive a {@link 464 * java.nio.channels.ClosedByInterruptException}. 465 * 466 * <p> If this thread is blocked in a {@link java.nio.channels.Selector} 467 * then the thread's interrupt status will be set and it will return 468 * immediately from the selection operation, possibly with a non-zero 469 * value, just as if the selector's {@link 470 * java.nio.channels.Selector#wakeup wakeup} method were invoked. 471 * 472 * <p> If none of the previous conditions hold then this thread's interrupt 473 * status will be set. </p> 474 * 475 * <p> Interrupting a thread that is not alive need not have any effect. 476 * 477 * @throws SecurityException 478 * if the current thread cannot modify this thread 479 * 480 * @revised 6.0 481 * @spec JSR-51 482 */ 483 void interrupt() { 484 synchronized (blockerLock) { 485 Interruptible b = blocker; 486 if (b !is null) { 487 interrupt0(); // Just to set the interrupt flag 488 b.interrupt(this); 489 return; 490 } 491 } 492 interrupt0(); 493 } 494 495 private void interrupt0() { 496 if(!_interrupted) { 497 _interrupted = true; 498 // More than one thread can get here with the same value of osthread, 499 // resulting in multiple notifications. We do, however, want the store 500 // to interrupted() to be visible to other threads before we execute unpark(). 501 // OrderAccess::fence(); 502 // ParkEvent * const slp = thread->_SleepEvent ; 503 // if (slp != NULL) slp->unpark() ; 504 } 505 506 // For JSR166. Unpark even if interrupt status already was set 507 _parker.unpark(); 508 // LockSupport.unpark(); 509 510 // ParkEvent * ev = thread->_ParkEvent ; 511 // if (ev != NULL) ev->unpark() ; 512 } 513 514 /** 515 * Tests whether this thread has been interrupted. The <i>interrupted 516 * status</i> of the thread is unaffected by this method. 517 * 518 * <p>A thread interruption ignored because a thread was not alive 519 * at the time of the interrupt will be reflected by this method 520 * returning false. 521 * 522 * @return <code>true</code> if this thread has been interrupted; 523 * <code>false</code> otherwise. 524 * @see #interrupted() 525 * @revised 6.0 526 */ 527 bool isInterrupted() { 528 return isInterrupted(false); 529 } 530 531 532 /** 533 * Tests whether the current thread has been interrupted. The 534 * <i>interrupted status</i> of the thread is cleared by this method. In 535 * other words, if this method were to be called twice in succession, the 536 * second call would return false (unless the current thread were 537 * interrupted again, after the first call had cleared its interrupted 538 * status and before the second call had examined it). 539 * 540 * <p>A thread interruption ignored because a thread was not alive 541 * at the time of the interrupt will be reflected by this method 542 * returning false. 543 * 544 * @return <code>true</code> if the current thread has been interrupted; 545 * <code>false</code> otherwise. 546 * @see #isInterrupted() 547 * @revised 6.0 548 */ 549 static bool interrupted() { 550 ThreadEx tex = cast(ThreadEx) Thread.getThis(); 551 if(tex is null) 552 return false; 553 554 return tex.isInterrupted(true); 555 } 556 557 /** 558 * Tests if some Thread has been interrupted. The interrupted state 559 * is reset or not based on the value of ClearInterrupted that is 560 * passed. 561 */ 562 private bool isInterrupted(bool canClear) { 563 // bool interrupted = osthread->interrupted(); 564 565 // NOTE that since there is no "lock" around the interrupt and 566 // is_interrupted operations, there is the possibility that the 567 // interrupted flag (in osThread) will be "false" but that the 568 // low-level events will be in the signaled state. This is 569 // intentional. The effect of this is that Object.wait() and 570 // LockSupport.park() will appear to have a spurious wakeup, which 571 // is allowed and not harmful, and the possibility is so rare that 572 // it is not worth the added complexity to add yet another lock. 573 // For the sleep event an explicit reset is performed on entry 574 // to os::sleep, so there is no early return. It has also been 575 // recommended not to put the interrupted flag into the "event" 576 // structure because it hides the issue. 577 if (_interrupted && canClear) { 578 _interrupted = false; 579 // consider thread->_SleepEvent->reset() ... optional optimization 580 } 581 582 return _interrupted; 583 } 584 585 static ThreadEx currentThread() { 586 ThreadEx tex = cast(ThreadEx) Thread.getThis(); 587 assert(tex !is null, "Must be a ThreadEx"); 588 return tex; 589 } 590 591 592 Parker parker() { 593 return _parker; 594 } 595 private Parker _parker; 596 597 // Short sleep, direct OS call. 598 static void nakedSleep(Duration timeout) { 599 Thread.sleep(timeout); 600 } 601 602 // Sleep forever; naked call to OS-specific sleep; use with CAUTION 603 static void infiniteSleep() { 604 while (true) { // sleep forever ... 605 Thread.sleep(100.seconds); // ... 100 seconds at a time 606 } 607 } 608 609 static void sleep(Duration timeout) { 610 // TODO: Tasks pending completion -@zxp at 11/6/2018, 12:29:22 PM 611 // using ParkEvent 612 LockSupport.park(timeout); 613 } 614 615 // Low-level leaf-lock primitives used to implement synchronization 616 // and native monitor-mutex infrastructure. 617 // Not for general synchronization use. 618 private static void spinAcquire(shared(int)* adr, string name) nothrow { 619 int last = *adr; 620 cas(adr, 0, 1); 621 if (last == 0) return; // normal fast-path return 622 623 // Slow-path : We've encountered contention -- Spin/Yield/Block strategy. 624 // TEVENT(SpinAcquire - ctx); 625 int ctr = 0; 626 int yields = 0; 627 for (;;) { 628 while (*adr != 0) { 629 ++ctr; 630 if ((ctr & 0xFFF) == 0 && !is_MP()) { 631 if (yields > 5) { 632 Thread.sleep(1.msecs); 633 } else { 634 Thread.yield(); 635 ++yields; 636 } 637 } else { 638 spinPause(); 639 } 640 } 641 last = *adr; 642 cas(adr, 1, 0); 643 if (last == 0) return; 644 } 645 } 646 647 private static void spinRelease(shared(int)* adr) @safe nothrow @nogc { 648 assert(*adr != 0, "invariant"); 649 atomicFence(); // guarantee at least release consistency. 650 // Roach-motel semantics. 651 // It's safe if subsequent LDs and STs float "up" into the critical section, 652 // but prior LDs and STs within the critical section can't be allowed 653 // to reorder or float past the ST that releases the lock. 654 // Loads and stores in the critical section - which appear in program 655 // order before the store that releases the lock - must also appear 656 // before the store that releases the lock in memory visibility order. 657 // Conceptually we need a #loadstore|#storestore "release" MEMBAR before 658 // the ST of 0 into the lock-word which releases the lock, so fence 659 // more than covers this on all platforms. 660 *adr = 0; 661 } 662 663 // static void muxAcquire(shared intptr_t * Lock, string Name); 664 // // static void muxAcquireW(shared intptr_t * Lock, ParkEvent * ev); 665 // static void muxRelease(shared intptr_t * Lock); 666 667 private static int spinPause() @safe nothrow @nogc { 668 version (X86_64) { 669 return 0; 670 } 671 else version (AsmX86_Windows) { 672 asm pure nothrow @nogc { 673 pause; 674 } 675 return 1; 676 } 677 else { 678 return -1; 679 } 680 } 681 682 private static bool is_MP() @safe pure nothrow @nogc { 683 // During bootstrap if _processor_count is not yet initialized 684 // we claim to be MP as that is safest. If any platform has a 685 // stub generator that might be triggered in this phase and for 686 // which being declared MP when in fact not, is a problem - then 687 // the bootstrap routine for the stub generator needs to check 688 // the processor count directly and leave the bootstrap routine 689 // in place until called after initialization has ocurred. 690 // return (_processor_count != 1); // AssumeMP || 691 return totalCPUs != 1; 692 } 693 694 695 696 // null unless explicitly set 697 private UncaughtExceptionHandler uncaughtExceptionHandler; 698 699 // null unless explicitly set 700 private __gshared UncaughtExceptionHandler defaultUncaughtExceptionHandler; 701 702 /** 703 * Set the default handler invoked when a thread abruptly terminates 704 * due to an uncaught exception, and no other handler has been defined 705 * for that thread. 706 * 707 * <p>Uncaught exception handling is controlled first by the thread, then 708 * by the thread's {@link ThreadGroup} object and finally by the default 709 * uncaught exception handler. If the thread does not have an explicit 710 * uncaught exception handler set, and the thread's thread group 711 * (including parent thread groups) does not specialize its 712 * {@code uncaughtException} method, then the default handler's 713 * {@code uncaughtException} method will be invoked. 714 * <p>By setting the default uncaught exception handler, an application 715 * can change the way in which uncaught exceptions are handled (such as 716 * logging to a specific device, or file) for those threads that would 717 * already accept whatever "default" behavior the system 718 * provided. 719 * 720 * <p>Note that the default uncaught exception handler should not usually 721 * defer to the thread's {@code ThreadGroup} object, as that could cause 722 * infinite recursion. 723 * 724 * @param eh the object to use as the default uncaught exception handler. 725 * If {@code null} then there is no default handler. 726 * 727 * @throws SecurityException if a security manager is present and it denies 728 * {@link RuntimePermission}{@code ("setDefaultUncaughtExceptionHandler")} 729 * 730 * @see #setUncaughtExceptionHandler 731 * @see #getUncaughtExceptionHandler 732 * @see ThreadGroup#uncaughtException 733 */ 734 static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { 735 // SecurityManager sm = System.getSecurityManager(); 736 // if (sm != null) { 737 // sm.checkPermission( 738 // new RuntimePermission("setDefaultUncaughtExceptionHandler") 739 // ); 740 // } 741 742 defaultUncaughtExceptionHandler = eh; 743 } 744 745 /** 746 * Returns the default handler invoked when a thread abruptly terminates 747 * due to an uncaught exception. If the returned value is {@code null}, 748 * there is no default. 749 * @see #setDefaultUncaughtExceptionHandler 750 * @return the default uncaught exception handler for all threads 751 */ 752 static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){ 753 return defaultUncaughtExceptionHandler; 754 } 755 756 /** 757 * Returns the handler invoked when this thread abruptly terminates 758 * due to an uncaught exception. If this thread has not had an 759 * uncaught exception handler explicitly set then this thread's 760 * {@code ThreadGroup} object is returned, unless this thread 761 * has terminated, in which case {@code null} is returned. 762 * @return the uncaught exception handler for this thread 763 */ 764 UncaughtExceptionHandler getUncaughtExceptionHandler() { 765 return uncaughtExceptionHandler !is null ? 766 uncaughtExceptionHandler : group; 767 } 768 769 /** 770 * Set the handler invoked when this thread abruptly terminates 771 * due to an uncaught exception. 772 * <p>A thread can take full control of how it responds to uncaught 773 * exceptions by having its uncaught exception handler explicitly set. 774 * If no such handler is set then the thread's {@code ThreadGroup} 775 * object acts as its handler. 776 * @param eh the object to use as this thread's uncaught exception 777 * handler. If {@code null} then this thread has no explicit handler. 778 * @throws SecurityException if the current thread is not allowed to 779 * modify this thread. 780 * @see #setDefaultUncaughtExceptionHandler 781 * @see ThreadGroup#uncaughtException 782 */ 783 void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { 784 checkAccess(); 785 uncaughtExceptionHandler = eh; 786 } 787 788 void checkAccess() { 789 790 } 791 } 792 793 794 /* 795 * Per-thread blocking support for JSR166. See the Java-level 796 * Documentation for rationale. Basically, park acts like wait, unpark 797 * like notify. 798 * 799 * 6271289 -- 800 * To avoid errors where an os thread expires but the JavaThread still 801 * exists, Parkers are immortal (type-stable) and are recycled across 802 * new threads. This parallels the ParkEvent implementation. 803 * Because park-unpark allow spurious wakeups it is harmless if an 804 * unpark call unparks a new thread using the old Parker reference. 805 * 806 * In the future we'll want to think about eliminating Parker and using 807 * ParkEvent instead. There's considerable duplication between the two 808 * services. 809 * 810 */ 811 class Parker { 812 813 enum int REL_INDEX = 0; 814 enum int ABS_INDEX = 1; 815 816 private Object parkBlocker; 817 818 private shared int _counter; 819 private int _nParked; 820 private Parker freeNext; 821 private Thread associatedWith; // Current association 822 823 private int _cur_index; // which cond is in use: -1, 0, 1 824 private Mutex _mutex; 825 private Condition[2] _cond; // one for relative times and one for absolute 826 827 this() @safe nothrow { 828 _counter = 0; 829 _mutex = new Mutex(); 830 _cond[REL_INDEX] = new Condition(_mutex); 831 _cond[ABS_INDEX] = new Condition(_mutex); 832 _cur_index = -1; // mark as unused 833 } 834 835 // For simplicity of interface, all forms of park (indefinite, 836 // relative, and absolute) are multiplexed into one call. 837 // park decrements count if > 0, else does a condvar wait. Unpark 838 // sets count to 1 and signals condvar. Only one thread ever waits 839 // on the condvar. Contention seen when trying to park implies that someone 840 // is unparking you, so don't wait. And spurious returns are fine, so there 841 // is no need to track notifications. 842 void park(Duration time) { 843 debug(HUNT_CONCURRENCY_DEBUG) { 844 tracef("try to park a thread %s", 845 time <= Duration.zero ? "forever" : "in " ~ time.toString()); 846 } 847 // Optional fast-path check: 848 // Return immediately if a permit is available. 849 // We depend on Atomic::xchg() having full barrier semantics 850 // since we are doing a lock-free update to _counter. 851 const int c = _counter; 852 if(c > 0) { 853 atomicStore(_counter, 0); 854 debug(HUNT_CONCURRENCY_DEBUG) infof("no need to park, counter=%s", c); 855 return; 856 } 857 858 // Next, demultiplex/decode time arguments 859 if (time < Duration.zero) { // don't wait at all 860 return; 861 } 862 863 ThreadEx thread = cast(ThreadEx) Thread.getThis(); 864 865 // Enter safepoint region 866 // Beware of deadlocks such as 6317397. 867 // The per-thread Parker. mutex is a classic leaf-lock. 868 // In particular a thread must never block on the Threads_lock while 869 // holding the Parker.mutex. 870 871 // Don't wait if cannot get lock since interference arises from 872 // unparking. Also re-check interrupt before trying wait. 873 if((thread !is null && thread.isInterrupted()) || !_mutex.tryLock()) 874 return; 875 876 if (_counter > 0) { // no wait needed 877 return; 878 } 879 880 scope(exit) { 881 _counter = 0; 882 _mutex.unlock(); 883 // Paranoia to ensure our locked and lock-free paths interact 884 // correctly with each other and Java-level accesses. 885 atomicFence(); 886 } 887 888 // OSThreadWaitState osts(thread.osthread(), false /* not Object.wait() */ ); 889 // jt.set_suspend_equivalent(); 890 // // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() 891 892 assert(_cur_index == -1, "invariant"); 893 if (time == Duration.zero) { 894 _cur_index = REL_INDEX; // arbitrary choice when not timed 895 _cond[_cur_index].wait(); 896 } 897 else { 898 _cur_index = REL_INDEX; 899 _cond[REL_INDEX].wait(time); 900 } 901 _cur_index = -1; 902 } 903 904 void park(MonoTime time) { 905 debug(HUNT_CONCURRENCY_DEBUG) { 906 Duration d = time - MonoTime.currTime; 907 tracef("try to park a thread %s", 908 d <= Duration.zero ? "forever" : "in " ~ d.toString()); 909 } 910 // Optional fast-path check: 911 // Return immediately if a permit is available. 912 // We depend on Atomic::xchg() having full barrier semantics 913 // since we are doing a lock-free update to _counter. 914 const int c = _counter; 915 if(c > 0) { 916 atomicStore(_counter, 0); 917 debug(HUNT_CONCURRENCY_DEBUG) infof("no need to park, counter=%s", c); 918 return; 919 } 920 921 // Next, demultiplex/decode time arguments 922 if (time <= MonoTime.zero) { // don't wait at all 923 return; 924 } 925 926 ThreadEx thread = cast(ThreadEx) Thread.getThis(); 927 928 // Enter safepoint region 929 // Beware of deadlocks such as 6317397. 930 // The per-thread Parker. mutex is a classic leaf-lock. 931 // In particular a thread must never block on the Threads_lock while 932 // holding the Parker.mutex. 933 934 // Don't wait if cannot get lock since interference arises from 935 // unparking. Also re-check interrupt before trying wait. 936 if((thread !is null && thread.isInterrupted()) || !_mutex.tryLock()) 937 return; 938 939 if (_counter > 0) { // no wait needed 940 return; 941 } 942 943 scope(exit) { 944 _counter = 0; 945 _mutex.unlock(); 946 // Paranoia to ensure our locked and lock-free paths interact 947 // correctly with each other and Java-level accesses. 948 atomicFence(); 949 } 950 951 // OSThreadWaitState osts(thread.osthread(), false /* not Object.wait() */ ); 952 // jt.set_suspend_equivalent(); 953 // // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() 954 955 assert(_cur_index == -1, "invariant"); 956 if (time == MonoTime.zero) { 957 _cur_index = REL_INDEX; // arbitrary choice when not timed 958 _cond[_cur_index].wait(); 959 } 960 else { 961 _cur_index = ABS_INDEX; 962 Duration t = time - MonoTime.currTime; 963 if(t > Duration.zero) 964 _cond[ABS_INDEX].wait(t); 965 } 966 _cur_index = -1; 967 } 968 969 void unpark() { 970 debug(HUNT_CONCURRENCY_DEBUG) { 971 tracef("try to unpark a thread"); 972 } 973 _mutex.lock(); 974 const int s = _counter; 975 _counter = 1; 976 // must capture correct index before unlocking 977 int index = _cur_index; 978 _mutex.unlock(); 979 980 // Note that we signal() *after* dropping the lock for "immortal" Events. 981 // This is safe and avoids a common class of futile wakeups. In rare 982 // circumstances this can cause a thread to return prematurely from 983 // cond_{timed}wait() but the spurious wakeup is benign and the victim 984 // will simply re-test the condition and re-park itself. 985 // This provides particular benefit if the underlying platform does not 986 // provide wait morphing. 987 988 if (s < 1 && index != -1) { 989 // thread is definitely parked 990 _cond[index].notify(); 991 } 992 } 993 994 Object getBlocker() { 995 return parkBlocker; 996 } 997 998 void setBlocker(Object arg) { 999 parkBlocker = arg; 1000 } 1001 1002 // Lifecycle operators 1003 static Parker allocate(Thread t) nothrow { 1004 assert(t !is null, "invariant"); 1005 Parker p; 1006 1007 // Start by trying to recycle an existing but unassociated 1008 // Parker from the global free list. 1009 // 8028280: using concurrent free list without memory management can leak 1010 // pretty badly it turns out. 1011 ThreadEx.spinAcquire(&listLock, "ParkerFreeListAllocate"); 1012 { 1013 p = freeList; 1014 if (p !is null) { 1015 freeList = p.freeNext; 1016 } 1017 } 1018 ThreadEx.spinRelease(&listLock); 1019 1020 if (p !is null) { 1021 assert(p.associatedWith is null, "invariant"); 1022 } 1023 else { 1024 // Do this the hard way -- materialize a new Parker.. 1025 p = new Parker(); 1026 } 1027 p.associatedWith = t; // Associate p with t 1028 p.freeNext = null; 1029 return p; 1030 } 1031 1032 static void release(Parker p) { 1033 if (p is null) 1034 return; 1035 assert(p.associatedWith !is null, "invariant"); 1036 assert(p.freeNext is null, "invariant"); 1037 p.associatedWith = null; 1038 1039 ThreadEx.spinAcquire(&listLock, "ParkerFreeListRelease"); 1040 { 1041 p.freeNext = freeList; 1042 freeList = p; 1043 } 1044 ThreadEx.spinRelease(&listLock); 1045 } 1046 1047 private static Parker freeList; 1048 private static shared int listLock; 1049 } 1050 1051 1052 /** 1053 * A thread group represents a set of threads. In addition, a thread 1054 * group can also include other thread groups. The thread groups form 1055 * a tree in which every thread group except the initial thread group 1056 * has a parent. 1057 * <p> 1058 * A thread is allowed to access information about its own thread 1059 * group, but not to access information about its thread group's 1060 * parent thread group or any other thread groups. 1061 * 1062 * @author unascribed 1063 */ 1064 /* The locking strategy for this code is to try to lock only one level of the 1065 * tree wherever possible, but otherwise to lock from the bottom up. 1066 * That is, from child thread groups to parents. 1067 * This has the advantage of limiting the number of locks that need to be held 1068 * and in particular avoids having to grab the lock for the root thread group, 1069 * (or a global lock) which would be a source of contention on a 1070 * multi-processor system with many thread groups. 1071 * This policy often leads to taking a snapshot of the state of a thread group 1072 * and working off of that snapshot, rather than holding the thread group locked 1073 * while we work on the children. 1074 */ 1075 class ThreadGroupEx : UncaughtExceptionHandler { 1076 private ThreadGroupEx parent; 1077 string name; 1078 int maxPriority; 1079 bool destroyed; 1080 bool daemon; 1081 1082 int nUnstartedThreads = 0; 1083 int nthreads; 1084 Thread[] threads; 1085 1086 int ngroups; 1087 ThreadGroupEx[] groups; 1088 1089 /** 1090 * Creates an empty Thread group that is not in any Thread group. 1091 * This method is used to create the system Thread group. 1092 */ 1093 private this() { // called from C code 1094 this.name = "system"; 1095 this.maxPriority = Thread.PRIORITY_MAX; 1096 this.parent = null; 1097 } 1098 1099 /** 1100 * Constructs a new thread group. The parent of this new group is 1101 * the thread group of the currently running thread. 1102 * <p> 1103 * The {@code checkAccess} method of the parent thread group is 1104 * called with no arguments; this may result in a security exception. 1105 * 1106 * @param name the name of the new thread group. 1107 * @throws SecurityException if the current thread cannot create a 1108 * thread in the specified thread group. 1109 * @see java.lang.ThreadGroupEx#checkAccess() 1110 */ 1111 this(string name) { 1112 ThreadEx t = cast(ThreadEx)Thread.getThis(); 1113 if(t is null) 1114 this(null, name); 1115 else 1116 this(t.getThreadGroup(), name); 1117 } 1118 1119 /** 1120 * Creates a new thread group. The parent of this new group is the 1121 * specified thread group. 1122 * <p> 1123 * The {@code checkAccess} method of the parent thread group is 1124 * called with no arguments; this may result in a security exception. 1125 * 1126 * @param parent the parent thread group. 1127 * @param name the name of the new thread group. 1128 * @throws NullPointerException if the thread group argument is 1129 * {@code null}. 1130 * @throws SecurityException if the current thread cannot create a 1131 * thread in the specified thread group. 1132 * @see java.lang.SecurityException 1133 * @see java.lang.ThreadGroupEx#checkAccess() 1134 */ 1135 this(ThreadGroupEx parent, string name) { 1136 // this(checkParentAccess(parent), parent, name); 1137 1138 this.name = name; 1139 if(parent !is null) { 1140 parent.checkAccess(); 1141 this.maxPriority = parent.maxPriority; 1142 this.daemon = parent.daemon; 1143 this.parent = parent; 1144 parent.add(this); 1145 } 1146 } 1147 1148 // private ThreadGroupEx(Void unused, ThreadGroupEx parent, string name) { 1149 // this.name = name; 1150 // this.maxPriority = parent.maxPriority; 1151 // this.daemon = parent.daemon; 1152 // this.parent = parent; 1153 // parent.add(this); 1154 // } 1155 1156 /* 1157 * @throws NullPointerException if the parent argument is {@code null} 1158 * @throws SecurityException if the current thread cannot create a 1159 * thread in the specified thread group. 1160 */ 1161 // private static void checkParentAccess(ThreadGroupEx parent) { 1162 // parent.checkAccess(); 1163 // // return null; 1164 // } 1165 1166 /** 1167 * Returns the name of this thread group. 1168 * 1169 * @return the name of this thread group. 1170 */ 1171 final string getName() { 1172 return name; 1173 } 1174 1175 /** 1176 * Returns the parent of this thread group. 1177 * <p> 1178 * First, if the parent is not {@code null}, the 1179 * {@code checkAccess} method of the parent thread group is 1180 * called with no arguments; this may result in a security exception. 1181 * 1182 * @return the parent of this thread group. The top-level thread group 1183 * is the only thread group whose parent is {@code null}. 1184 * @throws SecurityException if the current thread cannot modify 1185 * this thread group. 1186 * @see java.lang.ThreadGroupEx#checkAccess() 1187 * @see java.lang.SecurityException 1188 * @see java.lang.RuntimePermission 1189 */ 1190 final ThreadGroupEx getParent() { 1191 if (parent !is null) 1192 parent.checkAccess(); 1193 return parent; 1194 } 1195 1196 /** 1197 * Returns the maximum priority of this thread group. Threads that are 1198 * part of this group cannot have a higher priority than the maximum 1199 * priority. 1200 * 1201 * @return the maximum priority that a thread in this thread group 1202 * can have. 1203 * @see #setMaxPriority 1204 */ 1205 final int getMaxPriority() { 1206 return maxPriority; 1207 } 1208 1209 /** 1210 * Tests if this thread group is a daemon thread group. A 1211 * daemon thread group is automatically destroyed when its last 1212 * thread is stopped or its last thread group is destroyed. 1213 * 1214 * @return {@code true} if this thread group is a daemon thread group; 1215 * {@code false} otherwise. 1216 */ 1217 final bool isDaemon() { 1218 return daemon; 1219 } 1220 1221 /** 1222 * Tests if this thread group has been destroyed. 1223 * 1224 * @return true if this object is destroyed 1225 */ 1226 bool isDestroyed() { 1227 return destroyed; 1228 } 1229 1230 /** 1231 * Changes the daemon status of this thread group. 1232 * <p> 1233 * First, the {@code checkAccess} method of this thread group is 1234 * called with no arguments; this may result in a security exception. 1235 * <p> 1236 * A daemon thread group is automatically destroyed when its last 1237 * thread is stopped or its last thread group is destroyed. 1238 * 1239 * @param daemon if {@code true}, marks this thread group as 1240 * a daemon thread group; otherwise, marks this 1241 * thread group as normal. 1242 * @throws SecurityException if the current thread cannot modify 1243 * this thread group. 1244 * @see java.lang.SecurityException 1245 * @see java.lang.ThreadGroupEx#checkAccess() 1246 */ 1247 final void setDaemon(bool daemon) { 1248 // checkAccess(); 1249 this.daemon = daemon; 1250 } 1251 1252 /** 1253 * Sets the maximum priority of the group. Threads in the thread 1254 * group that already have a higher priority are not affected. 1255 * <p> 1256 * First, the {@code checkAccess} method of this thread group is 1257 * called with no arguments; this may result in a security exception. 1258 * <p> 1259 * If the {@code pri} argument is less than 1260 * {@link Thread#PRIORITY_MIN} or greater than 1261 * {@link Thread#PRIORITY_MAX}, the maximum priority of the group 1262 * remains unchanged. 1263 * <p> 1264 * Otherwise, the priority of this ThreadGroupEx object is set to the 1265 * smaller of the specified {@code pri} and the maximum permitted 1266 * priority of the parent of this thread group. (If this thread group 1267 * is the system thread group, which has no parent, then its maximum 1268 * priority is simply set to {@code pri}.) Then this method is 1269 * called recursively, with {@code pri} as its argument, for 1270 * every thread group that belongs to this thread group. 1271 * 1272 * @param pri the new priority of the thread group. 1273 * @throws SecurityException if the current thread cannot modify 1274 * this thread group. 1275 * @see #getMaxPriority 1276 * @see java.lang.SecurityException 1277 * @see java.lang.ThreadGroupEx#checkAccess() 1278 */ 1279 final void setMaxPriority(int pri) { 1280 int ngroupsSnapshot; 1281 ThreadGroupEx[] groupsSnapshot; 1282 synchronized (this) { 1283 checkAccess(); 1284 if (pri < Thread.PRIORITY_MIN || pri > Thread.PRIORITY_MAX) { 1285 return; 1286 } 1287 maxPriority = (parent !is null) ? min(pri, parent.maxPriority) : pri; 1288 ngroupsSnapshot = ngroups; 1289 if (groups !is null) { 1290 // groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); 1291 size_t limit = min(ngroupsSnapshot, groups.length); 1292 groupsSnapshot = groups[0..limit].dup; 1293 } else { 1294 groupsSnapshot = null; 1295 } 1296 } 1297 for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1298 groupsSnapshot[i].setMaxPriority(pri); 1299 } 1300 } 1301 1302 /** 1303 * Tests if this thread group is either the thread group 1304 * argument or one of its ancestor thread groups. 1305 * 1306 * @param g a thread group. 1307 * @return {@code true} if this thread group is the thread group 1308 * argument or one of its ancestor thread groups; 1309 * {@code false} otherwise. 1310 */ 1311 final bool parentOf(ThreadGroupEx g) { 1312 for (; g !is null ; g = g.parent) { 1313 if (g is this) { 1314 return true; 1315 } 1316 } 1317 return false; 1318 } 1319 1320 /** 1321 * Determines if the currently running thread has permission to 1322 * modify this thread group. 1323 * <p> 1324 * If there is a security manager, its {@code checkAccess} method 1325 * is called with this thread group as its argument. This may result 1326 * in throwing a {@code SecurityException}. 1327 * 1328 * @throws SecurityException if the current thread is not allowed to 1329 * access this thread group. 1330 * @see java.lang.SecurityManager#checkAccess(java.lang.ThreadGroupEx) 1331 */ 1332 final void checkAccess() { 1333 // SecurityManager security = System.getSecurityManager(); 1334 // if (security !is null) { 1335 // security.checkAccess(this); 1336 // } 1337 } 1338 1339 /** 1340 * Returns an estimate of the number of active threads in this thread 1341 * group and its subgroups. Recursively iterates over all subgroups in 1342 * this thread group. 1343 * 1344 * <p> The value returned is only an estimate because the number of 1345 * threads may change dynamically while this method traverses internal 1346 * data structures, and might be affected by the presence of certain 1347 * system threads. This method is intended primarily for debugging 1348 * and monitoring purposes. 1349 * 1350 * @return an estimate of the number of active threads in this thread 1351 * group and in any other thread group that has this thread 1352 * group as an ancestor 1353 * 1354 */ 1355 int activeCount() { 1356 int result; 1357 // Snapshot sub-group data so we don't hold this lock 1358 // while our children are computing. 1359 int ngroupsSnapshot; 1360 ThreadGroupEx[] groupsSnapshot; 1361 synchronized (this) { 1362 if (destroyed) { 1363 return 0; 1364 } 1365 result = nthreads; 1366 ngroupsSnapshot = ngroups; 1367 if (groups !is null) { 1368 size_t limit = min(ngroupsSnapshot, groups.length); 1369 groupsSnapshot = groups[0..limit].dup; 1370 } else { 1371 groupsSnapshot = null; 1372 } 1373 } 1374 for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1375 result += groupsSnapshot[i].activeCount(); 1376 } 1377 return result; 1378 } 1379 1380 /** 1381 * Copies into the specified array every active thread in this 1382 * thread group and its subgroups. 1383 * 1384 * <p> An invocation of this method behaves in exactly the same 1385 * way as the invocation 1386 * 1387 * <blockquote> 1388 * {@linkplain #enumerate(Thread[], bool) enumerate}{@code (list, true)} 1389 * </blockquote> 1390 * 1391 * @param list 1392 * an array into which to put the list of threads 1393 * 1394 * @return the number of threads put into the array 1395 * 1396 * @throws SecurityException 1397 * if {@linkplain #checkAccess checkAccess} determines that 1398 * the current thread cannot access this thread group 1399 * 1400 */ 1401 int enumerate(Thread[] list) { 1402 checkAccess(); 1403 return enumerate(list, 0, true); 1404 } 1405 1406 /** 1407 * Copies into the specified array every active thread in this 1408 * thread group. If {@code recurse} is {@code true}, 1409 * this method recursively enumerates all subgroups of this 1410 * thread group and references to every active thread in these 1411 * subgroups are also included. If the array is too short to 1412 * hold all the threads, the extra threads are silently ignored. 1413 * 1414 * <p> An application might use the {@linkplain #activeCount activeCount} 1415 * method to get an estimate of how big the array should be, however 1416 * <i>if the array is too short to hold all the threads, the extra threads 1417 * are silently ignored.</i> If it is critical to obtain every active 1418 * thread in this thread group, the caller should verify that the returned 1419 * int value is strictly less than the length of {@code list}. 1420 * 1421 * <p> Due to the inherent race condition in this method, it is recommended 1422 * that the method only be used for debugging and monitoring purposes. 1423 * 1424 * @param list 1425 * an array into which to put the list of threads 1426 * 1427 * @param recurse 1428 * if {@code true}, recursively enumerate all subgroups of this 1429 * thread group 1430 * 1431 * @return the number of threads put into the array 1432 * 1433 * @throws SecurityException 1434 * if {@linkplain #checkAccess checkAccess} determines that 1435 * the current thread cannot access this thread group 1436 * 1437 */ 1438 int enumerate(Thread[] list, bool recurse) { 1439 checkAccess(); 1440 return enumerate(list, 0, recurse); 1441 } 1442 1443 private int enumerate(Thread[] list, int n, bool recurse) { 1444 int ngroupsSnapshot = 0; 1445 ThreadGroupEx[] groupsSnapshot = null; 1446 synchronized (this) { 1447 if (destroyed) { 1448 return 0; 1449 } 1450 int nt = nthreads; 1451 if (nt > cast(int)list.length - n) { 1452 nt = cast(int)list.length - n; 1453 } 1454 for (int i = 0; i < nt; i++) { 1455 // TODO: Tasks pending completion -@zxp at 10/14/2018, 9:11:46 AM 1456 // 1457 implementationMissing(false); 1458 // if (threads[i].isAlive()) { 1459 // list[n++] = threads[i]; 1460 // } 1461 } 1462 if (recurse) { 1463 ngroupsSnapshot = ngroups; 1464 if (groups !is null) { 1465 size_t limit = min(ngroupsSnapshot, groups.length); 1466 groupsSnapshot = groups[0..limit].dup; 1467 } else { 1468 groupsSnapshot = null; 1469 } 1470 } 1471 } 1472 if (recurse) { 1473 for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1474 n = groupsSnapshot[i].enumerate(list, n, true); 1475 } 1476 } 1477 return n; 1478 } 1479 1480 /** 1481 * Returns an estimate of the number of active groups in this 1482 * thread group and its subgroups. Recursively iterates over 1483 * all subgroups in this thread group. 1484 * 1485 * <p> The value returned is only an estimate because the number of 1486 * thread groups may change dynamically while this method traverses 1487 * internal data structures. This method is intended primarily for 1488 * debugging and monitoring purposes. 1489 * 1490 * @return the number of active thread groups with this thread group as 1491 * an ancestor 1492 * 1493 */ 1494 int activeGroupCount() { 1495 int ngroupsSnapshot; 1496 ThreadGroupEx[] groupsSnapshot; 1497 synchronized (this) { 1498 if (destroyed) { 1499 return 0; 1500 } 1501 ngroupsSnapshot = ngroups; 1502 if (groups !is null) { 1503 size_t limit = min(ngroupsSnapshot, groups.length); 1504 groupsSnapshot = groups[0..limit].dup; 1505 } else { 1506 groupsSnapshot = null; 1507 } 1508 } 1509 int n = ngroupsSnapshot; 1510 for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1511 n += groupsSnapshot[i].activeGroupCount(); 1512 } 1513 return n; 1514 } 1515 1516 /** 1517 * Copies into the specified array references to every active 1518 * subgroup in this thread group and its subgroups. 1519 * 1520 * <p> An invocation of this method behaves in exactly the same 1521 * way as the invocation 1522 * 1523 * <blockquote> 1524 * {@linkplain #enumerate(ThreadGroupEx[], bool) enumerate}{@code (list, true)} 1525 * </blockquote> 1526 * 1527 * @param list 1528 * an array into which to put the list of thread groups 1529 * 1530 * @return the number of thread groups put into the array 1531 * 1532 * @throws SecurityException 1533 * if {@linkplain #checkAccess checkAccess} determines that 1534 * the current thread cannot access this thread group 1535 * 1536 */ 1537 int enumerate(ThreadGroupEx[] list) { 1538 checkAccess(); 1539 return enumerate(list, 0, true); 1540 } 1541 1542 /** 1543 * Copies into the specified array references to every active 1544 * subgroup in this thread group. If {@code recurse} is 1545 * {@code true}, this method recursively enumerates all subgroups of this 1546 * thread group and references to every active thread group in these 1547 * subgroups are also included. 1548 * 1549 * <p> An application might use the 1550 * {@linkplain #activeGroupCount activeGroupCount} method to 1551 * get an estimate of how big the array should be, however <i>if the 1552 * array is too short to hold all the thread groups, the extra thread 1553 * groups are silently ignored.</i> If it is critical to obtain every 1554 * active subgroup in this thread group, the caller should verify that 1555 * the returned int value is strictly less than the length of 1556 * {@code list}. 1557 * 1558 * <p> Due to the inherent race condition in this method, it is recommended 1559 * that the method only be used for debugging and monitoring purposes. 1560 * 1561 * @param list 1562 * an array into which to put the list of thread groups 1563 * 1564 * @param recurse 1565 * if {@code true}, recursively enumerate all subgroups 1566 * 1567 * @return the number of thread groups put into the array 1568 * 1569 * @throws SecurityException 1570 * if {@linkplain #checkAccess checkAccess} determines that 1571 * the current thread cannot access this thread group 1572 * 1573 */ 1574 int enumerate(ThreadGroupEx[] list, bool recurse) { 1575 checkAccess(); 1576 return enumerate(list, 0, recurse); 1577 } 1578 1579 private int enumerate(ThreadGroupEx[] list, int n, bool recurse) { 1580 int ngroupsSnapshot = 0; 1581 ThreadGroupEx[] groupsSnapshot = null; 1582 synchronized (this) { 1583 if (destroyed) { 1584 return 0; 1585 } 1586 int ng = ngroups; 1587 if (ng > cast(int)list.length - n) { 1588 ng = cast(int)list.length - n; 1589 } 1590 if (ng > 0) { 1591 // System.arraycopy(groups, 0, list, n, ng); 1592 list[n .. n+ng] = groups[0..ng]; 1593 n += ng; 1594 } 1595 if (recurse) { 1596 ngroupsSnapshot = ngroups; 1597 if (groups !is null) { 1598 size_t limit = min(ngroupsSnapshot, groups.length); 1599 groupsSnapshot = groups[0..limit].dup; 1600 } else { 1601 groupsSnapshot = null; 1602 } 1603 } 1604 } 1605 if (recurse) { 1606 for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1607 n = groupsSnapshot[i].enumerate(list, n, true); 1608 } 1609 } 1610 return n; 1611 } 1612 1613 /** 1614 * Stops all threads in this thread group. 1615 * <p> 1616 * First, the {@code checkAccess} method of this thread group is 1617 * called with no arguments; this may result in a security exception. 1618 * <p> 1619 * This method then calls the {@code stop} method on all the 1620 * threads in this thread group and in all of its subgroups. 1621 * 1622 * @throws SecurityException if the current thread is not allowed 1623 * to access this thread group or any of the threads in 1624 * the thread group. 1625 * @see java.lang.SecurityException 1626 * @see java.lang.Thread#stop() 1627 * @see java.lang.ThreadGroupEx#checkAccess() 1628 * @deprecated This method is inherently unsafe. See 1629 * {@link Thread#stop} for details. 1630 */ 1631 // @Deprecated(since="1.2") 1632 // final void stop() { 1633 // if (stopOrSuspend(false)) 1634 // Thread.getThis().stop(); 1635 // } 1636 1637 /** 1638 * Interrupts all threads in this thread group. 1639 * <p> 1640 * First, the {@code checkAccess} method of this thread group is 1641 * called with no arguments; this may result in a security exception. 1642 * <p> 1643 * This method then calls the {@code interrupt} method on all the 1644 * threads in this thread group and in all of its subgroups. 1645 * 1646 * @throws SecurityException if the current thread is not allowed 1647 * to access this thread group or any of the threads in 1648 * the thread group. 1649 * @see java.lang.Thread#interrupt() 1650 * @see java.lang.SecurityException 1651 * @see java.lang.ThreadGroupEx#checkAccess() 1652 */ 1653 final void interrupt() { 1654 int ngroupsSnapshot; 1655 ThreadGroupEx[] groupsSnapshot; 1656 synchronized (this) { 1657 checkAccess(); 1658 // for (int i = 0 ; i < nthreads ; i++) { 1659 // threads[i].interrupt(); 1660 // } 1661 ngroupsSnapshot = ngroups; 1662 if (groups !is null) { 1663 size_t limit = min(ngroupsSnapshot, groups.length); 1664 groupsSnapshot = groups[0..limit].dup; 1665 } else { 1666 groupsSnapshot = null; 1667 } 1668 } 1669 for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1670 groupsSnapshot[i].interrupt(); 1671 } 1672 } 1673 1674 /** 1675 * Suspends all threads in this thread group. 1676 * <p> 1677 * First, the {@code checkAccess} method of this thread group is 1678 * called with no arguments; this may result in a security exception. 1679 * <p> 1680 * This method then calls the {@code suspend} method on all the 1681 * threads in this thread group and in all of its subgroups. 1682 * 1683 * @throws SecurityException if the current thread is not allowed 1684 * to access this thread group or any of the threads in 1685 * the thread group. 1686 * @see java.lang.Thread#suspend() 1687 * @see java.lang.SecurityException 1688 * @see java.lang.ThreadGroupEx#checkAccess() 1689 * @deprecated This method is inherently deadlock-prone. See 1690 * {@link Thread#suspend} for details. 1691 */ 1692 // @Deprecated(since="1.2") 1693 // @SuppressWarnings("deprecation") 1694 // final void suspend() { 1695 // if (stopOrSuspend(true)) 1696 // Thread.getThis().suspend(); 1697 // } 1698 1699 /** 1700 * Helper method: recursively stops or suspends (as directed by the 1701 * bool argument) all of the threads in this thread group and its 1702 * subgroups, except the current thread. This method returns true 1703 * if (and only if) the current thread is found to be in this thread 1704 * group or one of its subgroups. 1705 */ 1706 // @SuppressWarnings("deprecation") 1707 // private bool stopOrSuspend(bool suspend) { 1708 // bool suicide = false; 1709 // Thread us = Thread.getThis(); 1710 // int ngroupsSnapshot; 1711 // ThreadGroupEx[] groupsSnapshot = null; 1712 // synchronized (this) { 1713 // checkAccess(); 1714 // for (int i = 0 ; i < nthreads ; i++) { 1715 // if (threads[i]==us) 1716 // suicide = true; 1717 // else if (suspend) 1718 // threads[i].suspend(); 1719 // else 1720 // threads[i].stop(); 1721 // } 1722 1723 // ngroupsSnapshot = ngroups; 1724 // if (groups !is null) { 1725 // groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); 1726 // } 1727 // } 1728 // for (int i = 0 ; i < ngroupsSnapshot ; i++) 1729 // suicide = groupsSnapshot[i].stopOrSuspend(suspend) || suicide; 1730 1731 // return suicide; 1732 // } 1733 1734 /** 1735 * Resumes all threads in this thread group. 1736 * <p> 1737 * First, the {@code checkAccess} method of this thread group is 1738 * called with no arguments; this may result in a security exception. 1739 * <p> 1740 * This method then calls the {@code resume} method on all the 1741 * threads in this thread group and in all of its sub groups. 1742 * 1743 * @throws SecurityException if the current thread is not allowed to 1744 * access this thread group or any of the threads in the 1745 * thread group. 1746 * @see java.lang.SecurityException 1747 * @see java.lang.Thread#resume() 1748 * @see java.lang.ThreadGroupEx#checkAccess() 1749 * @deprecated This method is used solely in conjunction with 1750 * {@code Thread.suspend} and {@code ThreadGroupEx.suspend}, 1751 * both of which have been deprecated, as they are inherently 1752 * deadlock-prone. See {@link Thread#suspend} for details. 1753 */ 1754 // @Deprecated(since="1.2") 1755 // @SuppressWarnings("deprecation") 1756 // final void resume() { 1757 // int ngroupsSnapshot; 1758 // ThreadGroupEx[] groupsSnapshot; 1759 // synchronized (this) { 1760 // checkAccess(); 1761 // for (int i = 0 ; i < nthreads ; i++) { 1762 // threads[i].resume(); 1763 // } 1764 // ngroupsSnapshot = ngroups; 1765 // if (groups !is null) { 1766 // groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); 1767 // } else { 1768 // groupsSnapshot = null; 1769 // } 1770 // } 1771 // for (int i = 0 ; i < ngroupsSnapshot ; i++) { 1772 // groupsSnapshot[i].resume(); 1773 // } 1774 // } 1775 1776 /** 1777 * Destroys this thread group and all of its subgroups. This thread 1778 * group must be empty, indicating that all threads that had been in 1779 * this thread group have since stopped. 1780 * <p> 1781 * First, the {@code checkAccess} method of this thread group is 1782 * called with no arguments; this may result in a security exception. 1783 * 1784 * @throws IllegalThreadStateException if the thread group is not 1785 * empty or if the thread group has already been destroyed. 1786 * @throws SecurityException if the current thread cannot modify this 1787 * thread group. 1788 * @see java.lang.ThreadGroupEx#checkAccess() 1789 */ 1790 final void destroy() { 1791 int ngroupsSnapshot; 1792 ThreadGroupEx[] groupsSnapshot; 1793 synchronized (this) { 1794 checkAccess(); 1795 if (destroyed || (nthreads > 0)) { 1796 throw new IllegalThreadStateException(); 1797 } 1798 ngroupsSnapshot = ngroups; 1799 if (groups !is null) { 1800 size_t limit = min(ngroupsSnapshot, groups.length); 1801 groupsSnapshot = groups[0..limit].dup; 1802 } else { 1803 groupsSnapshot = null; 1804 } 1805 if (parent !is null) { 1806 destroyed = true; 1807 ngroups = 0; 1808 groups = null; 1809 nthreads = 0; 1810 threads = null; 1811 } 1812 } 1813 for (int i = 0 ; i < ngroupsSnapshot ; i += 1) { 1814 groupsSnapshot[i].destroy(); 1815 } 1816 if (parent !is null) { 1817 parent.remove(this); 1818 } 1819 } 1820 1821 /** 1822 * Adds the specified Thread group to this group. 1823 * @param g the specified Thread group to be added 1824 * @throws IllegalThreadStateException If the Thread group has been destroyed. 1825 */ 1826 private final void add(ThreadGroupEx g){ 1827 synchronized (this) { 1828 if (destroyed) { 1829 throw new IllegalThreadStateException(); 1830 } 1831 if (groups == null) { 1832 groups = new ThreadGroupEx[4]; 1833 } else if (ngroups == groups.length) { 1834 size_t limit = min(ngroups * 2, groups.length); 1835 groups = groups[0..limit].dup; 1836 } 1837 groups[ngroups] = g; 1838 1839 // This is done last so it doesn't matter in case the 1840 // thread is killed 1841 ngroups++; 1842 } 1843 } 1844 1845 /** 1846 * Removes the specified Thread group from this group. 1847 * @param g the Thread group to be removed 1848 * @return if this Thread has already been destroyed. 1849 */ 1850 private void remove(ThreadGroupEx g) { 1851 synchronized (this) { 1852 if (destroyed) { 1853 return; 1854 } 1855 for (int i = 0 ; i < ngroups ; i++) { 1856 if (groups[i] == g) { 1857 ngroups -= 1; 1858 // System.arraycopy(groups, i + 1, groups, i, ngroups - i); 1859 for(int j=i; j<ngroups; j++) 1860 groups[j] = groups[j+1]; 1861 // Zap dangling reference to the dead group so that 1862 // the garbage collector will collect it. 1863 groups[ngroups] = null; 1864 break; 1865 } 1866 } 1867 if (nthreads == 0) { 1868 // TODO: Tasks pending completion -@zxp at 12/19/2018, 4:57:38 PM 1869 // 1870 // notifyAll(); 1871 } 1872 if (daemon && (nthreads == 0) && 1873 (nUnstartedThreads == 0) && (ngroups == 0)) 1874 { 1875 // TODO: Tasks pending completion -@zxp at 12/19/2018, 4:57:42 PM 1876 // 1877 // destroy(); 1878 } 1879 } 1880 } 1881 1882 1883 /** 1884 * Increments the count of unstarted threads in the thread group. 1885 * Unstarted threads are not added to the thread group so that they 1886 * can be collected if they are never started, but they must be 1887 * counted so that daemon thread groups with unstarted threads in 1888 * them are not destroyed. 1889 */ 1890 void addUnstarted() { 1891 synchronized(this) { 1892 if (destroyed) { 1893 throw new IllegalThreadStateException(); 1894 } 1895 nUnstartedThreads++; 1896 } 1897 } 1898 1899 /** 1900 * Adds the specified thread to this thread group. 1901 * 1902 * <p> Note: This method is called from both library code 1903 * and the Virtual Machine. It is called from VM to add 1904 * certain system threads to the system thread group. 1905 * 1906 * @param t 1907 * the Thread to be added 1908 * 1909 * @throws IllegalThreadStateException 1910 * if the Thread group has been destroyed 1911 */ 1912 void add(Thread t) { 1913 synchronized (this) { 1914 if (destroyed) { 1915 throw new IllegalThreadStateException(); 1916 } 1917 if (threads == null) { 1918 threads = new Thread[4]; 1919 } else if (nthreads == threads.length) { 1920 size_t limit = min(nthreads * 2, threads.length); 1921 threads = threads[0..limit].dup; 1922 } 1923 threads[nthreads] = t; 1924 1925 // This is done last so it doesn't matter in case the 1926 // thread is killed 1927 nthreads++; 1928 1929 // The thread is now a fully fledged member of the group, even 1930 // though it may, or may not, have been started yet. It will prevent 1931 // the group from being destroyed so the unstarted Threads count is 1932 // decremented. 1933 nUnstartedThreads--; 1934 } 1935 } 1936 1937 /** 1938 * Notifies the group that the thread {@code t} has failed 1939 * an attempt to start. 1940 * 1941 * <p> The state of this thread group is rolled back as if the 1942 * attempt to start the thread has never occurred. The thread is again 1943 * considered an unstarted member of the thread group, and a subsequent 1944 * attempt to start the thread is permitted. 1945 * 1946 * @param t 1947 * the Thread whose start method was invoked 1948 */ 1949 void threadStartFailed(Thread t) { 1950 synchronized(this) { 1951 remove(t); 1952 nUnstartedThreads++; 1953 } 1954 } 1955 1956 /** 1957 * Notifies the group that the thread {@code t} has terminated. 1958 * 1959 * <p> Destroy the group if all of the following conditions are 1960 * true: this is a daemon thread group; there are no more alive 1961 * or unstarted threads in the group; there are no subgroups in 1962 * this thread group. 1963 * 1964 * @param t 1965 * the Thread that has terminated 1966 */ 1967 void threadTerminated(Thread t) { 1968 synchronized (this) { 1969 remove(t); 1970 1971 if (nthreads == 0) { 1972 // TODO: Tasks pending completion -@zxp at 12/19/2018, 4:57:55 PM 1973 // 1974 // notifyAll(); 1975 } 1976 if (daemon && (nthreads == 0) && 1977 (nUnstartedThreads == 0) && (ngroups == 0)) 1978 { 1979 destroy(); 1980 } 1981 } 1982 } 1983 1984 /** 1985 * Removes the specified Thread from this group. Invoking this method 1986 * on a thread group that has been destroyed has no effect. 1987 * 1988 * @param t 1989 * the Thread to be removed 1990 */ 1991 private void remove(Thread t) { 1992 synchronized (this) { 1993 if (destroyed) { 1994 return; 1995 } 1996 for (int i = 0 ; i < nthreads ; i++) { 1997 if (threads[i] == t) { 1998 // System.arraycopy(threads, i + 1, threads, i, --nthreads - i); 1999 for(int j=i; j<ngroups; j++) 2000 groups[j] = groups[j+1]; 2001 // Zap dangling reference to the dead thread so that 2002 // the garbage collector will collect it. 2003 threads[nthreads] = null; 2004 break; 2005 } 2006 } 2007 } 2008 } 2009 2010 /** 2011 * Prints information about this thread group to the standard 2012 * output. This method is useful only for debugging. 2013 * 2014 */ 2015 void list() { 2016 // list(System.out, 0); 2017 } 2018 // void list(PrintStream out, int indent) { 2019 // int ngroupsSnapshot; 2020 // ThreadGroupEx[] groupsSnapshot; 2021 // synchronized (this) { 2022 // for (int j = 0 ; j < indent ; j++) { 2023 // out.print(" "); 2024 // } 2025 // out.println(this); 2026 // indent += 4; 2027 // for (int i = 0 ; i < nthreads ; i++) { 2028 // for (int j = 0 ; j < indent ; j++) { 2029 // out.print(" "); 2030 // } 2031 // out.println(threads[i]); 2032 // } 2033 // ngroupsSnapshot = ngroups; 2034 // if (groups !is null) { 2035 // groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot); 2036 // } else { 2037 // groupsSnapshot = null; 2038 // } 2039 // } 2040 // for (int i = 0 ; i < ngroupsSnapshot ; i++) { 2041 // groupsSnapshot[i].list(out, indent); 2042 // } 2043 // } 2044 2045 /** 2046 * Called by the Java Virtual Machine when a thread in this 2047 * thread group stops because of an uncaught exception, and the thread 2048 * does not have a specific {@link Thread.UncaughtExceptionHandler} 2049 * installed. 2050 * <p> 2051 * The {@code uncaughtException} method of 2052 * {@code ThreadGroupEx} does the following: 2053 * <ul> 2054 * <li>If this thread group has a parent thread group, the 2055 * {@code uncaughtException} method of that parent is called 2056 * with the same two arguments. 2057 * <li>Otherwise, this method checks to see if there is a 2058 * {@linkplain Thread#getDefaultUncaughtExceptionHandler default 2059 * uncaught exception handler} installed, and if so, its 2060 * {@code uncaughtException} method is called with the same 2061 * two arguments. 2062 * <li>Otherwise, this method determines if the {@code Throwable} 2063 * argument is an instance of {@link ThreadDeath}. If so, nothing 2064 * special is done. Otherwise, a message containing the 2065 * thread's name, as returned from the thread's {@link 2066 * Thread#getName getName} method, and a stack backtrace, 2067 * using the {@code Throwable}'s {@link 2068 * Throwable#printStackTrace printStackTrace} method, is 2069 * printed to the {@linkplain System#err standard error stream}. 2070 * </ul> 2071 * <p> 2072 * Applications can override this method in subclasses of 2073 * {@code ThreadGroupEx} to provide alternative handling of 2074 * uncaught exceptions. 2075 * 2076 * @param t the thread that is about to exit. 2077 * @param e the uncaught exception. 2078 */ 2079 void uncaughtException(Thread t, Throwable e) { 2080 if (parent !is null) { 2081 parent.uncaughtException(t, e); 2082 } else { 2083 // Thread.UncaughtExceptionHandler ueh = 2084 // Thread.getDefaultUncaughtExceptionHandler(); 2085 // if (ueh !is null) { 2086 // ueh.uncaughtException(t, e); 2087 // } else if (!(e instanceof ThreadDeath)) { 2088 // System.err.print("Exception in thread \"" 2089 // + t.getName() + "\" "); 2090 // e.printStackTrace(System.err); 2091 // } 2092 } 2093 } 2094 2095 /** 2096 * Used by VM to control lowmem implicit suspension. 2097 * 2098 * @param b bool to allow or disallow suspension 2099 * @return true on success 2100 * @deprecated The definition of this call depends on {@link #suspend}, 2101 * which is deprecated. Further, the behavior of this call 2102 * was never specified. 2103 */ 2104 // @Deprecated(since="1.2") 2105 // bool allowThreadSuspension(bool b) { 2106 // return true; 2107 // } 2108 2109 /** 2110 * Returns a string representation of this Thread group. 2111 * 2112 * @return a string representation of this thread group. 2113 */ 2114 // string toString() { 2115 // return getClass().getName() + "[name=" + getName() + ",maxpri=" + maxPriority + "]"; 2116 // } 2117 }