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