1 /** 2 * The threadbase module provides OS-independent code 3 * for thread storage and management. 4 * 5 * Copyright: Copyright Sean Kelly 2005 - 2012. 6 * License: Distributed under the 7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 8 * (See accompanying file LICENSE) 9 * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak 10 * Source: $(DRUNTIMESRC core/thread/osthread.d) 11 */ 12 13 module core.thread.threadbase; 14 15 import core.thread.context; 16 import core.thread.types; 17 import core.time; 18 import core.sync.mutex; 19 import core.stdc.stdlib : free, realloc; 20 21 private 22 { 23 import core.internal.traits : externDFunc; 24 25 // interface to rt.tlsgc 26 alias rt_tlsgc_init = externDFunc!("rt.tlsgc.init", void* function() nothrow @nogc); 27 alias rt_tlsgc_destroy = externDFunc!("rt.tlsgc.destroy", void function(void*) nothrow @nogc); 28 29 alias ScanDg = void delegate(void* pstart, void* pend) nothrow; 30 alias rt_tlsgc_scan = 31 externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow); 32 33 alias rt_tlsgc_processGCMarks = 34 externDFunc!("rt.tlsgc.processGCMarks", void function(void*, scope IsMarkedDg) nothrow); 35 } 36 37 38 /////////////////////////////////////////////////////////////////////////////// 39 // Thread and Fiber Exceptions 40 /////////////////////////////////////////////////////////////////////////////// 41 42 43 /** 44 * Base class for thread exceptions. 45 */ 46 class ThreadException : Exception 47 { 48 @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 49 { 50 super(msg, file, line, next); 51 } 52 53 @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) 54 { 55 super(msg, file, line, next); 56 } 57 } 58 59 60 /** 61 * Base class for thread errors to be used for function inside GC when allocations are unavailable. 62 */ 63 class ThreadError : Error 64 { 65 @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 66 { 67 super(msg, file, line, next); 68 } 69 70 @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) 71 { 72 super(msg, file, line, next); 73 } 74 } 75 76 private 77 { 78 // Handling unaligned mutexes are not supported on all platforms, so we must 79 // ensure that the address of all shared data are appropriately aligned. 80 enum mutexAlign = __traits(classInstanceAlignment, Mutex); 81 enum mutexClassInstanceSize = __traits(classInstanceSize, Mutex); 82 83 alias swapContext = externDFunc!("core.thread.osthread.swapContext", void* function(void*) nothrow @nogc); 84 85 alias getStackBottom = externDFunc!("core.thread.osthread.getStackBottom", void* function() nothrow @nogc); 86 alias getStackTop = externDFunc!("core.thread.osthread.getStackTop", void* function() nothrow @nogc); 87 } 88 89 90 /////////////////////////////////////////////////////////////////////////////// 91 // Thread 92 /////////////////////////////////////////////////////////////////////////////// 93 94 95 class ThreadBase 96 { 97 /////////////////////////////////////////////////////////////////////////// 98 // Initialization 99 /////////////////////////////////////////////////////////////////////////// 100 101 this(void function() fn, size_t sz = 0) @safe pure nothrow @nogc 102 in(fn) 103 { 104 this(sz); 105 m_call = fn; 106 } 107 108 this(void delegate() dg, size_t sz = 0) @trusted pure nothrow @nogc 109 in( cast(const void delegate()) dg) 110 { 111 this(sz); 112 m_call = dg; 113 } 114 115 /** 116 * Cleans up any remaining resources used by this object. 117 */ 118 package bool destructBeforeDtor() nothrow @nogc 119 { 120 destroyDataStorageIfAvail(); 121 122 bool no_context = m_addr == m_addr.init; 123 bool not_registered = !next && !prev && (sm_tbeg !is this); 124 125 return (no_context || not_registered); 126 } 127 128 package void tlsGCdataInit() nothrow @nogc 129 { 130 m_tlsgcdata = rt_tlsgc_init(); 131 } 132 133 package void initDataStorage() nothrow 134 { 135 assert(m_curr is &m_main); 136 137 m_main.bstack = getStackBottom(); 138 m_main.tstack = m_main.bstack; 139 tlsGCdataInit(); 140 } 141 142 package void destroyDataStorage() nothrow @nogc 143 { 144 rt_tlsgc_destroy(m_tlsgcdata); 145 m_tlsgcdata = null; 146 } 147 148 package void destroyDataStorageIfAvail() nothrow @nogc 149 { 150 if (m_tlsgcdata) 151 destroyDataStorage(); 152 } 153 154 155 /////////////////////////////////////////////////////////////////////////// 156 // General Actions 157 /////////////////////////////////////////////////////////////////////////// 158 159 160 /** 161 * Waits for this thread to complete. If the thread terminated as the 162 * result of an unhandled exception, this exception will be rethrown. 163 * 164 * Params: 165 * rethrow = Rethrow any unhandled exception which may have caused this 166 * thread to terminate. 167 * 168 * Throws: 169 * ThreadException if the operation fails. 170 * Any exception not handled by the joined thread. 171 * 172 * Returns: 173 * Any exception not handled by this thread if rethrow = false, null 174 * otherwise. 175 */ 176 abstract Throwable join(bool rethrow = true); 177 178 179 /////////////////////////////////////////////////////////////////////////// 180 // General Properties 181 /////////////////////////////////////////////////////////////////////////// 182 183 184 /** 185 * Gets the OS identifier for this thread. 186 * 187 * Returns: 188 * If the thread hasn't been started yet, returns $(LREF ThreadID)$(D.init). 189 * Otherwise, returns the result of $(D GetCurrentThreadId) on Windows, 190 * and $(D pthread_self) on POSIX. 191 * 192 * The value is unique for the current process. 193 */ 194 final @property ThreadID id() @safe @nogc 195 { 196 synchronized(this) 197 { 198 return m_addr; 199 } 200 } 201 202 203 /** 204 * Gets the user-readable label for this thread. 205 * 206 * Returns: 207 * The name of this thread. 208 */ 209 final @property string name() @safe @nogc 210 { 211 synchronized(this) 212 { 213 return m_name; 214 } 215 } 216 217 218 /** 219 * Sets the user-readable label for this thread. 220 * 221 * Params: 222 * val = The new name of this thread. 223 */ 224 final @property void name(string val) @safe @nogc 225 { 226 synchronized(this) 227 { 228 m_name = val; 229 } 230 } 231 232 233 /** 234 * Gets the daemon status for this thread. While the runtime will wait for 235 * all normal threads to complete before tearing down the process, daemon 236 * threads are effectively ignored and thus will not prevent the process 237 * from terminating. In effect, daemon threads will be terminated 238 * automatically by the OS when the process exits. 239 * 240 * Returns: 241 * true if this is a daemon thread. 242 */ 243 final @property bool isDaemon() @safe @nogc 244 { 245 synchronized(this) 246 { 247 return m_isDaemon; 248 } 249 } 250 251 252 /** 253 * Sets the daemon status for this thread. While the runtime will wait for 254 * all normal threads to complete before tearing down the process, daemon 255 * threads are effectively ignored and thus will not prevent the process 256 * from terminating. In effect, daemon threads will be terminated 257 * automatically by the OS when the process exits. 258 * 259 * Params: 260 * val = The new daemon status for this thread. 261 */ 262 final @property void isDaemon(bool val) @safe @nogc 263 { 264 synchronized(this) 265 { 266 m_isDaemon = val; 267 } 268 } 269 270 /** 271 * Tests whether this thread is the main thread, i.e. the thread 272 * that initialized the runtime 273 * 274 * Returns: 275 * true if the thread is the main thread 276 */ 277 final @property bool isMainThread() nothrow @nogc 278 { 279 return this is sm_main; 280 } 281 282 /** 283 * Tests whether this thread is running. 284 * 285 * Returns: 286 * true if the thread is running, false if not. 287 */ 288 @property bool isRunning() nothrow @nogc 289 { 290 if (m_addr == m_addr.init) 291 return false; 292 293 return true; 294 } 295 296 297 /////////////////////////////////////////////////////////////////////////// 298 // Thread Accessors 299 /////////////////////////////////////////////////////////////////////////// 300 301 /** 302 * Provides a reference to the calling thread. 303 * 304 * Returns: 305 * The thread object representing the calling thread. The result of 306 * deleting this object is undefined. If the current thread is not 307 * attached to the runtime, a null reference is returned. 308 */ 309 static ThreadBase getThis() @safe nothrow @nogc 310 { 311 // NOTE: This function may not be called until thread_init has 312 // completed. See thread_suspendAll for more information 313 // on why this might occur. 314 return sm_this; 315 } 316 317 318 /** 319 * Provides a list of all threads currently being tracked by the system. 320 * Note that threads in the returned array might no longer run (see 321 * $(D ThreadBase.)$(LREF isRunning)). 322 * 323 * Returns: 324 * An array containing references to all threads currently being 325 * tracked by the system. The result of deleting any contained 326 * objects is undefined. 327 */ 328 static ThreadBase[] getAll() 329 { 330 static void resize(ref ThreadBase[] buf, size_t nlen) 331 { 332 buf.length = nlen; 333 } 334 return getAllImpl!resize(); 335 } 336 337 338 /** 339 * Operates on all threads currently being tracked by the system. The 340 * result of deleting any Thread object is undefined. 341 * Note that threads passed to the callback might no longer run (see 342 * $(D ThreadBase.)$(LREF isRunning)). 343 * 344 * Params: 345 * dg = The supplied code as a delegate. 346 * 347 * Returns: 348 * Zero if all elemented are visited, nonzero if not. 349 */ 350 static int opApply(scope int delegate(ref ThreadBase) dg) 351 { 352 static void resize(ref ThreadBase[] buf, size_t nlen) 353 { 354 import core.exception: onOutOfMemoryError; 355 356 auto newBuf = cast(ThreadBase*)realloc(buf.ptr, nlen * size_t.sizeof); 357 if (newBuf is null) onOutOfMemoryError(); 358 buf = newBuf[0 .. nlen]; 359 } 360 auto buf = getAllImpl!resize; 361 scope(exit) if (buf.ptr) free(buf.ptr); 362 363 foreach (t; buf) 364 { 365 if (auto res = dg(t)) 366 return res; 367 } 368 return 0; 369 } 370 371 private static ThreadBase[] getAllImpl(alias resize)() 372 { 373 import core.atomic; 374 375 ThreadBase[] buf; 376 while (true) 377 { 378 immutable len = atomicLoad!(MemoryOrder.raw)(*cast(shared)&sm_tlen); 379 resize(buf, len); 380 assert(buf.length == len); 381 synchronized (slock) 382 { 383 if (len == sm_tlen) 384 { 385 size_t pos; 386 for (ThreadBase t = sm_tbeg; t; t = t.next) 387 buf[pos++] = t; 388 return buf; 389 } 390 } 391 } 392 } 393 394 /////////////////////////////////////////////////////////////////////////// 395 // Actions on Calling Thread 396 /////////////////////////////////////////////////////////////////////////// 397 398 /** 399 * Forces a context switch to occur away from the calling thread. 400 */ 401 private static void yield() @nogc nothrow 402 { 403 thread_yield(); 404 } 405 406 /////////////////////////////////////////////////////////////////////////// 407 // Stuff That Should Go Away 408 /////////////////////////////////////////////////////////////////////////// 409 410 411 // 412 // Initializes a thread object which has no associated executable function. 413 // This is used for the main thread initialized in thread_init(). 414 // 415 package this(size_t sz = 0) @safe pure nothrow @nogc 416 { 417 m_sz = sz; 418 m_curr = &m_main; 419 } 420 421 // 422 // Thread entry point. Invokes the function or delegate passed on 423 // construction (if any). 424 // 425 package final void run() 426 { 427 m_call(); 428 } 429 430 package: 431 432 // 433 // Local storage 434 // 435 static ThreadBase sm_this; 436 437 438 // 439 // Main process thread 440 // 441 __gshared ThreadBase sm_main; 442 443 444 // 445 // Standard thread data 446 // 447 ThreadID m_addr; 448 Callable m_call; 449 string m_name; 450 size_t m_sz; 451 bool m_isDaemon; 452 bool m_isInCriticalRegion; 453 Throwable m_unhandled; 454 455 /////////////////////////////////////////////////////////////////////////// 456 // Storage of Active Thread 457 /////////////////////////////////////////////////////////////////////////// 458 459 460 // 461 // Sets a thread-local reference to the current thread object. 462 // 463 package static void setThis(ThreadBase t) nothrow @nogc 464 { 465 sm_this = t; 466 } 467 468 package(core.thread): 469 470 StackContext m_main; 471 StackContext* m_curr; 472 bool m_lock; 473 private void* m_tlsgcdata; 474 475 /////////////////////////////////////////////////////////////////////////// 476 // Thread Context and GC Scanning Support 477 /////////////////////////////////////////////////////////////////////////// 478 479 480 final void pushContext(StackContext* c) nothrow @nogc 481 in 482 { 483 assert(!c.within); 484 } 485 do 486 { 487 m_curr.ehContext = swapContext(c.ehContext); 488 c.within = m_curr; 489 m_curr = c; 490 } 491 492 493 final void popContext() nothrow @nogc 494 in 495 { 496 assert(m_curr && m_curr.within); 497 } 498 do 499 { 500 StackContext* c = m_curr; 501 m_curr = c.within; 502 c.ehContext = swapContext(m_curr.ehContext); 503 c.within = null; 504 } 505 506 private final StackContext* topContext() nothrow @nogc 507 in(m_curr) 508 { 509 return m_curr; 510 } 511 512 513 package(core.thread): 514 /////////////////////////////////////////////////////////////////////////// 515 // GC Scanning Support 516 /////////////////////////////////////////////////////////////////////////// 517 518 519 // NOTE: The GC scanning process works like so: 520 // 521 // 1. Suspend all threads. 522 // 2. Scan the stacks of all suspended threads for roots. 523 // 3. Resume all threads. 524 // 525 // Step 1 and 3 require a list of all threads in the system, while 526 // step 2 requires a list of all thread stacks (each represented by 527 // a Context struct). Traditionally, there was one stack per thread 528 // and the Context structs were not necessary. However, Fibers have 529 // changed things so that each thread has its own 'main' stack plus 530 // an arbitrary number of nested stacks (normally referenced via 531 // m_curr). Also, there may be 'free-floating' stacks in the system, 532 // which are Fibers that are not currently executing on any specific 533 // thread but are still being processed and still contain valid 534 // roots. 535 // 536 // To support all of this, the Context struct has been created to 537 // represent a stack range, and a global list of Context structs has 538 // been added to enable scanning of these stack ranges. The lifetime 539 // (and presence in the Context list) of a thread's 'main' stack will 540 // be equivalent to the thread's lifetime. So the Ccontext will be 541 // added to the list on thread entry, and removed from the list on 542 // thread exit (which is essentially the same as the presence of a 543 // Thread object in its own global list). The lifetime of a Fiber's 544 // context, however, will be tied to the lifetime of the Fiber object 545 // itself, and Fibers are expected to add/remove their Context struct 546 // on construction/deletion. 547 548 549 // 550 // All use of the global thread lists/array should synchronize on this lock. 551 // 552 // Careful as the GC acquires this lock after the GC lock to suspend all 553 // threads any GC usage with slock held can result in a deadlock through 554 // lock order inversion. 555 @property static Mutex slock() nothrow @nogc 556 { 557 return cast(Mutex)_slock.ptr; 558 } 559 560 @property static Mutex criticalRegionLock() nothrow @nogc 561 { 562 return cast(Mutex)_criticalRegionLock.ptr; 563 } 564 565 __gshared align(mutexAlign) void[mutexClassInstanceSize] _slock; 566 __gshared align(mutexAlign) void[mutexClassInstanceSize] _criticalRegionLock; 567 568 static void initLocks() @nogc nothrow 569 { 570 import core.lifetime : emplace; 571 emplace!Mutex(_slock[]); 572 emplace!Mutex(_criticalRegionLock[]); 573 } 574 575 static void termLocks() @nogc nothrow 576 { 577 (cast(Mutex)_slock.ptr).__dtor(); 578 (cast(Mutex)_criticalRegionLock.ptr).__dtor(); 579 } 580 581 __gshared StackContext* sm_cbeg; 582 583 __gshared ThreadBase sm_tbeg; 584 __gshared size_t sm_tlen; 585 586 // can't use core.internal.util.array in public code 587 __gshared ThreadBase* pAboutToStart; 588 __gshared size_t nAboutToStart; 589 590 // 591 // Used for ordering threads in the global thread list. 592 // 593 ThreadBase prev; 594 ThreadBase next; 595 596 597 /////////////////////////////////////////////////////////////////////////// 598 // Global Context List Operations 599 /////////////////////////////////////////////////////////////////////////// 600 601 602 // 603 // Add a context to the global context list. 604 // 605 static void add(StackContext* c) nothrow @nogc 606 in 607 { 608 assert(c); 609 assert(!c.next && !c.prev); 610 } 611 do 612 { 613 slock.lock_nothrow(); 614 scope(exit) slock.unlock_nothrow(); 615 assert(!suspendDepth); // must be 0 b/c it's only set with slock held 616 617 if (sm_cbeg) 618 { 619 c.next = sm_cbeg; 620 sm_cbeg.prev = c; 621 } 622 sm_cbeg = c; 623 } 624 625 // 626 // Remove a context from the global context list. 627 // 628 // This assumes slock being acquired. This isn't done here to 629 // avoid double locking when called from remove(Thread) 630 static void remove(StackContext* c) nothrow @nogc 631 in 632 { 633 assert(c); 634 assert(c.next || c.prev); 635 } 636 do 637 { 638 if (c.prev) 639 c.prev.next = c.next; 640 if (c.next) 641 c.next.prev = c.prev; 642 if (sm_cbeg == c) 643 sm_cbeg = c.next; 644 // NOTE: Don't null out c.next or c.prev because opApply currently 645 // follows c.next after removing a node. This could be easily 646 // addressed by simply returning the next node from this 647 // function, however, a context should never be re-added to the 648 // list anyway and having next and prev be non-null is a good way 649 // to ensure that. 650 } 651 652 653 /////////////////////////////////////////////////////////////////////////// 654 // Global Thread List Operations 655 /////////////////////////////////////////////////////////////////////////// 656 657 658 // 659 // Add a thread to the global thread list. 660 // 661 static void add(ThreadBase t, bool rmAboutToStart = true) nothrow @nogc @system 662 in 663 { 664 assert(t); 665 assert(!t.next && !t.prev); 666 } 667 do 668 { 669 slock.lock_nothrow(); 670 scope(exit) slock.unlock_nothrow(); 671 assert(t.isRunning); // check this with slock to ensure pthread_create already returned 672 assert(!suspendDepth); // must be 0 b/c it's only set with slock held 673 674 if (rmAboutToStart) 675 { 676 size_t idx = -1; 677 foreach (i, thr; pAboutToStart[0 .. nAboutToStart]) 678 { 679 if (thr is t) 680 { 681 idx = i; 682 break; 683 } 684 } 685 assert(idx != -1); 686 import core.stdc.string : memmove; 687 memmove(pAboutToStart + idx, pAboutToStart + idx + 1, size_t.sizeof * (nAboutToStart - idx - 1)); 688 pAboutToStart = 689 cast(ThreadBase*)realloc(pAboutToStart, size_t.sizeof * --nAboutToStart); 690 } 691 692 if (sm_tbeg) 693 { 694 t.next = sm_tbeg; 695 sm_tbeg.prev = t; 696 } 697 sm_tbeg = t; 698 ++sm_tlen; 699 } 700 701 702 // 703 // Remove a thread from the global thread list. 704 // 705 static void remove(ThreadBase t) nothrow @nogc 706 in 707 { 708 assert(t); 709 } 710 do 711 { 712 // Thread was already removed earlier, might happen b/c of thread_detachInstance 713 if (!t.next && !t.prev && (sm_tbeg !is t)) 714 return; 715 716 slock.lock_nothrow(); 717 { 718 // NOTE: When a thread is removed from the global thread list its 719 // main context is invalid and should be removed as well. 720 // It is possible that t.m_curr could reference more 721 // than just the main context if the thread exited abnormally 722 // (if it was terminated), but we must assume that the user 723 // retains a reference to them and that they may be re-used 724 // elsewhere. Therefore, it is the responsibility of any 725 // object that creates contexts to clean them up properly 726 // when it is done with them. 727 remove(&t.m_main); 728 729 if (t.prev) 730 t.prev.next = t.next; 731 if (t.next) 732 t.next.prev = t.prev; 733 if (sm_tbeg is t) 734 sm_tbeg = t.next; 735 t.prev = t.next = null; 736 --sm_tlen; 737 } 738 // NOTE: Don't null out t.next or t.prev because opApply currently 739 // follows t.next after removing a node. This could be easily 740 // addressed by simply returning the next node from this 741 // function, however, a thread should never be re-added to the 742 // list anyway and having next and prev be non-null is a good way 743 // to ensure that. 744 slock.unlock_nothrow(); 745 } 746 } 747 748 749 /////////////////////////////////////////////////////////////////////////////// 750 // GC Support Routines 751 /////////////////////////////////////////////////////////////////////////////// 752 753 private alias attachThread = externDFunc!("core.thread.osthread.attachThread", ThreadBase function(ThreadBase) @nogc nothrow); 754 755 extern (C) void _d_monitordelete_nogc(Object h) @nogc nothrow; 756 757 /** 758 * Terminates the thread module. No other thread routine may be called 759 * afterwards. 760 */ 761 package void thread_term_tpl(ThreadT, MainThreadStore)(ref MainThreadStore _mainThreadStore) @nogc nothrow 762 { 763 assert(_mainThreadStore.ptr is cast(void*) ThreadBase.sm_main); 764 765 // destruct manually as object.destroy is not @nogc 766 (cast(ThreadT) cast(void*) ThreadBase.sm_main).__dtor(); 767 _d_monitordelete_nogc(ThreadBase.sm_main); 768 _mainThreadStore[] = __traits(initSymbol, ThreadT)[]; 769 ThreadBase.sm_main = null; 770 771 assert(ThreadBase.sm_tbeg && ThreadBase.sm_tlen == 1); 772 assert(!ThreadBase.nAboutToStart); 773 if (ThreadBase.pAboutToStart) // in case realloc(p, 0) doesn't return null 774 { 775 free(ThreadBase.pAboutToStart); 776 ThreadBase.pAboutToStart = null; 777 } 778 ThreadBase.termLocks(); 779 termLowlevelThreads(); 780 } 781 782 783 /** 784 * 785 */ 786 extern (C) bool thread_isMainThread() nothrow @nogc 787 { 788 return ThreadBase.getThis() is ThreadBase.sm_main; 789 } 790 791 792 /** 793 * Registers the calling thread for use with the D Runtime. If this routine 794 * is called for a thread which is already registered, no action is performed. 795 * 796 * NOTE: This routine does not run thread-local static constructors when called. 797 * If full functionality as a D thread is desired, the following function 798 * must be called after thread_attachThis: 799 * 800 * extern (C) void rt_moduleTlsCtor(); 801 */ 802 package ThreadT thread_attachThis_tpl(ThreadT)() 803 { 804 if (auto t = ThreadT.getThis()) 805 return t; 806 807 return cast(ThreadT) attachThread(new ThreadT()); 808 } 809 810 811 /** 812 * Deregisters the calling thread from use with the runtime. If this routine 813 * is called for a thread which is not registered, the result is undefined. 814 * 815 * NOTE: This routine does not run thread-local static destructors when called. 816 * If full functionality as a D thread is desired, the following function 817 * must be called before thread_detachThis, particularly if the thread is 818 * being detached at some indeterminate time before program termination: 819 * 820 * $(D extern(C) void rt_moduleTlsDtor();) 821 * 822 * See_Also: 823 * $(REF thread_attachThis, core,thread,osthread) 824 */ 825 extern (C) void thread_detachThis() nothrow @nogc 826 { 827 if (auto t = ThreadBase.getThis()) 828 ThreadBase.remove(t); 829 } 830 831 832 /** 833 * Deregisters the given thread from use with the runtime. If this routine 834 * is called for a thread which is not registered, the result is undefined. 835 * 836 * NOTE: This routine does not run thread-local static destructors when called. 837 * If full functionality as a D thread is desired, the following function 838 * must be called by the detached thread, particularly if the thread is 839 * being detached at some indeterminate time before program termination: 840 * 841 * $(D extern(C) void rt_moduleTlsDtor();) 842 */ 843 extern (C) void thread_detachByAddr(ThreadID addr) 844 { 845 if (auto t = thread_findByAddr(addr)) 846 ThreadBase.remove(t); 847 } 848 849 850 /// ditto 851 extern (C) void thread_detachInstance(ThreadBase t) nothrow @nogc 852 { 853 ThreadBase.remove(t); 854 } 855 856 857 /** 858 * Search the list of all threads for a thread with the given thread identifier. 859 * 860 * Params: 861 * addr = The thread identifier to search for. 862 * Returns: 863 * The thread object associated with the thread identifier, null if not found. 864 */ 865 static ThreadBase thread_findByAddr(ThreadID addr) 866 { 867 ThreadBase.slock.lock_nothrow(); 868 scope(exit) ThreadBase.slock.unlock_nothrow(); 869 870 // also return just spawned thread so that 871 // DLL_THREAD_ATTACH knows it's a D thread 872 foreach (t; ThreadBase.pAboutToStart[0 .. ThreadBase.nAboutToStart]) 873 if (t.m_addr == addr) 874 return t; 875 876 foreach (t; ThreadBase) 877 if (t.m_addr == addr) 878 return t; 879 880 return null; 881 } 882 883 884 /** 885 * Sets the current thread to a specific reference. Only to be used 886 * when dealing with externally-created threads (in e.g. C code). 887 * The primary use of this function is when ThreadBase.getThis() must 888 * return a sensible value in, for example, TLS destructors. In 889 * other words, don't touch this unless you know what you're doing. 890 * 891 * Params: 892 * t = A reference to the current thread. May be null. 893 */ 894 extern (C) void thread_setThis(ThreadBase t) nothrow @nogc 895 { 896 ThreadBase.setThis(t); 897 } 898 899 900 /** 901 * Joins all non-daemon threads that are currently running. This is done by 902 * performing successive scans through the thread list until a scan consists 903 * of only daemon threads. 904 */ 905 extern (C) void thread_joinAll() 906 { 907 Lagain: 908 ThreadBase.slock.lock_nothrow(); 909 // wait for just spawned threads 910 if (ThreadBase.nAboutToStart) 911 { 912 ThreadBase.slock.unlock_nothrow(); 913 ThreadBase.yield(); 914 goto Lagain; 915 } 916 917 // join all non-daemon threads, the main thread is also a daemon 918 auto t = ThreadBase.sm_tbeg; 919 while (t) 920 { 921 if (!t.isRunning) 922 { 923 auto tn = t.next; 924 ThreadBase.remove(t); 925 t = tn; 926 } 927 else if (t.isDaemon) 928 { 929 t = t.next; 930 } 931 else 932 { 933 ThreadBase.slock.unlock_nothrow(); 934 t.join(); // might rethrow 935 goto Lagain; // must restart iteration b/c of unlock 936 } 937 } 938 ThreadBase.slock.unlock_nothrow(); 939 } 940 941 942 /** 943 * Performs intermediate shutdown of the thread module. 944 */ 945 shared static ~this() 946 { 947 // NOTE: The functionality related to garbage collection must be minimally 948 // operable after this dtor completes. Therefore, only minimal 949 // cleanup may occur. 950 auto t = ThreadBase.sm_tbeg; 951 while (t) 952 { 953 auto tn = t.next; 954 if (!t.isRunning) 955 ThreadBase.remove(t); 956 t = tn; 957 } 958 } 959 960 // Used for needLock below. 961 package __gshared bool multiThreadedFlag = false; 962 963 // Used for suspendAll/resumeAll below. 964 package __gshared uint suspendDepth = 0; 965 966 private alias resume = externDFunc!("core.thread.osthread.resume", void function(ThreadBase) nothrow @nogc); 967 968 /** 969 * Resume all threads but the calling thread for "stop the world" garbage 970 * collection runs. This function must be called once for each preceding 971 * call to thread_suspendAll before the threads are actually resumed. 972 * 973 * In: 974 * This routine must be preceded by a call to thread_suspendAll. 975 * 976 * Throws: 977 * ThreadError if the resume operation fails for a running thread. 978 */ 979 extern (C) void thread_resumeAll() nothrow 980 in 981 { 982 assert(suspendDepth > 0); 983 } 984 do 985 { 986 // NOTE: See thread_suspendAll for the logic behind this. 987 if (!multiThreadedFlag && ThreadBase.sm_tbeg) 988 { 989 if (--suspendDepth == 0) 990 resume(ThreadBase.getThis()); 991 return; 992 } 993 994 scope(exit) ThreadBase.slock.unlock_nothrow(); 995 { 996 if (--suspendDepth > 0) 997 return; 998 999 for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next) 1000 { 1001 // NOTE: We do not need to care about critical regions at all 1002 // here. thread_suspendAll takes care of everything. 1003 resume(t); 1004 } 1005 } 1006 } 1007 1008 /** 1009 * Indicates the kind of scan being performed by $(D thread_scanAllType). 1010 */ 1011 enum ScanType 1012 { 1013 stack, /// The stack and/or registers are being scanned. 1014 tls, /// TLS data is being scanned. 1015 } 1016 1017 alias ScanAllThreadsFn = void delegate(void*, void*) nothrow; /// The scanning function. 1018 alias ScanAllThreadsTypeFn = void delegate(ScanType, void*, void*) nothrow; /// ditto 1019 1020 /** 1021 * The main entry point for garbage collection. The supplied delegate 1022 * will be passed ranges representing both stack and register values. 1023 * 1024 * Params: 1025 * scan = The scanner function. It should scan from p1 through p2 - 1. 1026 * 1027 * In: 1028 * This routine must be preceded by a call to thread_suspendAll. 1029 */ 1030 extern (C) void thread_scanAllType(scope ScanAllThreadsTypeFn scan) nothrow 1031 in 1032 { 1033 assert(suspendDepth > 0); 1034 } 1035 do 1036 { 1037 callWithStackShell(sp => scanAllTypeImpl(scan, sp)); 1038 } 1039 1040 package alias callWithStackShellDg = void delegate(void* sp) nothrow; 1041 private alias callWithStackShell = externDFunc!("core.thread.osthread.callWithStackShell", void function(scope callWithStackShellDg) nothrow); 1042 1043 private void scanAllTypeImpl(scope ScanAllThreadsTypeFn scan, void* curStackTop) nothrow @system 1044 { 1045 ThreadBase thisThread = null; 1046 void* oldStackTop = null; 1047 1048 if (ThreadBase.sm_tbeg) 1049 { 1050 thisThread = ThreadBase.getThis(); 1051 if (!thisThread.m_lock) 1052 { 1053 oldStackTop = thisThread.m_curr.tstack; 1054 thisThread.m_curr.tstack = curStackTop; 1055 } 1056 } 1057 1058 scope(exit) 1059 { 1060 if (ThreadBase.sm_tbeg) 1061 { 1062 if (!thisThread.m_lock) 1063 { 1064 thisThread.m_curr.tstack = oldStackTop; 1065 } 1066 } 1067 } 1068 1069 // NOTE: Synchronizing on ThreadBase.slock is not needed because this 1070 // function may only be called after all other threads have 1071 // been suspended from within the same lock. 1072 if (ThreadBase.nAboutToStart) 1073 scan(ScanType.stack, ThreadBase.pAboutToStart, ThreadBase.pAboutToStart + ThreadBase.nAboutToStart); 1074 1075 for (StackContext* c = ThreadBase.sm_cbeg; c; c = c.next) 1076 { 1077 static if (isStackGrowingDown) 1078 { 1079 assert(c.tstack <= c.bstack, "stack bottom can't be less than top"); 1080 1081 // NOTE: We can't index past the bottom of the stack 1082 // so don't do the "+1" if isStackGrowingDown. 1083 if (c.tstack && c.tstack < c.bstack) 1084 scan(ScanType.stack, c.tstack, c.bstack); 1085 } 1086 else 1087 { 1088 assert(c.bstack <= c.tstack, "stack top can't be less than bottom"); 1089 1090 if (c.bstack && c.bstack < c.tstack) 1091 scan(ScanType.stack, c.bstack, c.tstack + 1); 1092 } 1093 } 1094 1095 for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next) 1096 { 1097 version (Windows) 1098 { 1099 // Ideally, we'd pass ScanType.regs or something like that, but this 1100 // would make portability annoying because it only makes sense on Windows. 1101 scanWindowsOnly(scan, t); 1102 } 1103 1104 if (t.m_tlsgcdata !is null) 1105 rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2)); 1106 } 1107 } 1108 1109 version (Windows) 1110 { 1111 // Currently scanWindowsOnly can't be handled properly by externDFunc 1112 // https://github.com/dlang/druntime/pull/3135#issuecomment-643673218 1113 pragma(mangle, "_D4core6thread8osthread15scanWindowsOnlyFNbMDFNbEQBvQBt10threadbase8ScanTypePvQcZvCQDdQDbQBi10ThreadBaseZv") 1114 private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase) nothrow; 1115 } 1116 1117 /** 1118 * The main entry point for garbage collection. The supplied delegate 1119 * will be passed ranges representing both stack and register values. 1120 * 1121 * Params: 1122 * scan = The scanner function. It should scan from p1 through p2 - 1. 1123 * 1124 * In: 1125 * This routine must be preceded by a call to thread_suspendAll. 1126 */ 1127 extern (C) void thread_scanAll(scope ScanAllThreadsFn scan) nothrow 1128 { 1129 thread_scanAllType((type, p1, p2) => scan(p1, p2)); 1130 } 1131 1132 private alias thread_yield = externDFunc!("core.thread.osthread.thread_yield", void function() @nogc nothrow); 1133 1134 /** 1135 * Signals that the code following this call is a critical region. Any code in 1136 * this region must finish running before the calling thread can be suspended 1137 * by a call to thread_suspendAll. 1138 * 1139 * This function is, in particular, meant to help maintain garbage collector 1140 * invariants when a lock is not used. 1141 * 1142 * A critical region is exited with thread_exitCriticalRegion. 1143 * 1144 * $(RED Warning): 1145 * Using critical regions is extremely error-prone. For instance, using locks 1146 * inside a critical region can easily result in a deadlock when another thread 1147 * holding the lock already got suspended. 1148 * 1149 * The term and concept of a 'critical region' comes from 1150 * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925, Mono's SGen garbage collector). 1151 * 1152 * In: 1153 * The calling thread must be attached to the runtime. 1154 */ 1155 extern (C) void thread_enterCriticalRegion() @nogc 1156 in 1157 { 1158 assert(ThreadBase.getThis()); 1159 } 1160 do 1161 { 1162 synchronized (ThreadBase.criticalRegionLock) 1163 ThreadBase.getThis().m_isInCriticalRegion = true; 1164 } 1165 1166 1167 /** 1168 * Signals that the calling thread is no longer in a critical region. Following 1169 * a call to this function, the thread can once again be suspended. 1170 * 1171 * In: 1172 * The calling thread must be attached to the runtime. 1173 */ 1174 extern (C) void thread_exitCriticalRegion() @nogc 1175 in 1176 { 1177 assert(ThreadBase.getThis()); 1178 } 1179 do 1180 { 1181 synchronized (ThreadBase.criticalRegionLock) 1182 ThreadBase.getThis().m_isInCriticalRegion = false; 1183 } 1184 1185 1186 /** 1187 * Returns true if the current thread is in a critical region; otherwise, false. 1188 * 1189 * In: 1190 * The calling thread must be attached to the runtime. 1191 */ 1192 extern (C) bool thread_inCriticalRegion() @nogc 1193 in 1194 { 1195 assert(ThreadBase.getThis()); 1196 } 1197 do 1198 { 1199 synchronized (ThreadBase.criticalRegionLock) 1200 return ThreadBase.getThis().m_isInCriticalRegion; 1201 } 1202 1203 1204 /** 1205 * A callback for thread errors in D during collections. Since an allocation is not possible 1206 * a preallocated ThreadError will be used as the Error instance 1207 * 1208 * Returns: 1209 * never returns 1210 * Throws: 1211 * ThreadError. 1212 */ 1213 package void onThreadError(string msg) nothrow @nogc @system 1214 { 1215 __gshared ThreadError error = new ThreadError(null); 1216 error.msg = msg; 1217 error.next = null; 1218 import core.exception : SuppressTraceInfo; 1219 error.info = SuppressTraceInfo.instance; 1220 throw error; 1221 } 1222 1223 unittest 1224 { 1225 assert(!thread_inCriticalRegion()); 1226 1227 { 1228 thread_enterCriticalRegion(); 1229 1230 scope (exit) 1231 thread_exitCriticalRegion(); 1232 1233 assert(thread_inCriticalRegion()); 1234 } 1235 1236 assert(!thread_inCriticalRegion()); 1237 } 1238 1239 1240 /** 1241 * Indicates whether an address has been marked by the GC. 1242 */ 1243 enum IsMarked : int 1244 { 1245 no, /// Address is not marked. 1246 yes, /// Address is marked. 1247 unknown, /// Address is not managed by the GC. 1248 } 1249 1250 alias IsMarkedDg = int delegate(void* addr) nothrow; /// The isMarked callback function. 1251 1252 /** 1253 * This routine allows the runtime to process any special per-thread handling 1254 * for the GC. This is needed for taking into account any memory that is 1255 * referenced by non-scanned pointers but is about to be freed. That currently 1256 * means the array append cache. 1257 * 1258 * Params: 1259 * isMarked = The function used to check if $(D addr) is marked. 1260 * 1261 * In: 1262 * This routine must be called just prior to resuming all threads. 1263 */ 1264 extern(C) void thread_processGCMarks(scope IsMarkedDg isMarked) nothrow 1265 { 1266 for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next) 1267 { 1268 /* Can be null if collection was triggered between adding a 1269 * thread and calling rt_tlsgc_init. 1270 */ 1271 if (t.m_tlsgcdata !is null) 1272 rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked); 1273 } 1274 } 1275 1276 1277 /** 1278 * Returns the stack top of the currently active stack within the calling 1279 * thread. 1280 * 1281 * In: 1282 * The calling thread must be attached to the runtime. 1283 * 1284 * Returns: 1285 * The address of the stack top. 1286 */ 1287 extern (C) void* thread_stackTop() nothrow @nogc 1288 in 1289 { 1290 // Not strictly required, but it gives us more flexibility. 1291 assert(ThreadBase.getThis()); 1292 } 1293 do 1294 { 1295 return getStackTop(); 1296 } 1297 1298 1299 /** 1300 * Returns the stack bottom of the currently active stack within the calling 1301 * thread. 1302 * 1303 * In: 1304 * The calling thread must be attached to the runtime. 1305 * 1306 * Returns: 1307 * The address of the stack bottom. 1308 */ 1309 extern (C) void* thread_stackBottom() nothrow @nogc 1310 in (ThreadBase.getThis()) 1311 { 1312 return ThreadBase.getThis().topContext().bstack; 1313 } 1314 1315 1316 /////////////////////////////////////////////////////////////////////////////// 1317 // lowlovel threading support 1318 /////////////////////////////////////////////////////////////////////////////// 1319 package 1320 { 1321 __gshared size_t ll_nThreads; 1322 __gshared ll_ThreadData* ll_pThreads; 1323 1324 __gshared align(mutexAlign) void[mutexClassInstanceSize] ll_lock; 1325 1326 @property Mutex lowlevelLock() nothrow @nogc 1327 { 1328 return cast(Mutex)ll_lock.ptr; 1329 } 1330 1331 void initLowlevelThreads() @nogc nothrow 1332 { 1333 import core.lifetime : emplace; 1334 emplace(lowlevelLock()); 1335 } 1336 1337 void termLowlevelThreads() @nogc nothrow 1338 { 1339 lowlevelLock.__dtor(); 1340 } 1341 1342 void ll_removeThread(ThreadID tid) nothrow @nogc @system 1343 { 1344 lowlevelLock.lock_nothrow(); 1345 scope(exit) lowlevelLock.unlock_nothrow(); 1346 1347 foreach (i; 0 .. ll_nThreads) 1348 { 1349 if (tid is ll_pThreads[i].tid) 1350 { 1351 import core.stdc.string : memmove; 1352 memmove(ll_pThreads + i, ll_pThreads + i + 1, ll_ThreadData.sizeof * (ll_nThreads - i - 1)); 1353 --ll_nThreads; 1354 // no need to minimize, next add will do 1355 break; 1356 } 1357 } 1358 } 1359 } 1360 1361 /** 1362 * Check whether a thread was created by `createLowLevelThread`. 1363 * 1364 * Params: 1365 * tid = the platform specific thread ID. 1366 * 1367 * Returns: `true` if the thread was created by `createLowLevelThread` and is still running. 1368 */ 1369 bool findLowLevelThread(ThreadID tid) nothrow @nogc @system 1370 { 1371 lowlevelLock.lock_nothrow(); 1372 scope(exit) lowlevelLock.unlock_nothrow(); 1373 1374 foreach (i; 0 .. ll_nThreads) 1375 if (tid is ll_pThreads[i].tid) 1376 return true; 1377 return false; 1378 }