1 /** 2 * The osthread module provides low-level, OS-dependent code 3 * for thread creation 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.osthread; 14 15 import core.thread.threadbase; 16 import core.thread.context; 17 import core.thread.types; 18 import core.atomic; 19 import core.memory : GC, pageSize; 20 import core.time; 21 import core.exception : onOutOfMemoryError; 22 import core.internal.traits : externDFunc; 23 24 version (LDC) 25 { 26 import ldc.attributes; 27 import ldc.llvmasm; 28 29 version (Windows) version = LDC_Windows; 30 31 version (ARM) version = ARM_Any; 32 version (AArch64) version = ARM_Any; 33 34 version (MIPS32) version = MIPS_Any; 35 version (MIPS64) version = MIPS_Any; 36 37 version (PPC) version = PPC_Any; 38 version (PPC64) version = PPC_Any; 39 40 version (RISCV32) version = RISCV_Any; 41 version (RISCV64) version = RISCV_Any; 42 43 version (SupportSanitizers) 44 { 45 import ldc.sanitizers_optionally_linked; 46 } 47 } 48 49 50 /////////////////////////////////////////////////////////////////////////////// 51 // Platform Detection and Memory Allocation 52 /////////////////////////////////////////////////////////////////////////////// 53 54 version (OSX) 55 version = Darwin; 56 else version (iOS) 57 version = Darwin; 58 else version (TVOS) 59 version = Darwin; 60 else version (WatchOS) 61 version = Darwin; 62 63 version (D_InlineAsm_X86) 64 { 65 version (Windows) 66 version = AsmX86_Windows; 67 else version (Posix) 68 version = AsmX86_Posix; 69 } 70 else version (D_InlineAsm_X86_64) 71 { 72 version (Windows) 73 { 74 version = AsmX86_64_Windows; 75 } 76 else version (Posix) 77 { 78 version = AsmX86_64_Posix; 79 } 80 } 81 82 version (Posix) 83 { 84 version (AsmX86_Windows) {} else 85 version (AsmX86_Posix) {} else 86 version (AsmX86_64_Windows) {} else 87 version (AsmX86_64_Posix) {} else 88 version (AsmExternal) {} else 89 { 90 // NOTE: The ucontext implementation requires architecture specific 91 // data definitions to operate so testing for it must be done 92 // by checking for the existence of ucontext_t rather than by 93 // a version identifier. Please note that this is considered 94 // an obsolescent feature according to the POSIX spec, so a 95 // custom solution is still preferred. 96 import core.sys.posix.ucontext; 97 } 98 } 99 100 version (Windows) 101 { 102 import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below 103 import core.stdc.stdlib; // for malloc, atexit 104 import core.sys.windows.basetsd /+: HANDLE+/; 105 import core.sys.windows.threadaux /+: getThreadStackBottom, impersonate_thread, OpenThreadHandle+/; 106 import core.sys.windows.winbase /+: CloseHandle, CREATE_SUSPENDED, DuplicateHandle, GetCurrentThread, 107 GetCurrentThreadId, GetCurrentProcess, GetExitCodeThread, GetSystemInfo, GetThreadContext, 108 GetThreadPriority, INFINITE, ResumeThread, SetThreadPriority, Sleep, STILL_ACTIVE, 109 SuspendThread, SwitchToThread, SYSTEM_INFO, THREAD_PRIORITY_IDLE, THREAD_PRIORITY_NORMAL, 110 THREAD_PRIORITY_TIME_CRITICAL, WAIT_OBJECT_0, WaitForSingleObject+/; 111 import core.sys.windows.windef /+: TRUE+/; 112 import core.sys.windows.winnt /+: CONTEXT, CONTEXT_CONTROL, CONTEXT_INTEGER+/; 113 114 private extern (Windows) alias btex_fptr = uint function(void*); 115 private extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow @nogc; 116 } 117 else version (Posix) 118 { 119 import core.stdc.errno; 120 import core.sys.posix.semaphore; 121 import core.sys.posix.stdlib; // for malloc, valloc, free, atexit 122 import core.sys.posix.pthread; 123 import core.sys.posix.signal; 124 import core.sys.posix.time; 125 126 version (Darwin) 127 { 128 import core.sys.darwin.mach.thread_act; 129 import core.sys.darwin.pthread : pthread_mach_thread_np; 130 } 131 } 132 133 version (Solaris) 134 { 135 import core.sys.solaris.sys.priocntl; 136 import core.sys.solaris.sys.types; 137 import core.sys.posix.sys.wait : idtype_t; 138 } 139 140 version (GNU) 141 { 142 import gcc.builtins; 143 } 144 145 /** 146 * Hook for whatever EH implementation is used to save/restore some data 147 * per stack. 148 * 149 * Params: 150 * newContext = The return value of the prior call to this function 151 * where the stack was last swapped out, or null when a fiber stack 152 * is switched in for the first time. 153 */ 154 private extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc; 155 156 // LDC: changed from `version (DigitalMars)` 157 version (all) 158 { 159 // LDC: changed from `version (Windows)` 160 version (CRuntime_Microsoft) 161 { 162 extern(D) void* swapContext(void* newContext) nothrow @nogc 163 { 164 return _d_eh_swapContext(newContext); 165 } 166 } 167 else 168 { 169 extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc; 170 171 extern(D) void* swapContext(void* newContext) nothrow @nogc 172 { 173 /* Detect at runtime which scheme is being used. 174 * Eventually, determine it statically. 175 */ 176 static int which = 0; 177 final switch (which) 178 { 179 case 0: 180 { 181 assert(newContext == null); 182 auto p = _d_eh_swapContext(newContext); 183 auto pdwarf = _d_eh_swapContextDwarf(newContext); 184 if (p) 185 { 186 which = 1; 187 return p; 188 } 189 else if (pdwarf) 190 { 191 which = 2; 192 return pdwarf; 193 } 194 return null; 195 } 196 case 1: 197 return _d_eh_swapContext(newContext); 198 case 2: 199 return _d_eh_swapContextDwarf(newContext); 200 } 201 } 202 } 203 } 204 else 205 { 206 extern(D) void* swapContext(void* newContext) nothrow @nogc 207 { 208 return _d_eh_swapContext(newContext); 209 } 210 } 211 212 /////////////////////////////////////////////////////////////////////////////// 213 // Thread 214 /////////////////////////////////////////////////////////////////////////////// 215 216 /** 217 * This class encapsulates all threading functionality for the D 218 * programming language. As thread manipulation is a required facility 219 * for garbage collection, all user threads should derive from this 220 * class, and instances of this class should never be explicitly deleted. 221 * A new thread may be created using either derivation or composition, as 222 * in the following example. 223 */ 224 class Thread : ThreadBase 225 { 226 // 227 // Standard thread data 228 // 229 version (Windows) 230 { 231 private HANDLE m_hndl; 232 } 233 234 version (Posix) 235 { 236 private shared bool m_isRunning; 237 } 238 239 version (Darwin) 240 { 241 private mach_port_t m_tmach; 242 } 243 244 version (Solaris) 245 { 246 private __gshared bool m_isRTClass; 247 } 248 249 // 250 // Standard types 251 // 252 version (Windows) 253 { 254 alias TLSKey = uint; 255 } 256 else version (Posix) 257 { 258 alias TLSKey = pthread_key_t; 259 } 260 261 /////////////////////////////////////////////////////////////////////////// 262 // Initialization 263 /////////////////////////////////////////////////////////////////////////// 264 265 266 /** 267 * Initializes a thread object which is associated with a static 268 * D function. 269 * 270 * Params: 271 * fn = The thread function. 272 * sz = The stack size for this thread. 273 * 274 * In: 275 * fn must not be null. 276 */ 277 this( void function() fn, size_t sz = 0 ) @safe pure nothrow @nogc 278 { 279 super(fn, sz); 280 } 281 282 283 /** 284 * Initializes a thread object which is associated with a dynamic 285 * D function. 286 * 287 * Params: 288 * dg = The thread function. 289 * sz = The stack size for this thread. 290 * 291 * In: 292 * dg must not be null. 293 */ 294 this( void delegate() dg, size_t sz = 0 ) @safe pure nothrow @nogc 295 { 296 super(dg, sz); 297 } 298 299 package this( size_t sz = 0 ) @safe pure nothrow @nogc 300 { 301 super(sz); 302 } 303 304 /** 305 * Cleans up any remaining resources used by this object. 306 */ 307 ~this() nothrow @nogc 308 { 309 if (super.destructBeforeDtor()) 310 return; 311 312 version (Windows) 313 { 314 m_addr = m_addr.init; 315 CloseHandle( m_hndl ); 316 m_hndl = m_hndl.init; 317 } 318 else version (Posix) 319 { 320 if (m_addr != m_addr.init) 321 { 322 version (LDC) 323 { 324 // don't detach the main thread, TSan doesn't like it: 325 // https://github.com/ldc-developers/ldc/issues/3519 326 if (!isMainThread()) 327 pthread_detach( m_addr ); 328 } 329 else 330 { 331 pthread_detach( m_addr ); 332 } 333 } 334 m_addr = m_addr.init; 335 } 336 version (Darwin) 337 { 338 m_tmach = m_tmach.init; 339 } 340 } 341 342 // 343 // Thread entry point. Invokes the function or delegate passed on 344 // construction (if any). 345 // 346 private final void run() 347 { 348 super.run(); 349 } 350 351 /** 352 * Provides a reference to the calling thread. 353 * 354 * Returns: 355 * The thread object representing the calling thread. The result of 356 * deleting this object is undefined. If the current thread is not 357 * attached to the runtime, a null reference is returned. 358 */ 359 static Thread getThis() @safe nothrow @nogc 360 { 361 return ThreadBase.getThis().toThread; 362 } 363 364 /////////////////////////////////////////////////////////////////////////// 365 // Thread Context and GC Scanning Support 366 /////////////////////////////////////////////////////////////////////////// 367 368 369 version (Windows) 370 { 371 version (X86) 372 { 373 uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax 374 } 375 else version (X86_64) 376 { 377 ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax 378 // r8,r9,r10,r11,r12,r13,r14,r15 379 } 380 else 381 { 382 static assert(false, "Architecture not supported." ); 383 } 384 } 385 else version (Darwin) 386 { 387 version (X86) 388 { 389 uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax 390 } 391 else version (X86_64) 392 { 393 ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax 394 // r8,r9,r10,r11,r12,r13,r14,r15 395 } 396 else version (AArch64) 397 { 398 ulong[33] m_reg; // x0-x31, pc 399 } 400 else version (ARM) 401 { 402 uint[16] m_reg; // r0-r15 403 } 404 else version (PPC) 405 { 406 // Make the assumption that we only care about non-fp and non-vr regs. 407 // ??? : it seems plausible that a valid address can be copied into a VR. 408 uint[32] m_reg; // r0-31 409 } 410 else version (PPC64) 411 { 412 // As above. 413 ulong[32] m_reg; // r0-31 414 } 415 else 416 { 417 static assert(false, "Architecture not supported." ); 418 } 419 } 420 421 422 /////////////////////////////////////////////////////////////////////////// 423 // General Actions 424 /////////////////////////////////////////////////////////////////////////// 425 426 427 /** 428 * Starts the thread and invokes the function or delegate passed upon 429 * construction. 430 * 431 * In: 432 * This routine may only be called once per thread instance. 433 * 434 * Throws: 435 * ThreadException if the thread fails to start. 436 */ 437 final Thread start() nothrow @system 438 in 439 { 440 assert( !next && !prev ); 441 } 442 do 443 { 444 auto wasThreaded = multiThreadedFlag; 445 multiThreadedFlag = true; 446 scope( failure ) 447 { 448 if ( !wasThreaded ) 449 multiThreadedFlag = false; 450 } 451 452 version (Windows) {} else 453 version (Posix) 454 { 455 size_t stksz = adjustStackSize( m_sz ); 456 457 pthread_attr_t attr; 458 459 if ( pthread_attr_init( &attr ) ) 460 onThreadError( "Error initializing thread attributes" ); 461 if ( stksz && pthread_attr_setstacksize( &attr, stksz ) ) 462 onThreadError( "Error initializing thread stack size" ); 463 } 464 465 version (Shared) 466 { 467 auto ps = cast(void**).malloc(2 * size_t.sizeof); 468 if (ps is null) onOutOfMemoryError(); 469 } 470 471 version (Windows) 472 { 473 // NOTE: If a thread is just executing DllMain() 474 // while another thread is started here, it holds an OS internal 475 // lock that serializes DllMain with CreateThread. As the code 476 // might request a synchronization on slock (e.g. in thread_findByAddr()), 477 // we cannot hold that lock while creating the thread without 478 // creating a deadlock 479 // 480 // Solution: Create the thread in suspended state and then 481 // add and resume it with slock acquired 482 assert(m_sz <= uint.max, "m_sz must be less than or equal to uint.max"); 483 version (Shared) 484 auto threadArg = cast(void*) ps; 485 else 486 auto threadArg = cast(void*) this; 487 m_hndl = cast(HANDLE) _beginthreadex( null, cast(uint) m_sz, &thread_entryPoint, threadArg, CREATE_SUSPENDED, &m_addr ); 488 if ( cast(size_t) m_hndl == 0 ) 489 onThreadError( "Error creating thread" ); 490 } 491 492 slock.lock_nothrow(); 493 scope(exit) slock.unlock_nothrow(); 494 { 495 ++nAboutToStart; 496 pAboutToStart = cast(ThreadBase*)realloc(pAboutToStart, Thread.sizeof * nAboutToStart); 497 pAboutToStart[nAboutToStart - 1] = this; 498 499 version (Posix) 500 { 501 // NOTE: This is also set to true by thread_entryPoint, but set it 502 // here as well so the calling thread will see the isRunning 503 // state immediately. 504 atomicStore!(MemoryOrder.raw)(m_isRunning, true); 505 scope( failure ) atomicStore!(MemoryOrder.raw)(m_isRunning, false); 506 } 507 508 version (Shared) 509 { 510 auto libs = externDFunc!("rt.sections_elf_shared.pinLoadedLibraries", 511 void* function() @nogc nothrow)(); 512 513 ps[0] = cast(void*)this; 514 ps[1] = cast(void*)libs; 515 516 version (Windows) 517 { 518 if ( ResumeThread( m_hndl ) == -1 ) 519 { 520 externDFunc!("rt.sections_elf_shared.unpinLoadedLibraries", 521 void function(void*) @nogc nothrow)(libs); 522 .free(ps); 523 onThreadError( "Error resuming thread" ); 524 } 525 } 526 else version (Posix) 527 { 528 if ( pthread_create( &m_addr, &attr, &thread_entryPoint, ps ) != 0 ) 529 { 530 externDFunc!("rt.sections_elf_shared.unpinLoadedLibraries", 531 void function(void*) @nogc nothrow)(libs); 532 .free(ps); 533 onThreadError( "Error creating thread" ); 534 } 535 } 536 } 537 else 538 { 539 version (Windows) 540 { 541 if ( ResumeThread( m_hndl ) == -1 ) 542 onThreadError( "Error resuming thread" ); 543 } 544 else version (Posix) 545 { 546 if ( pthread_create( &m_addr, &attr, &thread_entryPoint, cast(void*) this ) != 0 ) 547 onThreadError( "Error creating thread" ); 548 } 549 } 550 551 version (Posix) 552 { 553 if ( pthread_attr_destroy( &attr ) != 0 ) 554 onThreadError( "Error destroying thread attributes" ); 555 } 556 557 version (Darwin) 558 { 559 m_tmach = pthread_mach_thread_np( m_addr ); 560 if ( m_tmach == m_tmach.init ) 561 onThreadError( "Error creating thread" ); 562 } 563 564 return this; 565 } 566 } 567 568 /** 569 * Waits for this thread to complete. If the thread terminated as the 570 * result of an unhandled exception, this exception will be rethrown. 571 * 572 * Params: 573 * rethrow = Rethrow any unhandled exception which may have caused this 574 * thread to terminate. 575 * 576 * Throws: 577 * ThreadException if the operation fails. 578 * Any exception not handled by the joined thread. 579 * 580 * Returns: 581 * Any exception not handled by this thread if rethrow = false, null 582 * otherwise. 583 */ 584 override final Throwable join( bool rethrow = true ) 585 { 586 version (Windows) 587 { 588 if ( m_addr != m_addr.init && WaitForSingleObject( m_hndl, INFINITE ) != WAIT_OBJECT_0 ) 589 throw new ThreadException( "Unable to join thread" ); 590 // NOTE: m_addr must be cleared before m_hndl is closed to avoid 591 // a race condition with isRunning. The operation is done 592 // with atomicStore to prevent compiler reordering. 593 atomicStore!(MemoryOrder.raw)(*cast(shared)&m_addr, m_addr.init); 594 CloseHandle( m_hndl ); 595 m_hndl = m_hndl.init; 596 } 597 else version (Posix) 598 { 599 if ( m_addr != m_addr.init && pthread_join( m_addr, null ) != 0 ) 600 throw new ThreadException( "Unable to join thread" ); 601 // NOTE: pthread_join acts as a substitute for pthread_detach, 602 // which is normally called by the dtor. Setting m_addr 603 // to zero ensures that pthread_detach will not be called 604 // on object destruction. 605 m_addr = m_addr.init; 606 } 607 if ( m_unhandled ) 608 { 609 if ( rethrow ) 610 throw m_unhandled; 611 return m_unhandled; 612 } 613 return null; 614 } 615 616 617 /////////////////////////////////////////////////////////////////////////// 618 // Thread Priority Actions 619 /////////////////////////////////////////////////////////////////////////// 620 621 version (Windows) 622 { 623 @property static int PRIORITY_MIN() @nogc nothrow pure @safe 624 { 625 return THREAD_PRIORITY_IDLE; 626 } 627 628 @property static const(int) PRIORITY_MAX() @nogc nothrow pure @safe 629 { 630 return THREAD_PRIORITY_TIME_CRITICAL; 631 } 632 633 @property static int PRIORITY_DEFAULT() @nogc nothrow pure @safe 634 { 635 return THREAD_PRIORITY_NORMAL; 636 } 637 } 638 else 639 { 640 private struct Priority 641 { 642 int PRIORITY_MIN = int.min; 643 int PRIORITY_DEFAULT = int.min; 644 int PRIORITY_MAX = int.min; 645 } 646 647 /* 648 Lazily loads one of the members stored in a hidden global variable of 649 type `Priority`. Upon the first access of either member, the entire 650 `Priority` structure is initialized. Multiple initializations from 651 different threads calling this function are tolerated. 652 653 `which` must be one of `PRIORITY_MIN`, `PRIORITY_DEFAULT`, 654 `PRIORITY_MAX`. 655 */ 656 private static shared Priority cache; 657 private static int loadGlobal(string which)() 658 { 659 auto local = atomicLoad(mixin("cache." ~ which)); 660 if (local != local.min) return local; 661 // There will be benign races 662 cache = loadPriorities; 663 return atomicLoad(mixin("cache." ~ which)); 664 } 665 666 /* 667 Loads all priorities and returns them as a `Priority` structure. This 668 function is thread-neutral. 669 */ 670 private static Priority loadPriorities() @nogc nothrow @trusted 671 { 672 Priority result; 673 version (Solaris) 674 { 675 pcparms_t pcParms; 676 pcinfo_t pcInfo; 677 678 pcParms.pc_cid = PC_CLNULL; 679 if (priocntl(idtype_t.P_PID, P_MYID, PC_GETPARMS, &pcParms) == -1) 680 assert( 0, "Unable to get scheduling class" ); 681 682 pcInfo.pc_cid = pcParms.pc_cid; 683 // PC_GETCLINFO ignores the first two args, use dummy values 684 if (priocntl(idtype_t.P_PID, 0, PC_GETCLINFO, &pcInfo) == -1) 685 assert( 0, "Unable to get scheduling class info" ); 686 687 pri_t* clparms = cast(pri_t*)&pcParms.pc_clparms; 688 pri_t* clinfo = cast(pri_t*)&pcInfo.pc_clinfo; 689 690 result.PRIORITY_MAX = clparms[0]; 691 692 if (pcInfo.pc_clname == "RT") 693 { 694 m_isRTClass = true; 695 696 // For RT class, just assume it can't be changed 697 result.PRIORITY_MIN = clparms[0]; 698 result.PRIORITY_DEFAULT = clparms[0]; 699 } 700 else 701 { 702 m_isRTClass = false; 703 704 // For all other scheduling classes, there are 705 // two key values -- uprilim and maxupri. 706 // maxupri is the maximum possible priority defined 707 // for the scheduling class, and valid priorities 708 // range are in [-maxupri, maxupri]. 709 // 710 // However, uprilim is an upper limit that the 711 // current thread can set for the current scheduling 712 // class, which can be less than maxupri. As such, 713 // use this value for priorityMax since this is 714 // the effective maximum. 715 716 // maxupri 717 result.PRIORITY_MIN = -cast(int)(clinfo[0]); 718 // by definition 719 result.PRIORITY_DEFAULT = 0; 720 } 721 } 722 else version (Posix) 723 { 724 int policy; 725 sched_param param; 726 pthread_getschedparam( pthread_self(), &policy, ¶m ) == 0 727 || assert(0, "Internal error in pthread_getschedparam"); 728 729 result.PRIORITY_MIN = sched_get_priority_min( policy ); 730 result.PRIORITY_MIN != -1 731 || assert(0, "Internal error in sched_get_priority_min"); 732 result.PRIORITY_DEFAULT = param.sched_priority; 733 result.PRIORITY_MAX = sched_get_priority_max( policy ); 734 result.PRIORITY_MAX != -1 || 735 assert(0, "Internal error in sched_get_priority_max"); 736 } 737 else 738 { 739 static assert(0, "Your code here."); 740 } 741 return result; 742 } 743 744 /** 745 * The minimum scheduling priority that may be set for a thread. On 746 * systems where multiple scheduling policies are defined, this value 747 * represents the minimum valid priority for the scheduling policy of 748 * the process. 749 */ 750 @property static int PRIORITY_MIN() @nogc nothrow pure @trusted 751 { 752 return (cast(int function() @nogc nothrow pure @safe) 753 &loadGlobal!"PRIORITY_MIN")(); 754 } 755 756 /** 757 * The maximum scheduling priority that may be set for a thread. On 758 * systems where multiple scheduling policies are defined, this value 759 * represents the maximum valid priority for the scheduling policy of 760 * the process. 761 */ 762 @property static const(int) PRIORITY_MAX() @nogc nothrow pure @trusted 763 { 764 return (cast(int function() @nogc nothrow pure @safe) 765 &loadGlobal!"PRIORITY_MAX")(); 766 } 767 768 /** 769 * The default scheduling priority that is set for a thread. On 770 * systems where multiple scheduling policies are defined, this value 771 * represents the default priority for the scheduling policy of 772 * the process. 773 */ 774 @property static int PRIORITY_DEFAULT() @nogc nothrow pure @trusted 775 { 776 return (cast(int function() @nogc nothrow pure @safe) 777 &loadGlobal!"PRIORITY_DEFAULT")(); 778 } 779 } 780 781 version (NetBSD) 782 { 783 //NetBSD does not support priority for default policy 784 // and it is not possible change policy without root access 785 int fakePriority = int.max; 786 } 787 788 /** 789 * Gets the scheduling priority for the associated thread. 790 * 791 * Note: Getting the priority of a thread that already terminated 792 * might return the default priority. 793 * 794 * Returns: 795 * The scheduling priority of this thread. 796 */ 797 final @property int priority() @system 798 { 799 version (Windows) 800 { 801 return GetThreadPriority( m_hndl ); 802 } 803 else version (NetBSD) 804 { 805 return fakePriority==int.max? PRIORITY_DEFAULT : fakePriority; 806 } 807 else version (Posix) 808 { 809 int policy; 810 sched_param param; 811 812 if (auto err = pthread_getschedparam(m_addr, &policy, ¶m)) 813 { 814 // ignore error if thread is not running => Bugzilla 8960 815 if (!atomicLoad(m_isRunning)) return PRIORITY_DEFAULT; 816 throw new ThreadException("Unable to get thread priority"); 817 } 818 return param.sched_priority; 819 } 820 } 821 822 823 /** 824 * Sets the scheduling priority for the associated thread. 825 * 826 * Note: Setting the priority of a thread that already terminated 827 * might have no effect. 828 * 829 * Params: 830 * val = The new scheduling priority of this thread. 831 */ 832 final @property void priority( int val ) 833 in 834 { 835 assert(val >= PRIORITY_MIN); 836 assert(val <= PRIORITY_MAX); 837 } 838 do 839 { 840 version (Windows) 841 { 842 if ( !SetThreadPriority( m_hndl, val ) ) 843 throw new ThreadException( "Unable to set thread priority" ); 844 } 845 else version (Solaris) 846 { 847 // the pthread_setschedprio(3c) and pthread_setschedparam functions 848 // are broken for the default (TS / time sharing) scheduling class. 849 // instead, we use priocntl(2) which gives us the desired behavior. 850 851 // We hardcode the min and max priorities to the current value 852 // so this is a no-op for RT threads. 853 if (m_isRTClass) 854 return; 855 856 pcparms_t pcparm; 857 858 pcparm.pc_cid = PC_CLNULL; 859 if (priocntl(idtype_t.P_LWPID, P_MYID, PC_GETPARMS, &pcparm) == -1) 860 throw new ThreadException( "Unable to get scheduling class" ); 861 862 pri_t* clparms = cast(pri_t*)&pcparm.pc_clparms; 863 864 // clparms is filled in by the PC_GETPARMS call, only necessary 865 // to adjust the element that contains the thread priority 866 clparms[1] = cast(pri_t) val; 867 868 if (priocntl(idtype_t.P_LWPID, P_MYID, PC_SETPARMS, &pcparm) == -1) 869 throw new ThreadException( "Unable to set scheduling class" ); 870 } 871 else version (NetBSD) 872 { 873 fakePriority = val; 874 } 875 else version (Posix) 876 { 877 static if (__traits(compiles, pthread_setschedprio)) 878 { 879 if (auto err = pthread_setschedprio(m_addr, val)) 880 { 881 // ignore error if thread is not running => Bugzilla 8960 882 if (!atomicLoad(m_isRunning)) return; 883 throw new ThreadException("Unable to set thread priority"); 884 } 885 } 886 else 887 { 888 // NOTE: pthread_setschedprio is not implemented on Darwin, FreeBSD, OpenBSD, 889 // or DragonFlyBSD, so use the more complicated get/set sequence below. 890 int policy; 891 sched_param param; 892 893 if (auto err = pthread_getschedparam(m_addr, &policy, ¶m)) 894 { 895 // ignore error if thread is not running => Bugzilla 8960 896 if (!atomicLoad(m_isRunning)) return; 897 throw new ThreadException("Unable to set thread priority"); 898 } 899 param.sched_priority = val; 900 if (auto err = pthread_setschedparam(m_addr, policy, ¶m)) 901 { 902 // ignore error if thread is not running => Bugzilla 8960 903 if (!atomicLoad(m_isRunning)) return; 904 throw new ThreadException("Unable to set thread priority"); 905 } 906 } 907 } 908 } 909 910 911 unittest 912 { 913 auto thr = Thread.getThis(); 914 immutable prio = thr.priority; 915 scope (exit) thr.priority = prio; 916 917 assert(prio == PRIORITY_DEFAULT); 918 assert(prio >= PRIORITY_MIN && prio <= PRIORITY_MAX); 919 thr.priority = PRIORITY_MIN; 920 assert(thr.priority == PRIORITY_MIN); 921 thr.priority = PRIORITY_MAX; 922 assert(thr.priority == PRIORITY_MAX); 923 } 924 925 unittest // Bugzilla 8960 926 { 927 import core.sync.semaphore; 928 929 auto thr = new Thread({}); 930 thr.start(); 931 Thread.sleep(1.msecs); // wait a little so the thread likely has finished 932 thr.priority = PRIORITY_MAX; // setting priority doesn't cause error 933 auto prio = thr.priority; // getting priority doesn't cause error 934 assert(prio >= PRIORITY_MIN && prio <= PRIORITY_MAX); 935 } 936 937 /** 938 * Tests whether this thread is running. 939 * 940 * Returns: 941 * true if the thread is running, false if not. 942 */ 943 override final @property bool isRunning() nothrow @nogc 944 { 945 if (!super.isRunning()) 946 return false; 947 948 version (Windows) 949 { 950 uint ecode = 0; 951 GetExitCodeThread( m_hndl, &ecode ); 952 return ecode == STILL_ACTIVE; 953 } 954 else version (Posix) 955 { 956 return atomicLoad(m_isRunning); 957 } 958 } 959 960 961 /////////////////////////////////////////////////////////////////////////// 962 // Actions on Calling Thread 963 /////////////////////////////////////////////////////////////////////////// 964 965 966 /** 967 * Suspends the calling thread for at least the supplied period. This may 968 * result in multiple OS calls if period is greater than the maximum sleep 969 * duration supported by the operating system. 970 * 971 * Params: 972 * val = The minimum duration the calling thread should be suspended. 973 * 974 * In: 975 * period must be non-negative. 976 * 977 * Example: 978 * ------------------------------------------------------------------------ 979 * 980 * Thread.sleep( dur!("msecs")( 50 ) ); // sleep for 50 milliseconds 981 * Thread.sleep( dur!("seconds")( 5 ) ); // sleep for 5 seconds 982 * 983 * ------------------------------------------------------------------------ 984 */ 985 static void sleep( Duration val ) @nogc nothrow @system 986 in 987 { 988 assert( !val.isNegative ); 989 } 990 do 991 { 992 version (Windows) 993 { 994 auto maxSleepMillis = dur!("msecs")( uint.max - 1 ); 995 996 // avoid a non-zero time to be round down to 0 997 if ( val > dur!"msecs"( 0 ) && val < dur!"msecs"( 1 ) ) 998 val = dur!"msecs"( 1 ); 999 1000 // NOTE: In instances where all other threads in the process have a 1001 // lower priority than the current thread, the current thread 1002 // will not yield with a sleep time of zero. However, unlike 1003 // yield(), the user is not asking for a yield to occur but 1004 // only for execution to suspend for the requested interval. 1005 // Therefore, expected performance may not be met if a yield 1006 // is forced upon the user. 1007 while ( val > maxSleepMillis ) 1008 { 1009 Sleep( cast(uint) 1010 maxSleepMillis.total!"msecs" ); 1011 val -= maxSleepMillis; 1012 } 1013 Sleep( cast(uint) val.total!"msecs" ); 1014 } 1015 else version (Posix) 1016 { 1017 timespec tin = void; 1018 timespec tout = void; 1019 1020 val.split!("seconds", "nsecs")(tin.tv_sec, tin.tv_nsec); 1021 if ( val.total!"seconds" > tin.tv_sec.max ) 1022 tin.tv_sec = tin.tv_sec.max; 1023 while ( true ) 1024 { 1025 if ( !nanosleep( &tin, &tout ) ) 1026 return; 1027 if ( errno != EINTR ) 1028 assert(0, "Unable to sleep for the specified duration"); 1029 tin = tout; 1030 } 1031 } 1032 } 1033 1034 1035 /** 1036 * Forces a context switch to occur away from the calling thread. 1037 */ 1038 static void yield() @nogc nothrow 1039 { 1040 version (Windows) 1041 SwitchToThread(); 1042 else version (Posix) 1043 sched_yield(); 1044 } 1045 } 1046 1047 private Thread toThread(return scope ThreadBase t) @trusted nothrow @nogc pure 1048 { 1049 return cast(Thread) cast(void*) t; 1050 } 1051 1052 private extern(D) static void thread_yield() @nogc nothrow 1053 { 1054 Thread.yield(); 1055 } 1056 1057 /// 1058 unittest 1059 { 1060 class DerivedThread : Thread 1061 { 1062 this() 1063 { 1064 super(&run); 1065 } 1066 1067 private: 1068 void run() 1069 { 1070 // Derived thread running. 1071 } 1072 } 1073 1074 void threadFunc() 1075 { 1076 // Composed thread running. 1077 } 1078 1079 // create and start instances of each type 1080 auto derived = new DerivedThread().start(); 1081 auto composed = new Thread(&threadFunc).start(); 1082 new Thread({ 1083 // Codes to run in the newly created thread. 1084 }).start(); 1085 } 1086 1087 unittest 1088 { 1089 int x = 0; 1090 1091 new Thread( 1092 { 1093 x++; 1094 }).start().join(); 1095 assert( x == 1 ); 1096 } 1097 1098 1099 unittest 1100 { 1101 enum MSG = "Test message."; 1102 string caughtMsg; 1103 1104 try 1105 { 1106 new Thread( 1107 function() 1108 { 1109 throw new Exception( MSG ); 1110 }).start().join(); 1111 assert( false, "Expected rethrown exception." ); 1112 } 1113 catch ( Throwable t ) 1114 { 1115 assert( t.msg == MSG ); 1116 } 1117 } 1118 1119 1120 unittest 1121 { 1122 // use >pageSize to avoid stack overflow (e.g. in an syscall) 1123 auto thr = new Thread(function{}, 4096 + 1).start(); 1124 thr.join(); 1125 } 1126 1127 1128 unittest 1129 { 1130 import core.memory : GC; 1131 1132 auto t1 = new Thread({ 1133 foreach (_; 0 .. 20) 1134 ThreadBase.getAll; 1135 }).start; 1136 auto t2 = new Thread({ 1137 foreach (_; 0 .. 20) 1138 GC.collect; 1139 }).start; 1140 t1.join(); 1141 t2.join(); 1142 } 1143 1144 unittest 1145 { 1146 import core.sync.semaphore; 1147 auto sem = new Semaphore(); 1148 1149 auto t = new Thread( 1150 { 1151 sem.notify(); 1152 Thread.sleep(100.msecs); 1153 }).start(); 1154 1155 sem.wait(); // thread cannot be detached while being started 1156 thread_detachInstance(t); 1157 foreach (t2; Thread) 1158 assert(t !is t2); 1159 t.join(); 1160 } 1161 1162 unittest 1163 { 1164 // NOTE: This entire test is based on the assumption that no 1165 // memory is allocated after the child thread is 1166 // started. If an allocation happens, a collection could 1167 // trigger, which would cause the synchronization below 1168 // to cause a deadlock. 1169 // NOTE: DO NOT USE LOCKS IN CRITICAL REGIONS IN NORMAL CODE. 1170 1171 import core.sync.semaphore; 1172 1173 auto sema = new Semaphore(), 1174 semb = new Semaphore(); 1175 1176 auto thr = new Thread( 1177 { 1178 thread_enterCriticalRegion(); 1179 assert(thread_inCriticalRegion()); 1180 sema.notify(); 1181 1182 semb.wait(); 1183 assert(thread_inCriticalRegion()); 1184 1185 thread_exitCriticalRegion(); 1186 assert(!thread_inCriticalRegion()); 1187 sema.notify(); 1188 1189 semb.wait(); 1190 assert(!thread_inCriticalRegion()); 1191 }); 1192 1193 thr.start(); 1194 1195 sema.wait(); 1196 synchronized (ThreadBase.criticalRegionLock) 1197 assert(thr.m_isInCriticalRegion); 1198 semb.notify(); 1199 1200 sema.wait(); 1201 synchronized (ThreadBase.criticalRegionLock) 1202 assert(!thr.m_isInCriticalRegion); 1203 semb.notify(); 1204 1205 thr.join(); 1206 } 1207 1208 // https://issues.dlang.org/show_bug.cgi?id=22124 1209 unittest 1210 { 1211 Thread thread = new Thread({}); 1212 auto fun(Thread t, int x) 1213 { 1214 t.__ctor({x = 3;}); 1215 return t; 1216 } 1217 static assert(!__traits(compiles, () @nogc => fun(thread, 3) )); 1218 } 1219 1220 unittest 1221 { 1222 import core.sync.semaphore; 1223 1224 shared bool inCriticalRegion; 1225 auto sema = new Semaphore(), 1226 semb = new Semaphore(); 1227 1228 auto thr = new Thread( 1229 { 1230 thread_enterCriticalRegion(); 1231 inCriticalRegion = true; 1232 sema.notify(); 1233 semb.wait(); 1234 1235 Thread.sleep(dur!"msecs"(1)); 1236 inCriticalRegion = false; 1237 thread_exitCriticalRegion(); 1238 }); 1239 thr.start(); 1240 1241 sema.wait(); 1242 assert(inCriticalRegion); 1243 semb.notify(); 1244 1245 thread_suspendAll(); 1246 assert(!inCriticalRegion); 1247 thread_resumeAll(); 1248 } 1249 1250 /////////////////////////////////////////////////////////////////////////////// 1251 // GC Support Routines 1252 /////////////////////////////////////////////////////////////////////////////// 1253 1254 version (CoreDdoc) 1255 { 1256 /** 1257 * Instruct the thread module, when initialized, to use a different set of 1258 * signals besides SIGRTMIN and SIGRTMIN + 1 for suspension and resumption of threads. 1259 * This function should be called at most once, prior to thread_init(). 1260 * This function is Posix-only. 1261 */ 1262 extern (C) void thread_setGCSignals(int suspendSignalNo, int resumeSignalNo) nothrow @nogc 1263 { 1264 } 1265 } 1266 else version (Posix) 1267 { 1268 extern (C) void thread_setGCSignals(int suspendSignalNo, int resumeSignalNo) nothrow @nogc 1269 in 1270 { 1271 assert(suspendSignalNo != 0); 1272 assert(resumeSignalNo != 0); 1273 } 1274 out 1275 { 1276 assert(suspendSignalNumber != 0); 1277 assert(resumeSignalNumber != 0); 1278 } 1279 do 1280 { 1281 suspendSignalNumber = suspendSignalNo; 1282 resumeSignalNumber = resumeSignalNo; 1283 } 1284 } 1285 1286 version (Posix) 1287 { 1288 private __gshared int suspendSignalNumber; 1289 private __gshared int resumeSignalNumber; 1290 } 1291 1292 private extern (D) ThreadBase attachThread(ThreadBase _thisThread) @nogc nothrow 1293 { 1294 Thread thisThread = _thisThread.toThread(); 1295 1296 StackContext* thisContext = &thisThread.m_main; 1297 assert( thisContext == thisThread.m_curr ); 1298 1299 version (SupportSanitizers) 1300 { 1301 // Save this thread's fake stack handler, to be stored in each StackContext belonging to this thread. 1302 thisThread.asan_fakestack = asanGetCurrentFakeStack(); 1303 thisContext.asan_fakestack = thisThread.asan_fakestack; 1304 } 1305 1306 version (Windows) 1307 { 1308 thisThread.m_addr = GetCurrentThreadId(); 1309 thisThread.m_hndl = GetCurrentThreadHandle(); 1310 thisContext.bstack = getStackBottom(); 1311 thisContext.tstack = thisContext.bstack; 1312 } 1313 else version (Posix) 1314 { 1315 thisThread.m_addr = pthread_self(); 1316 thisContext.bstack = getStackBottom(); 1317 thisContext.tstack = thisContext.bstack; 1318 1319 atomicStore!(MemoryOrder.raw)(thisThread.toThread.m_isRunning, true); 1320 } 1321 thisThread.m_isDaemon = true; 1322 thisThread.tlsGCdataInit(); 1323 Thread.setThis( thisThread ); 1324 1325 version (Darwin) 1326 { 1327 thisThread.m_tmach = pthread_mach_thread_np( thisThread.m_addr ); 1328 assert( thisThread.m_tmach != thisThread.m_tmach.init ); 1329 } 1330 1331 Thread.add( thisThread, false ); 1332 Thread.add( thisContext ); 1333 if ( Thread.sm_main !is null ) 1334 multiThreadedFlag = true; 1335 return thisThread; 1336 } 1337 1338 /** 1339 * Registers the calling thread for use with the D Runtime. If this routine 1340 * is called for a thread which is already registered, no action is performed. 1341 * 1342 * NOTE: This routine does not run thread-local static constructors when called. 1343 * If full functionality as a D thread is desired, the following function 1344 * must be called after thread_attachThis: 1345 * 1346 * extern (C) void rt_moduleTlsCtor(); 1347 * 1348 * See_Also: 1349 * $(REF thread_detachThis, core,thread,threadbase) 1350 */ 1351 extern(C) Thread thread_attachThis() 1352 { 1353 return thread_attachThis_tpl!Thread(); 1354 } 1355 1356 1357 version (Windows) 1358 { 1359 // NOTE: These calls are not safe on Posix systems that use signals to 1360 // perform garbage collection. The suspendHandler uses getThis() 1361 // to get the thread handle so getThis() must be a simple call. 1362 // Mutexes can't safely be acquired inside signal handlers, and 1363 // even if they could, the mutex needed (Thread.slock) is held by 1364 // thread_suspendAll(). So in short, these routines will remain 1365 // Windows-specific. If they are truly needed elsewhere, the 1366 // suspendHandler will need a way to call a version of getThis() 1367 // that only does the TLS lookup without the fancy fallback stuff. 1368 1369 /// ditto 1370 extern (C) Thread thread_attachByAddr( ThreadID addr ) 1371 { 1372 return thread_attachByAddrB( addr, getThreadStackBottom( addr ) ); 1373 } 1374 1375 1376 /// ditto 1377 extern (C) Thread thread_attachByAddrB( ThreadID addr, void* bstack ) 1378 { 1379 GC.disable(); scope(exit) GC.enable(); 1380 1381 if (auto t = thread_findByAddr(addr).toThread) 1382 return t; 1383 1384 Thread thisThread = new Thread(); 1385 StackContext* thisContext = &thisThread.m_main; 1386 assert( thisContext == thisThread.m_curr ); 1387 1388 thisThread.m_addr = addr; 1389 thisContext.bstack = bstack; 1390 thisContext.tstack = thisContext.bstack; 1391 1392 thisThread.m_isDaemon = true; 1393 1394 if ( addr == GetCurrentThreadId() ) 1395 { 1396 thisThread.m_hndl = GetCurrentThreadHandle(); 1397 thisThread.tlsGCdataInit(); 1398 Thread.setThis( thisThread ); 1399 1400 version (SupportSanitizers) 1401 { 1402 // Save this thread's fake stack handler, to be stored in each StackContext belonging to this thread. 1403 thisThread.asan_fakestack = asanGetCurrentFakeStack(); 1404 } 1405 } 1406 else 1407 { 1408 thisThread.m_hndl = OpenThreadHandle( addr ); 1409 impersonate_thread(addr, 1410 { 1411 thisThread.tlsGCdataInit(); 1412 Thread.setThis( thisThread ); 1413 1414 version (SupportSanitizers) 1415 { 1416 // Save this thread's fake stack handler, to be stored in each StackContext belonging to this thread. 1417 thisThread.asan_fakestack = asanGetCurrentFakeStack(); 1418 } 1419 }); 1420 } 1421 1422 version (SupportSanitizers) 1423 { 1424 thisContext.asan_fakestack = thisThread.asan_fakestack; 1425 } 1426 1427 Thread.add( thisThread, false ); 1428 Thread.add( thisContext ); 1429 if ( Thread.sm_main !is null ) 1430 multiThreadedFlag = true; 1431 return thisThread; 1432 } 1433 } 1434 1435 1436 // Calls the given delegate, passing the current thread's stack pointer to it. 1437 package extern(D) void callWithStackShell(scope callWithStackShellDg fn) nothrow @system 1438 in (fn) 1439 { 1440 // The purpose of the 'shell' is to ensure all the registers get 1441 // put on the stack so they'll be scanned. We only need to push 1442 // the callee-save registers. 1443 void *sp = void; 1444 version (GNU) 1445 { 1446 __builtin_unwind_init(); 1447 sp = &sp; 1448 } 1449 else version (AsmX86_Posix) 1450 { 1451 size_t[3] regs = void; 1452 asm pure nothrow @nogc 1453 { 1454 mov [regs + 0 * 4], EBX; 1455 mov [regs + 1 * 4], ESI; 1456 mov [regs + 2 * 4], EDI; 1457 1458 mov sp[EBP], ESP; 1459 } 1460 } 1461 else version (AsmX86_Windows) 1462 { 1463 size_t[3] regs = void; 1464 asm pure nothrow @nogc 1465 { 1466 mov [regs + 0 * 4], EBX; 1467 mov [regs + 1 * 4], ESI; 1468 mov [regs + 2 * 4], EDI; 1469 1470 mov sp[EBP], ESP; 1471 } 1472 } 1473 else version (AsmX86_64_Posix) 1474 { 1475 size_t[5] regs = void; 1476 asm pure nothrow @nogc 1477 { 1478 mov [regs + 0 * 8], RBX; 1479 mov [regs + 1 * 8], R12; 1480 mov [regs + 2 * 8], R13; 1481 mov [regs + 3 * 8], R14; 1482 mov [regs + 4 * 8], R15; 1483 1484 mov sp[RBP], RSP; 1485 } 1486 } 1487 else version (AsmX86_64_Windows) 1488 { 1489 size_t[7] regs = void; 1490 asm pure nothrow @nogc 1491 { 1492 mov [regs + 0 * 8], RBX; 1493 mov [regs + 1 * 8], RSI; 1494 mov [regs + 2 * 8], RDI; 1495 mov [regs + 3 * 8], R12; 1496 mov [regs + 4 * 8], R13; 1497 mov [regs + 5 * 8], R14; 1498 mov [regs + 6 * 8], R15; 1499 1500 mov sp[RBP], RSP; 1501 } 1502 } 1503 else version (LDC) 1504 { 1505 version (PPC_Any) 1506 { 1507 // Nonvolatile registers, according to: 1508 // System V Application Binary Interface 1509 // PowerPC Processor Supplement, September 1995 1510 // ELFv1: 64-bit PowerPC ELF ABI Supplement 1.9, July 2004 1511 // ELFv2: Power Architecture, 64-Bit ELV V2 ABI Specification, 1512 // OpenPOWER ABI for Linux Supplement, July 2014 1513 size_t[18] regs = void; 1514 static foreach (i; 0 .. regs.length) 1515 {{ 1516 enum int j = 14 + i; // source register 1517 static if (j == 21) 1518 { 1519 // Work around LLVM bug 21443 (http://llvm.org/bugs/show_bug.cgi?id=21443) 1520 // Because we clobber r0 a different register is chosen 1521 asm pure nothrow @nogc { ("std "~j.stringof~", %0") : "=m" (regs[i]) : : "r0"; } 1522 } 1523 else 1524 asm pure nothrow @nogc { ("std "~j.stringof~", %0") : "=m" (regs[i]); } 1525 }} 1526 1527 asm pure nothrow @nogc { "std 1, %0" : "=m" (sp); } 1528 } 1529 else version (AArch64) 1530 { 1531 // Callee-save registers, x19-x28 according to AAPCS64, section 1532 // 5.1.1. Include x29 fp because it optionally can be a callee 1533 // saved reg 1534 size_t[11] regs = void; 1535 // store the registers in pairs 1536 asm pure nothrow @nogc 1537 { 1538 "stp x19, x20, %0" : "=m" (regs[ 0]), "=m" (regs[1]); 1539 "stp x21, x22, %0" : "=m" (regs[ 2]), "=m" (regs[3]); 1540 "stp x23, x24, %0" : "=m" (regs[ 4]), "=m" (regs[5]); 1541 "stp x25, x26, %0" : "=m" (regs[ 6]), "=m" (regs[7]); 1542 "stp x27, x28, %0" : "=m" (regs[ 8]), "=m" (regs[9]); 1543 "str x29, %0" : "=m" (regs[10]); 1544 "mov %0, sp" : "=r" (sp); 1545 } 1546 } 1547 else version (ARM) 1548 { 1549 // Callee-save registers, according to AAPCS, section 5.1.1. 1550 // arm and thumb2 instructions 1551 size_t[8] regs = void; 1552 asm pure nothrow @nogc 1553 { 1554 "stm %0, {r4-r11}" : : "r" (regs.ptr) : "memory"; 1555 "mov %0, sp" : "=r" (sp); 1556 } 1557 } 1558 else version (MIPS_Any) 1559 { 1560 version (MIPS32) enum store = "sw"; 1561 else version (MIPS64) enum store = "sd"; 1562 else static assert(0); 1563 1564 // Callee-save registers, according to MIPS Calling Convention 1565 // and MIPSpro N32 ABI Handbook, chapter 2, table 2-1. 1566 // FIXME: Should $28 (gp) and $30 (s8) be saved, too? 1567 size_t[8] regs = void; 1568 asm pure nothrow @nogc { ".set noat"; } 1569 static foreach (i; 0 .. regs.length) 1570 {{ 1571 enum int j = 16 + i; // source register 1572 asm pure nothrow @nogc { (store ~ " $"~j.stringof~", %0") : "=m" (regs[i]); } 1573 }} 1574 asm pure nothrow @nogc { (store ~ " $29, %0") : "=m" (sp); } 1575 asm pure nothrow @nogc { ".set at"; } 1576 } 1577 else version (RISCV_Any) 1578 { 1579 version (RISCV32) enum store = "sw"; 1580 else version (RISCV64) enum store = "sd"; 1581 else static assert(0); 1582 1583 // Callee-save registers, according to RISCV Calling Convention 1584 // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc 1585 size_t[24] regs = void; 1586 static foreach (i; 0 .. 12) 1587 {{ 1588 enum int j = i; 1589 asm pure nothrow @nogc { (store ~ " s"~j.stringof~", %0") : "=m" (regs[i]); } 1590 }} 1591 static foreach (i; 0 .. 12) 1592 {{ 1593 enum int j = i; 1594 asm pure nothrow @nogc { ("f" ~ store ~ " fs"~j.stringof~", %0") : "=m" (regs[i + 12]); } 1595 }} 1596 asm pure nothrow @nogc { (store ~ " sp, %0") : "=m" (sp); } 1597 } 1598 else version (LoongArch64) 1599 { 1600 // Callee-save registers, according to LoongArch Calling Convention 1601 // https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html 1602 size_t[18] regs = void; 1603 static foreach (i; 0 .. 8) 1604 {{ 1605 enum int j = i; 1606 // save $fs0 - $fs7 1607 asm pure nothrow @nogc { ( "fst.d $fs"~j.stringof~", %0") : "=m" (regs[i]); } 1608 }} 1609 static foreach (i; 0 .. 9) 1610 {{ 1611 enum int j = i; 1612 // save $s0 - $s8 1613 asm pure nothrow @nogc { ( "st.d $s"~j.stringof~", %0") : "=m" (regs[i + 8]); } 1614 }} 1615 // save $fp (or $s9) and $sp 1616 asm pure nothrow @nogc { ( "st.d $fp, %0") : "=m" (regs[17]); } 1617 asm pure nothrow @nogc { ( "st.d $sp, %0") : "=m" (sp); } 1618 } 1619 else 1620 { 1621 static assert(false, "Architecture not supported."); 1622 } 1623 } 1624 else 1625 { 1626 static assert(false, "Architecture not supported."); 1627 } 1628 1629 fn(sp); 1630 } 1631 1632 version (Windows) 1633 private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase _t) nothrow @system 1634 { 1635 auto t = _t.toThread; 1636 1637 scan( ScanType.stack, t.m_reg.ptr, t.m_reg.ptr + t.m_reg.length ); 1638 } 1639 1640 1641 /** 1642 * Returns the process ID of the calling process, which is guaranteed to be 1643 * unique on the system. This call is always successful. 1644 * 1645 * Example: 1646 * --- 1647 * writefln("Current process id: %s", getpid()); 1648 * --- 1649 */ 1650 version (Posix) 1651 { 1652 import core.sys.posix.unistd; 1653 1654 alias getpid = core.sys.posix.unistd.getpid; 1655 } 1656 else version (Windows) 1657 { 1658 alias getpid = core.sys.windows.winbase.GetCurrentProcessId; 1659 } 1660 1661 extern (C) @nogc nothrow 1662 { 1663 version (CRuntime_Glibc) version = PThread_Getattr_NP; 1664 version (CRuntime_Bionic) version = PThread_Getattr_NP; 1665 version (CRuntime_Musl) version = PThread_Getattr_NP; 1666 version (CRuntime_UClibc) version = PThread_Getattr_NP; 1667 1668 version (FreeBSD) version = PThread_Attr_Get_NP; 1669 version (NetBSD) version = PThread_Attr_Get_NP; 1670 version (DragonFlyBSD) version = PThread_Attr_Get_NP; 1671 1672 version (PThread_Getattr_NP) int pthread_getattr_np(pthread_t thread, pthread_attr_t* attr); 1673 version (PThread_Attr_Get_NP) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr); 1674 version (Solaris) int thr_stksegment(stack_t* stk); 1675 version (OpenBSD) int pthread_stackseg_np(pthread_t thread, stack_t* sinfo); 1676 } 1677 1678 1679 version (LDC) 1680 { 1681 version (X86) version = LDC_stackTopAsm; 1682 version (X86_64) version = LDC_stackTopAsm; 1683 version (ARM_Any) version = LDC_stackTopAsm; 1684 version (PPC_Any) version = LDC_stackTopAsm; 1685 version (MIPS_Any) version = LDC_stackTopAsm; 1686 1687 version (LDC_stackTopAsm) 1688 { 1689 /* The inline assembler is written in a style that the code can be inlined. 1690 * If it isn't, the function is still naked, so the caller's stack pointer 1691 * is used nevertheless. 1692 */ 1693 package extern(D) void* getStackTop() nothrow @nogc @naked 1694 { 1695 version (X86) 1696 return __asm!(void*)("movl %esp, $0", "=r"); 1697 else version (X86_64) 1698 return __asm!(void*)("movq %rsp, $0", "=r"); 1699 else version (ARM_Any) 1700 return __asm!(void*)("mov $0, sp", "=r"); 1701 else version (PPC_Any) 1702 return __asm!(void*)("mr $0, 1", "=r"); 1703 else version (MIPS_Any) 1704 return __asm!(void*)("move $0, $$sp", "=r"); 1705 else 1706 static assert(0); 1707 } 1708 } 1709 else 1710 { 1711 /* The use of intrinsic llvm_frameaddress is a reasonable default for 1712 * cpu architectures without assembler support from LLVM. Because of 1713 * the slightly different meaning the function must neither be inlined 1714 * nor naked. 1715 */ 1716 package extern(D) void* getStackTop() nothrow @nogc 1717 { 1718 import ldc.intrinsics; 1719 pragma(LDC_never_inline); 1720 return llvm_frameaddress(0); 1721 } 1722 } 1723 } 1724 else 1725 package extern(D) void* getStackTop() nothrow @nogc 1726 { 1727 version (D_InlineAsm_X86) 1728 asm pure nothrow @nogc { naked; mov EAX, ESP; ret; } 1729 else version (D_InlineAsm_X86_64) 1730 asm pure nothrow @nogc { naked; mov RAX, RSP; ret; } 1731 else version (GNU) 1732 return __builtin_frame_address(0); 1733 else 1734 static assert(false, "Architecture not supported."); 1735 } 1736 1737 1738 version (LDC_Windows) 1739 { 1740 package extern(D) void* getStackBottom() nothrow @nogc @naked 1741 { 1742 version (X86) 1743 return __asm!(void*)("mov %fs:(4), $0", "=r"); 1744 else version (X86_64) 1745 return __asm!(void*)("mov %gs:0($1), $0", "=r,r", 8); 1746 else 1747 static assert(false, "Architecture not supported."); 1748 } 1749 } 1750 else 1751 package extern(D) void* getStackBottom() nothrow @nogc @system 1752 { 1753 version (Windows) 1754 { 1755 version (D_InlineAsm_X86) 1756 asm pure nothrow @nogc { naked; mov EAX, FS:4; ret; } 1757 else version (D_InlineAsm_X86_64) 1758 asm pure nothrow @nogc 1759 { naked; 1760 mov RAX, 8; 1761 mov RAX, GS:[RAX]; 1762 ret; 1763 } 1764 else 1765 static assert(false, "Architecture not supported."); 1766 } 1767 else version (Darwin) 1768 { 1769 import core.sys.darwin.pthread; 1770 return pthread_get_stackaddr_np(pthread_self()); 1771 } 1772 else version (PThread_Getattr_NP) 1773 { 1774 pthread_attr_t attr; 1775 void* addr; size_t size; 1776 1777 pthread_attr_init(&attr); 1778 pthread_getattr_np(pthread_self(), &attr); 1779 pthread_attr_getstack(&attr, &addr, &size); 1780 pthread_attr_destroy(&attr); 1781 static if (isStackGrowingDown) 1782 addr += size; 1783 return addr; 1784 } 1785 else version (PThread_Attr_Get_NP) 1786 { 1787 pthread_attr_t attr; 1788 void* addr; size_t size; 1789 1790 pthread_attr_init(&attr); 1791 pthread_attr_get_np(pthread_self(), &attr); 1792 pthread_attr_getstack(&attr, &addr, &size); 1793 pthread_attr_destroy(&attr); 1794 static if (isStackGrowingDown) 1795 addr += size; 1796 return addr; 1797 } 1798 else version (OpenBSD) 1799 { 1800 stack_t stk; 1801 1802 pthread_stackseg_np(pthread_self(), &stk); 1803 return stk.ss_sp; 1804 } 1805 else version (Solaris) 1806 { 1807 stack_t stk; 1808 1809 thr_stksegment(&stk); 1810 return stk.ss_sp; 1811 } 1812 else 1813 static assert(false, "Platform not supported."); 1814 } 1815 1816 /** 1817 * Suspend the specified thread and load stack and register information for 1818 * use by thread_scanAll. If the supplied thread is the calling thread, 1819 * stack and register information will be loaded but the thread will not 1820 * be suspended. If the suspend operation fails and the thread is not 1821 * running then it will be removed from the global thread list, otherwise 1822 * an exception will be thrown. 1823 * 1824 * Params: 1825 * t = The thread to suspend. 1826 * 1827 * Throws: 1828 * ThreadError if the suspend operation fails for a running thread. 1829 * Returns: 1830 * Whether the thread is now suspended (true) or terminated (false). 1831 */ 1832 private extern (D) bool suspend( Thread t ) nothrow @nogc 1833 { 1834 Duration waittime = dur!"usecs"(10); 1835 Lagain: 1836 if (!t.isRunning) 1837 { 1838 Thread.remove(t); 1839 return false; 1840 } 1841 else if (t.m_isInCriticalRegion) 1842 { 1843 Thread.criticalRegionLock.unlock_nothrow(); 1844 Thread.sleep(waittime); 1845 if (waittime < dur!"msecs"(10)) waittime *= 2; 1846 Thread.criticalRegionLock.lock_nothrow(); 1847 goto Lagain; 1848 } 1849 1850 version (Windows) 1851 { 1852 if ( t.m_addr != GetCurrentThreadId() && SuspendThread( t.m_hndl ) == 0xFFFFFFFF ) 1853 { 1854 if ( !t.isRunning ) 1855 { 1856 Thread.remove( t ); 1857 return false; 1858 } 1859 onThreadError( "Unable to suspend thread" ); 1860 } 1861 1862 CONTEXT context = void; 1863 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; 1864 1865 if ( !GetThreadContext( t.m_hndl, &context ) ) 1866 onThreadError( "Unable to load thread context" ); 1867 version (X86) 1868 { 1869 if ( !t.m_lock ) 1870 t.m_curr.tstack = cast(void*) context.Esp; 1871 // eax,ebx,ecx,edx,edi,esi,ebp,esp 1872 t.m_reg[0] = context.Eax; 1873 t.m_reg[1] = context.Ebx; 1874 t.m_reg[2] = context.Ecx; 1875 t.m_reg[3] = context.Edx; 1876 t.m_reg[4] = context.Edi; 1877 t.m_reg[5] = context.Esi; 1878 t.m_reg[6] = context.Ebp; 1879 t.m_reg[7] = context.Esp; 1880 } 1881 else version (X86_64) 1882 { 1883 if ( !t.m_lock ) 1884 t.m_curr.tstack = cast(void*) context.Rsp; 1885 // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp 1886 t.m_reg[0] = context.Rax; 1887 t.m_reg[1] = context.Rbx; 1888 t.m_reg[2] = context.Rcx; 1889 t.m_reg[3] = context.Rdx; 1890 t.m_reg[4] = context.Rdi; 1891 t.m_reg[5] = context.Rsi; 1892 t.m_reg[6] = context.Rbp; 1893 t.m_reg[7] = context.Rsp; 1894 // r8,r9,r10,r11,r12,r13,r14,r15 1895 t.m_reg[8] = context.R8; 1896 t.m_reg[9] = context.R9; 1897 t.m_reg[10] = context.R10; 1898 t.m_reg[11] = context.R11; 1899 t.m_reg[12] = context.R12; 1900 t.m_reg[13] = context.R13; 1901 t.m_reg[14] = context.R14; 1902 t.m_reg[15] = context.R15; 1903 } 1904 else 1905 { 1906 static assert(false, "Architecture not supported." ); 1907 } 1908 } 1909 else version (Darwin) 1910 { 1911 if ( t.m_addr != pthread_self() && thread_suspend( t.m_tmach ) != KERN_SUCCESS ) 1912 { 1913 if ( !t.isRunning ) 1914 { 1915 Thread.remove( t ); 1916 return false; 1917 } 1918 onThreadError( "Unable to suspend thread" ); 1919 } 1920 1921 version (X86) 1922 { 1923 x86_thread_state32_t state = void; 1924 mach_msg_type_number_t count = x86_THREAD_STATE32_COUNT; 1925 1926 if ( thread_get_state( t.m_tmach, x86_THREAD_STATE32, &state, &count ) != KERN_SUCCESS ) 1927 onThreadError( "Unable to load thread state" ); 1928 if ( !t.m_lock ) 1929 t.m_curr.tstack = cast(void*) state.esp; 1930 // eax,ebx,ecx,edx,edi,esi,ebp,esp 1931 t.m_reg[0] = state.eax; 1932 t.m_reg[1] = state.ebx; 1933 t.m_reg[2] = state.ecx; 1934 t.m_reg[3] = state.edx; 1935 t.m_reg[4] = state.edi; 1936 t.m_reg[5] = state.esi; 1937 t.m_reg[6] = state.ebp; 1938 t.m_reg[7] = state.esp; 1939 } 1940 else version (X86_64) 1941 { 1942 x86_thread_state64_t state = void; 1943 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; 1944 1945 if ( thread_get_state( t.m_tmach, x86_THREAD_STATE64, &state, &count ) != KERN_SUCCESS ) 1946 onThreadError( "Unable to load thread state" ); 1947 if ( !t.m_lock ) 1948 t.m_curr.tstack = cast(void*) state.rsp; 1949 // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp 1950 t.m_reg[0] = state.rax; 1951 t.m_reg[1] = state.rbx; 1952 t.m_reg[2] = state.rcx; 1953 t.m_reg[3] = state.rdx; 1954 t.m_reg[4] = state.rdi; 1955 t.m_reg[5] = state.rsi; 1956 t.m_reg[6] = state.rbp; 1957 t.m_reg[7] = state.rsp; 1958 // r8,r9,r10,r11,r12,r13,r14,r15 1959 t.m_reg[8] = state.r8; 1960 t.m_reg[9] = state.r9; 1961 t.m_reg[10] = state.r10; 1962 t.m_reg[11] = state.r11; 1963 t.m_reg[12] = state.r12; 1964 t.m_reg[13] = state.r13; 1965 t.m_reg[14] = state.r14; 1966 t.m_reg[15] = state.r15; 1967 } 1968 else version (AArch64) 1969 { 1970 arm_thread_state64_t state = void; 1971 mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; 1972 1973 if (thread_get_state(t.m_tmach, ARM_THREAD_STATE64, &state, &count) != KERN_SUCCESS) 1974 onThreadError("Unable to load thread state"); 1975 // TODO: ThreadException here recurses forever! Does it 1976 //still using onThreadError? 1977 //printf("state count %d (expect %d)\n", count ,ARM_THREAD_STATE64_COUNT); 1978 if (!t.m_lock) 1979 t.m_curr.tstack = cast(void*) state.sp; 1980 1981 t.m_reg[0..29] = state.x; // x0-x28 1982 t.m_reg[29] = state.fp; // x29 1983 t.m_reg[30] = state.lr; // x30 1984 t.m_reg[31] = state.sp; // x31 1985 t.m_reg[32] = state.pc; 1986 } 1987 else version (ARM) 1988 { 1989 arm_thread_state32_t state = void; 1990 mach_msg_type_number_t count = ARM_THREAD_STATE32_COUNT; 1991 1992 // Thought this would be ARM_THREAD_STATE32, but that fails. 1993 // Mystery 1994 if (thread_get_state(t.m_tmach, ARM_THREAD_STATE, &state, &count) != KERN_SUCCESS) 1995 onThreadError("Unable to load thread state"); 1996 // TODO: in past, ThreadException here recurses forever! Does it 1997 //still using onThreadError? 1998 //printf("state count %d (expect %d)\n", count ,ARM_THREAD_STATE32_COUNT); 1999 if (!t.m_lock) 2000 t.m_curr.tstack = cast(void*) state.sp; 2001 2002 t.m_reg[0..13] = state.r; // r0 - r13 2003 t.m_reg[13] = state.sp; 2004 t.m_reg[14] = state.lr; 2005 t.m_reg[15] = state.pc; 2006 } 2007 else version (PPC) 2008 { 2009 ppc_thread_state_t state = void; 2010 mach_msg_type_number_t count = PPC_THREAD_STATE_COUNT; 2011 2012 if (thread_get_state(t.m_tmach, PPC_THREAD_STATE, &state, &count) != KERN_SUCCESS) 2013 onThreadError("Unable to load thread state"); 2014 if (!t.m_lock) 2015 t.m_curr.tstack = cast(void*) state.r[1]; 2016 t.m_reg[] = state.r[]; 2017 } 2018 else version (PPC64) 2019 { 2020 ppc_thread_state64_t state = void; 2021 mach_msg_type_number_t count = PPC_THREAD_STATE64_COUNT; 2022 2023 if (thread_get_state(t.m_tmach, PPC_THREAD_STATE64, &state, &count) != KERN_SUCCESS) 2024 onThreadError("Unable to load thread state"); 2025 if (!t.m_lock) 2026 t.m_curr.tstack = cast(void*) state.r[1]; 2027 t.m_reg[] = state.r[]; 2028 } 2029 else 2030 { 2031 static assert(false, "Architecture not supported." ); 2032 } 2033 } 2034 else version (Posix) 2035 { 2036 if ( t.m_addr != pthread_self() ) 2037 { 2038 if ( pthread_kill( t.m_addr, suspendSignalNumber ) != 0 ) 2039 { 2040 if ( !t.isRunning ) 2041 { 2042 Thread.remove( t ); 2043 return false; 2044 } 2045 onThreadError( "Unable to suspend thread" ); 2046 } 2047 } 2048 else if ( !t.m_lock ) 2049 { 2050 t.m_curr.tstack = getStackTop(); 2051 } 2052 } 2053 return true; 2054 } 2055 2056 /** 2057 * Suspend all threads but the calling thread for "stop the world" garbage 2058 * collection runs. This function may be called multiple times, and must 2059 * be followed by a matching number of calls to thread_resumeAll before 2060 * processing is resumed. 2061 * 2062 * Throws: 2063 * ThreadError if the suspend operation fails for a running thread. 2064 */ 2065 extern (C) void thread_suspendAll() nothrow 2066 { 2067 // NOTE: We've got an odd chicken & egg problem here, because while the GC 2068 // is required to call thread_init before calling any other thread 2069 // routines, thread_init may allocate memory which could in turn 2070 // trigger a collection. Thus, thread_suspendAll, thread_scanAll, 2071 // and thread_resumeAll must be callable before thread_init 2072 // completes, with the assumption that no other GC memory has yet 2073 // been allocated by the system, and thus there is no risk of losing 2074 // data if the global thread list is empty. The check of 2075 // Thread.sm_tbeg below is done to ensure thread_init has completed, 2076 // and therefore that calling Thread.getThis will not result in an 2077 // error. For the short time when Thread.sm_tbeg is null, there is 2078 // no reason not to simply call the multithreaded code below, with 2079 // the expectation that the foreach loop will never be entered. 2080 if ( !multiThreadedFlag && Thread.sm_tbeg ) 2081 { 2082 if ( ++suspendDepth == 1 ) 2083 suspend( Thread.getThis() ); 2084 2085 return; 2086 } 2087 2088 Thread.slock.lock_nothrow(); 2089 { 2090 if ( ++suspendDepth > 1 ) 2091 return; 2092 2093 Thread.criticalRegionLock.lock_nothrow(); 2094 scope (exit) Thread.criticalRegionLock.unlock_nothrow(); 2095 size_t cnt; 2096 bool suspendedSelf; 2097 Thread t = ThreadBase.sm_tbeg.toThread; 2098 while (t) 2099 { 2100 auto tn = t.next.toThread; 2101 if (suspend(t)) 2102 { 2103 if (t is ThreadBase.getThis()) 2104 suspendedSelf = true; 2105 ++cnt; 2106 } 2107 t = tn; 2108 } 2109 2110 version (Darwin) 2111 {} 2112 else version (Posix) 2113 { 2114 // Subtract own thread if we called suspend() on ourselves. 2115 // For example, suspendedSelf would be false if the current 2116 // thread ran thread_detachThis(). 2117 assert(cnt >= 1); 2118 if (suspendedSelf) 2119 --cnt; 2120 // wait for semaphore notifications 2121 for (; cnt; --cnt) 2122 { 2123 while (sem_wait(&suspendCount) != 0) 2124 { 2125 if (errno != EINTR) 2126 onThreadError("Unable to wait for semaphore"); 2127 errno = 0; 2128 } 2129 } 2130 } 2131 } 2132 } 2133 2134 /** 2135 * Resume the specified thread and unload stack and register information. 2136 * If the supplied thread is the calling thread, stack and register 2137 * information will be unloaded but the thread will not be resumed. If 2138 * the resume operation fails and the thread is not running then it will 2139 * be removed from the global thread list, otherwise an exception will be 2140 * thrown. 2141 * 2142 * Params: 2143 * t = The thread to resume. 2144 * 2145 * Throws: 2146 * ThreadError if the resume fails for a running thread. 2147 */ 2148 private extern (D) void resume(ThreadBase _t) nothrow @nogc 2149 { 2150 Thread t = _t.toThread; 2151 2152 version (Windows) 2153 { 2154 if ( t.m_addr != GetCurrentThreadId() && ResumeThread( t.m_hndl ) == 0xFFFFFFFF ) 2155 { 2156 if ( !t.isRunning ) 2157 { 2158 Thread.remove( t ); 2159 return; 2160 } 2161 onThreadError( "Unable to resume thread" ); 2162 } 2163 2164 if ( !t.m_lock ) 2165 t.m_curr.tstack = t.m_curr.bstack; 2166 t.m_reg[0 .. $] = 0; 2167 } 2168 else version (Darwin) 2169 { 2170 if ( t.m_addr != pthread_self() && thread_resume( t.m_tmach ) != KERN_SUCCESS ) 2171 { 2172 if ( !t.isRunning ) 2173 { 2174 Thread.remove( t ); 2175 return; 2176 } 2177 onThreadError( "Unable to resume thread" ); 2178 } 2179 2180 if ( !t.m_lock ) 2181 t.m_curr.tstack = t.m_curr.bstack; 2182 t.m_reg[0 .. $] = 0; 2183 } 2184 else version (Posix) 2185 { 2186 if ( t.m_addr != pthread_self() ) 2187 { 2188 if ( pthread_kill( t.m_addr, resumeSignalNumber ) != 0 ) 2189 { 2190 if ( !t.isRunning ) 2191 { 2192 Thread.remove( t ); 2193 return; 2194 } 2195 onThreadError( "Unable to resume thread" ); 2196 } 2197 } 2198 else if ( !t.m_lock ) 2199 { 2200 t.m_curr.tstack = t.m_curr.bstack; 2201 } 2202 } 2203 } 2204 2205 2206 /** 2207 * Initializes the thread module. This function must be called by the 2208 * garbage collector on startup and before any other thread routines 2209 * are called. 2210 */ 2211 extern (C) void thread_init() @nogc nothrow @system 2212 { 2213 // NOTE: If thread_init itself performs any allocations then the thread 2214 // routines reserved for garbage collector use may be called while 2215 // thread_init is being processed. However, since no memory should 2216 // exist to be scanned at this point, it is sufficient for these 2217 // functions to detect the condition and return immediately. 2218 2219 initLowlevelThreads(); 2220 Thread.initLocks(); 2221 2222 version (Darwin) 2223 { 2224 // thread id different in forked child process 2225 static extern(C) void initChildAfterFork() 2226 { 2227 auto thisThread = Thread.getThis(); 2228 thisThread.m_addr = pthread_self(); 2229 assert( thisThread.m_addr != thisThread.m_addr.init ); 2230 thisThread.m_tmach = pthread_mach_thread_np( thisThread.m_addr ); 2231 assert( thisThread.m_tmach != thisThread.m_tmach.init ); 2232 } 2233 pthread_atfork(null, null, &initChildAfterFork); 2234 } 2235 else version (Posix) 2236 { 2237 version (OpenBSD) 2238 { 2239 // OpenBSD does not support SIGRTMIN or SIGRTMAX 2240 // Use SIGUSR1 for SIGRTMIN, SIGUSR2 for SIGRTMIN + 1 2241 // And use 32 for SIGRTMAX (32 is the max signal number on OpenBSD) 2242 enum SIGRTMIN = SIGUSR1; 2243 enum SIGRTMAX = 32; 2244 } 2245 2246 if ( suspendSignalNumber == 0 ) 2247 { 2248 suspendSignalNumber = SIGRTMIN; 2249 } 2250 2251 if ( resumeSignalNumber == 0 ) 2252 { 2253 resumeSignalNumber = SIGRTMIN + 1; 2254 assert(resumeSignalNumber <= SIGRTMAX); 2255 } 2256 int status; 2257 sigaction_t suspend = void; 2258 sigaction_t resume = void; 2259 2260 // This is a quick way to zero-initialize the structs without using 2261 // memset or creating a link dependency on their static initializer. 2262 (cast(byte*) &suspend)[0 .. sigaction_t.sizeof] = 0; 2263 (cast(byte*) &resume)[0 .. sigaction_t.sizeof] = 0; 2264 2265 // NOTE: SA_RESTART indicates that system calls should restart if they 2266 // are interrupted by a signal, but this is not available on all 2267 // Posix systems, even those that support multithreading. 2268 static if ( __traits( compiles, SA_RESTART ) ) 2269 suspend.sa_flags = SA_RESTART; 2270 2271 suspend.sa_handler = &thread_suspendHandler; 2272 // NOTE: We want to ignore all signals while in this handler, so fill 2273 // sa_mask to indicate this. 2274 status = sigfillset( &suspend.sa_mask ); 2275 assert( status == 0 ); 2276 2277 // NOTE: Since resumeSignalNumber should only be issued for threads within the 2278 // suspend handler, we don't want this signal to trigger a 2279 // restart. 2280 resume.sa_flags = 0; 2281 resume.sa_handler = &thread_resumeHandler; 2282 // NOTE: We want to ignore all signals while in this handler, so fill 2283 // sa_mask to indicate this. 2284 status = sigfillset( &resume.sa_mask ); 2285 assert( status == 0 ); 2286 2287 status = sigaction( suspendSignalNumber, &suspend, null ); 2288 assert( status == 0 ); 2289 2290 status = sigaction( resumeSignalNumber, &resume, null ); 2291 assert( status == 0 ); 2292 2293 status = sem_init( &suspendCount, 0, 0 ); 2294 assert( status == 0 ); 2295 } 2296 _mainThreadStore[] = __traits(initSymbol, Thread)[]; 2297 Thread.sm_main = attachThread((cast(Thread)_mainThreadStore.ptr).__ctor()); 2298 } 2299 2300 private alias MainThreadStore = void[__traits(classInstanceSize, Thread)]; 2301 package __gshared align(__traits(classInstanceAlignment, Thread)) MainThreadStore _mainThreadStore; 2302 2303 /** 2304 * Terminates the thread module. No other thread routine may be called 2305 * afterwards. 2306 */ 2307 extern (C) void thread_term() @nogc nothrow 2308 { 2309 thread_term_tpl!(Thread)(_mainThreadStore); 2310 } 2311 2312 2313 /////////////////////////////////////////////////////////////////////////////// 2314 // Thread Entry Point and Signal Handlers 2315 /////////////////////////////////////////////////////////////////////////////// 2316 2317 2318 version (Windows) 2319 { 2320 private 2321 { 2322 // 2323 // Entry point for Windows threads 2324 // 2325 extern (Windows) uint thread_entryPoint( void* arg ) nothrow @system 2326 { 2327 version (Shared) 2328 { 2329 Thread obj = cast(Thread)(cast(void**)arg)[0]; 2330 auto loadedLibraries = (cast(void**)arg)[1]; 2331 .free(arg); 2332 } 2333 else 2334 { 2335 Thread obj = cast(Thread)arg; 2336 } 2337 assert( obj ); 2338 2339 // loadedLibraries need to be inherited from parent thread 2340 // before initilizing GC for TLS (rt_tlsgc_init) 2341 version (Shared) 2342 { 2343 externDFunc!("rt.sections_elf_shared.inheritLoadedLibraries", 2344 void function(void*) @nogc nothrow)(loadedLibraries); 2345 } 2346 2347 obj.initDataStorage(); 2348 2349 Thread.setThis(obj); 2350 Thread.add(obj); 2351 scope (exit) 2352 { 2353 Thread.remove(obj); 2354 obj.destroyDataStorage(); 2355 } 2356 Thread.add(&obj.m_main); 2357 2358 // NOTE: No GC allocations may occur until the stack pointers have 2359 // been set and Thread.getThis returns a valid reference to 2360 // this thread object (this latter condition is not strictly 2361 // necessary on Windows but it should be followed for the 2362 // sake of consistency). 2363 2364 // TODO: Consider putting an auto exception object here (using 2365 // alloca) forOutOfMemoryError plus something to track 2366 // whether an exception is in-flight? 2367 2368 void append( Throwable t ) 2369 { 2370 obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t); 2371 } 2372 2373 version (D_InlineAsm_X86) 2374 { 2375 asm nothrow @nogc { fninit; } 2376 } 2377 2378 try 2379 { 2380 rt_moduleTlsCtor(); 2381 try 2382 { 2383 obj.run(); 2384 } 2385 catch ( Throwable t ) 2386 { 2387 append( t ); 2388 } 2389 rt_moduleTlsDtor(); 2390 version (Shared) 2391 { 2392 externDFunc!("rt.sections_elf_shared.cleanupLoadedLibraries", 2393 void function() @nogc nothrow)(); 2394 } 2395 } 2396 catch ( Throwable t ) 2397 { 2398 append( t ); 2399 } 2400 return 0; 2401 } 2402 2403 2404 HANDLE GetCurrentThreadHandle() nothrow @nogc 2405 { 2406 const uint DUPLICATE_SAME_ACCESS = 0x00000002; 2407 2408 HANDLE curr = GetCurrentThread(), 2409 proc = GetCurrentProcess(), 2410 hndl; 2411 2412 DuplicateHandle( proc, curr, proc, &hndl, 0, TRUE, DUPLICATE_SAME_ACCESS ); 2413 return hndl; 2414 } 2415 } 2416 } 2417 else version (Posix) 2418 { 2419 private 2420 { 2421 import core.stdc.errno; 2422 import core.sys.posix.semaphore; 2423 import core.sys.posix.stdlib; // for malloc, valloc, free, atexit 2424 import core.sys.posix.pthread; 2425 import core.sys.posix.signal; 2426 import core.sys.posix.time; 2427 2428 version (Darwin) 2429 { 2430 import core.sys.darwin.mach.thread_act; 2431 import core.sys.darwin.pthread : pthread_mach_thread_np; 2432 } 2433 2434 // 2435 // Entry point for POSIX threads 2436 // 2437 extern (C) void* thread_entryPoint( void* arg ) nothrow @system 2438 { 2439 version (Shared) 2440 { 2441 Thread obj = cast(Thread)(cast(void**)arg)[0]; 2442 auto loadedLibraries = (cast(void**)arg)[1]; 2443 .free(arg); 2444 } 2445 else 2446 { 2447 Thread obj = cast(Thread)arg; 2448 } 2449 assert( obj ); 2450 2451 // loadedLibraries need to be inherited from parent thread 2452 // before initilizing GC for TLS (rt_tlsgc_init) 2453 version (Shared) 2454 { 2455 externDFunc!("rt.sections_elf_shared.inheritLoadedLibraries", 2456 void function(void*) @nogc nothrow)(loadedLibraries); 2457 } 2458 2459 obj.initDataStorage(); 2460 2461 atomicStore!(MemoryOrder.raw)(obj.m_isRunning, true); 2462 Thread.setThis(obj); // allocates lazy TLS (see Issue 11981) 2463 Thread.add(obj); // can only receive signals from here on 2464 scope (exit) 2465 { 2466 Thread.remove(obj); 2467 atomicStore!(MemoryOrder.raw)(obj.m_isRunning, false); 2468 obj.destroyDataStorage(); 2469 } 2470 Thread.add(&obj.m_main); 2471 2472 static extern (C) void thread_cleanupHandler( void* arg ) nothrow @nogc 2473 { 2474 Thread obj = cast(Thread) arg; 2475 assert( obj ); 2476 2477 // NOTE: If the thread terminated abnormally, just set it as 2478 // not running and let thread_suspendAll remove it from 2479 // the thread list. This is safer and is consistent 2480 // with the Windows thread code. 2481 atomicStore!(MemoryOrder.raw)(obj.m_isRunning,false); 2482 } 2483 2484 // NOTE: Using void to skip the initialization here relies on 2485 // knowledge of how pthread_cleanup is implemented. It may 2486 // not be appropriate for all platforms. However, it does 2487 // avoid the need to link the pthread module. If any 2488 // implementation actually requires default initialization 2489 // then pthread_cleanup should be restructured to maintain 2490 // the current lack of a link dependency. 2491 static if ( __traits( compiles, pthread_cleanup ) ) 2492 { 2493 pthread_cleanup cleanup = void; 2494 cleanup.push( &thread_cleanupHandler, cast(void*) obj ); 2495 } 2496 else static if ( __traits( compiles, pthread_cleanup_push ) ) 2497 { 2498 pthread_cleanup_push( &thread_cleanupHandler, cast(void*) obj ); 2499 } 2500 else 2501 { 2502 static assert( false, "Platform not supported." ); 2503 } 2504 2505 // NOTE: No GC allocations may occur until the stack pointers have 2506 // been set and Thread.getThis returns a valid reference to 2507 // this thread object (this latter condition is not strictly 2508 // necessary on Windows but it should be followed for the 2509 // sake of consistency). 2510 2511 // TODO: Consider putting an auto exception object here (using 2512 // alloca) forOutOfMemoryError plus something to track 2513 // whether an exception is in-flight? 2514 2515 void append( Throwable t ) 2516 { 2517 obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t); 2518 } 2519 try 2520 { 2521 rt_moduleTlsCtor(); 2522 try 2523 { 2524 obj.run(); 2525 } 2526 catch ( Throwable t ) 2527 { 2528 append( t ); 2529 } 2530 rt_moduleTlsDtor(); 2531 version (Shared) 2532 { 2533 externDFunc!("rt.sections_elf_shared.cleanupLoadedLibraries", 2534 void function() @nogc nothrow)(); 2535 } 2536 } 2537 catch ( Throwable t ) 2538 { 2539 append( t ); 2540 } 2541 2542 // NOTE: Normal cleanup is handled by scope(exit). 2543 2544 static if ( __traits( compiles, pthread_cleanup ) ) 2545 { 2546 cleanup.pop( 0 ); 2547 } 2548 else static if ( __traits( compiles, pthread_cleanup_push ) ) 2549 { 2550 pthread_cleanup_pop( 0 ); 2551 } 2552 2553 return null; 2554 } 2555 2556 2557 // 2558 // Used to track the number of suspended threads 2559 // 2560 __gshared sem_t suspendCount; 2561 2562 2563 extern (C) void thread_suspendHandler( int sig ) nothrow 2564 in 2565 { 2566 assert( sig == suspendSignalNumber ); 2567 } 2568 do 2569 { 2570 void op(void* sp) nothrow 2571 { 2572 // NOTE: Since registers are being pushed and popped from the 2573 // stack, any other stack data used by this function should 2574 // be gone before the stack cleanup code is called below. 2575 Thread obj = Thread.getThis(); 2576 assert(obj !is null); 2577 2578 if ( !obj.m_lock ) 2579 { 2580 obj.m_curr.tstack = getStackTop(); 2581 } 2582 2583 sigset_t sigres = void; 2584 int status; 2585 2586 status = sigfillset( &sigres ); 2587 assert( status == 0 ); 2588 2589 status = sigdelset( &sigres, resumeSignalNumber ); 2590 assert( status == 0 ); 2591 2592 status = sem_post( &suspendCount ); 2593 assert( status == 0 ); 2594 2595 sigsuspend( &sigres ); 2596 2597 if ( !obj.m_lock ) 2598 { 2599 obj.m_curr.tstack = obj.m_curr.bstack; 2600 } 2601 } 2602 callWithStackShell(&op); 2603 } 2604 2605 2606 extern (C) void thread_resumeHandler( int sig ) nothrow 2607 in 2608 { 2609 assert( sig == resumeSignalNumber ); 2610 } 2611 do 2612 { 2613 2614 } 2615 } 2616 } 2617 else 2618 { 2619 // NOTE: This is the only place threading versions are checked. If a new 2620 // version is added, the module code will need to be searched for 2621 // places where version-specific code may be required. This can be 2622 // easily accomlished by searching for 'Windows' or 'Posix'. 2623 static assert( false, "Unknown threading implementation." ); 2624 } 2625 2626 // 2627 // exposed by compiler runtime 2628 // 2629 extern (C) void rt_moduleTlsCtor(); 2630 extern (C) void rt_moduleTlsDtor(); 2631 2632 2633 // regression test for Issue 13416 2634 version (FreeBSD) unittest 2635 { 2636 static void loop() 2637 { 2638 pthread_attr_t attr; 2639 pthread_attr_init(&attr); 2640 auto thr = pthread_self(); 2641 foreach (i; 0 .. 50) 2642 pthread_attr_get_np(thr, &attr); 2643 pthread_attr_destroy(&attr); 2644 } 2645 2646 auto thr = new Thread(&loop).start(); 2647 foreach (i; 0 .. 50) 2648 { 2649 thread_suspendAll(); 2650 thread_resumeAll(); 2651 } 2652 thr.join(); 2653 } 2654 2655 version (DragonFlyBSD) unittest 2656 { 2657 static void loop() 2658 { 2659 pthread_attr_t attr; 2660 pthread_attr_init(&attr); 2661 auto thr = pthread_self(); 2662 foreach (i; 0 .. 50) 2663 pthread_attr_get_np(thr, &attr); 2664 pthread_attr_destroy(&attr); 2665 } 2666 2667 auto thr = new Thread(&loop).start(); 2668 foreach (i; 0 .. 50) 2669 { 2670 thread_suspendAll(); 2671 thread_resumeAll(); 2672 } 2673 thr.join(); 2674 } 2675 2676 2677 /////////////////////////////////////////////////////////////////////////////// 2678 // lowlovel threading support 2679 /////////////////////////////////////////////////////////////////////////////// 2680 2681 private 2682 { 2683 version (Windows): 2684 // If the runtime is dynamically loaded as a DLL, there is a problem with 2685 // threads still running when the DLL is supposed to be unloaded: 2686 // 2687 // - with the VC runtime starting with VS2015 (i.e. using the Universal CRT) 2688 // a thread created with _beginthreadex increments the DLL reference count 2689 // and decrements it when done, so that the DLL is no longer unloaded unless 2690 // all the threads have terminated. With the DLL reference count held up 2691 // by a thread that is only stopped by a signal from a static destructor or 2692 // the termination of the runtime will cause the DLL to never be unloaded. 2693 // 2694 // - with the DigitalMars runtime and VC runtime up to VS2013, the thread 2695 // continues to run, but crashes once the DLL is unloaded from memory as 2696 // the code memory is no longer accessible. Stopping the threads is not possible 2697 // from within the runtime termination as it is invoked from 2698 // DllMain(DLL_PROCESS_DETACH) holding a lock that prevents threads from 2699 // terminating. 2700 // 2701 // Solution: start a watchdog thread that keeps the DLL reference count above 0 and 2702 // checks it periodically. If it is equal to 1 (plus the number of started threads), no 2703 // external references to the DLL exist anymore, threads can be stopped 2704 // and runtime termination and DLL unload can be invoked via FreeLibraryAndExitThread. 2705 // Note: runtime termination is then performed by a different thread than at startup. 2706 // 2707 // Note: if the DLL is never unloaded, process termination kills all threads 2708 // and signals their handles before unconditionally calling DllMain(DLL_PROCESS_DETACH). 2709 2710 import core.sys.windows.winbase : FreeLibraryAndExitThread, GetModuleHandleExW, 2711 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; 2712 import core.sys.windows.windef : HMODULE; 2713 import core.sys.windows.dll : dll_getRefCount; 2714 2715 version (CRuntime_Microsoft) 2716 extern(C) extern __gshared ubyte msvcUsesUCRT; // from rt/msvc.d 2717 2718 /// set during termination of a DLL on Windows, i.e. while executing DllMain(DLL_PROCESS_DETACH) 2719 public __gshared bool thread_DLLProcessDetaching; 2720 2721 __gshared HMODULE ll_dllModule; 2722 __gshared ThreadID ll_dllMonitorThread; 2723 2724 int ll_countLowLevelThreadsWithDLLUnloadCallback() nothrow @system 2725 { 2726 lowlevelLock.lock_nothrow(); 2727 scope(exit) lowlevelLock.unlock_nothrow(); 2728 2729 int cnt = 0; 2730 foreach (i; 0 .. ll_nThreads) 2731 if (ll_pThreads[i].cbDllUnload) 2732 cnt++; 2733 return cnt; 2734 } 2735 2736 bool ll_dllHasExternalReferences() nothrow 2737 { 2738 version (CRuntime_DigitalMars) 2739 enum internalReferences = 1; // only the watchdog thread 2740 else 2741 int internalReferences = msvcUsesUCRT ? 1 + ll_countLowLevelThreadsWithDLLUnloadCallback() : 1; 2742 2743 int refcnt = dll_getRefCount(ll_dllModule); 2744 return refcnt > internalReferences; 2745 } 2746 2747 private void monitorDLLRefCnt() nothrow @system 2748 { 2749 // this thread keeps the DLL alive until all external references are gone 2750 while (ll_dllHasExternalReferences()) 2751 { 2752 Thread.sleep(100.msecs); 2753 } 2754 2755 // the current thread will be terminated below 2756 ll_removeThread(GetCurrentThreadId()); 2757 2758 for (;;) 2759 { 2760 ThreadID tid; 2761 void delegate() nothrow cbDllUnload; 2762 { 2763 lowlevelLock.lock_nothrow(); 2764 scope(exit) lowlevelLock.unlock_nothrow(); 2765 2766 foreach (i; 0 .. ll_nThreads) 2767 if (ll_pThreads[i].cbDllUnload) 2768 { 2769 cbDllUnload = ll_pThreads[i].cbDllUnload; 2770 tid = ll_pThreads[0].tid; 2771 } 2772 } 2773 if (!cbDllUnload) 2774 break; 2775 cbDllUnload(); 2776 assert(!findLowLevelThread(tid)); 2777 } 2778 2779 FreeLibraryAndExitThread(ll_dllModule, 0); 2780 } 2781 2782 int ll_getDLLRefCount() nothrow @nogc 2783 { 2784 if (!ll_dllModule && 2785 !GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 2786 cast(const(wchar)*) &ll_getDLLRefCount, &ll_dllModule)) 2787 return -1; 2788 return dll_getRefCount(ll_dllModule); 2789 } 2790 2791 bool ll_startDLLUnloadThread() nothrow @nogc 2792 { 2793 int refcnt = ll_getDLLRefCount(); 2794 if (refcnt < 0) 2795 return false; // not a dynamically loaded DLL 2796 2797 if (ll_dllMonitorThread !is ThreadID.init) 2798 return true; 2799 2800 // if a thread is created from a DLL, the MS runtime (starting with VC2015) increments the DLL reference count 2801 // to avoid the DLL being unloaded while the thread is still running. Mimick this behavior here for all 2802 // runtimes not doing this 2803 version (CRuntime_DigitalMars) 2804 enum needRef = true; 2805 else 2806 bool needRef = !msvcUsesUCRT; 2807 2808 if (needRef) 2809 { 2810 HMODULE hmod; 2811 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, cast(const(wchar)*) &ll_getDLLRefCount, &hmod); 2812 } 2813 2814 ll_dllMonitorThread = createLowLevelThread(() { monitorDLLRefCnt(); }); 2815 return ll_dllMonitorThread != ThreadID.init; 2816 } 2817 } 2818 2819 /** 2820 * Create a thread not under control of the runtime, i.e. TLS module constructors are 2821 * not run and the GC does not suspend it during a collection. 2822 * 2823 * Params: 2824 * dg = delegate to execute in the created thread. 2825 * stacksize = size of the stack of the created thread. The default of 0 will select the 2826 * platform-specific default size. 2827 * cbDllUnload = Windows only: if running in a dynamically loaded DLL, this delegate will be called 2828 * if the DLL is supposed to be unloaded, but the thread is still running. 2829 * The thread must be terminated via `joinLowLevelThread` by the callback. 2830 * 2831 * Returns: the platform specific thread ID of the new thread. If an error occurs, `ThreadID.init` 2832 * is returned. 2833 */ 2834 ThreadID createLowLevelThread(void delegate() nothrow dg, uint stacksize = 0, 2835 void delegate() nothrow cbDllUnload = null) nothrow @nogc @system 2836 { 2837 void delegate() nothrow* context = cast(void delegate() nothrow*)malloc(dg.sizeof); 2838 *context = dg; 2839 2840 ThreadID tid; 2841 version (Windows) 2842 { 2843 // the thread won't start until after the DLL is unloaded 2844 if (thread_DLLProcessDetaching) 2845 return ThreadID.init; 2846 2847 static extern (Windows) uint thread_lowlevelEntry(void* ctx) nothrow 2848 { 2849 auto dg = *cast(void delegate() nothrow*)ctx; 2850 free(ctx); 2851 2852 dg(); 2853 ll_removeThread(GetCurrentThreadId()); 2854 return 0; 2855 } 2856 2857 // see Thread.start() for why thread is created in suspended state 2858 HANDLE hThread = cast(HANDLE) _beginthreadex(null, stacksize, &thread_lowlevelEntry, 2859 context, CREATE_SUSPENDED, &tid); 2860 if (!hThread) 2861 return ThreadID.init; 2862 } 2863 2864 lowlevelLock.lock_nothrow(); 2865 scope(exit) lowlevelLock.unlock_nothrow(); 2866 2867 ll_nThreads++; 2868 ll_pThreads = cast(ll_ThreadData*)realloc(ll_pThreads, ll_ThreadData.sizeof * ll_nThreads); 2869 2870 version (Windows) 2871 { 2872 ll_pThreads[ll_nThreads - 1].tid = tid; 2873 ll_pThreads[ll_nThreads - 1].cbDllUnload = cbDllUnload; 2874 if (ResumeThread(hThread) == -1) 2875 onThreadError("Error resuming thread"); 2876 CloseHandle(hThread); 2877 2878 if (cbDllUnload) 2879 ll_startDLLUnloadThread(); 2880 } 2881 else version (Posix) 2882 { 2883 static extern (C) void* thread_lowlevelEntry(void* ctx) nothrow 2884 { 2885 auto dg = *cast(void delegate() nothrow*)ctx; 2886 free(ctx); 2887 2888 dg(); 2889 ll_removeThread(pthread_self()); 2890 return null; 2891 } 2892 2893 size_t stksz = adjustStackSize(stacksize); 2894 2895 pthread_attr_t attr; 2896 2897 int rc; 2898 if ((rc = pthread_attr_init(&attr)) != 0) 2899 return ThreadID.init; 2900 if (stksz && (rc = pthread_attr_setstacksize(&attr, stksz)) != 0) 2901 return ThreadID.init; 2902 if ((rc = pthread_create(&tid, &attr, &thread_lowlevelEntry, context)) != 0) 2903 return ThreadID.init; 2904 if ((rc = pthread_attr_destroy(&attr)) != 0) 2905 return ThreadID.init; 2906 2907 ll_pThreads[ll_nThreads - 1].tid = tid; 2908 } 2909 return tid; 2910 } 2911 2912 /** 2913 * Wait for a thread created with `createLowLevelThread` to terminate. 2914 * 2915 * Note: In a Windows DLL, if this function is called via DllMain with 2916 * argument DLL_PROCESS_DETACH, the thread is terminated forcefully 2917 * without proper cleanup as a deadlock would happen otherwise. 2918 * 2919 * Params: 2920 * tid = the thread ID returned by `createLowLevelThread`. 2921 */ 2922 void joinLowLevelThread(ThreadID tid) nothrow @nogc 2923 { 2924 version (Windows) 2925 { 2926 HANDLE handle = OpenThreadHandle(tid); 2927 if (!handle) 2928 return; 2929 2930 if (thread_DLLProcessDetaching) 2931 { 2932 // When being called from DllMain/DLL_DETACH_PROCESS, threads cannot stop 2933 // due to the loader lock being held by the current thread. 2934 // On the other hand, the thread must not continue to run as it will crash 2935 // if the DLL is unloaded. The best guess is to terminate it immediately. 2936 TerminateThread(handle, 1); 2937 WaitForSingleObject(handle, 10); // give it some time to terminate, but don't wait indefinitely 2938 } 2939 else 2940 WaitForSingleObject(handle, INFINITE); 2941 CloseHandle(handle); 2942 } 2943 else version (Posix) 2944 { 2945 if (pthread_join(tid, null) != 0) 2946 onThreadError("Unable to join thread"); 2947 } 2948 } 2949 2950 nothrow @nogc unittest 2951 { 2952 struct TaskWithContect 2953 { 2954 shared int n = 0; 2955 void run() nothrow 2956 { 2957 n.atomicOp!"+="(1); 2958 } 2959 } 2960 TaskWithContect task; 2961 2962 ThreadID[8] tids; 2963 for (int i = 0; i < tids.length; i++) 2964 { 2965 tids[i] = createLowLevelThread(&task.run); 2966 assert(tids[i] != ThreadID.init); 2967 } 2968 2969 for (int i = 0; i < tids.length; i++) 2970 joinLowLevelThread(tids[i]); 2971 2972 assert(task.n == tids.length); 2973 } 2974 2975 version (Posix) 2976 private size_t adjustStackSize(size_t sz) nothrow @nogc 2977 { 2978 if (sz == 0) 2979 return 0; 2980 2981 // stack size must be at least PTHREAD_STACK_MIN for most platforms. 2982 if (PTHREAD_STACK_MIN > sz) 2983 sz = PTHREAD_STACK_MIN; 2984 2985 version (CRuntime_Glibc) 2986 { 2987 // On glibc, TLS uses the top of the stack, so add its size to the requested size 2988 sz += externDFunc!("rt.sections_elf_shared.sizeOfTLS", 2989 size_t function() @nogc nothrow)(); 2990 } 2991 2992 // stack size must be a multiple of pageSize 2993 sz = ((sz + pageSize - 1) & ~(pageSize - 1)); 2994 2995 return sz; 2996 }