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