2 * The osthread module provides low-level, OS-dependent code
3 * for thread creation and management.
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)
13 /* NOTE: This file has been patched from the original DMD distribution to
14 * work with the GDC compiler.
16 module core
.thread
.osthread
;
18 import core
.thread
.threadbase
;
19 import core
.thread
.context
;
20 import core
.thread
.types
;
22 import core
.memory
: GC
, pageSize
;
24 import core
.exception
: onOutOfMemoryError
;
25 import core
.internal
.traits
: externDFunc
;
28 ///////////////////////////////////////////////////////////////////////////////
29 // Platform Detection and Memory Allocation
30 ///////////////////////////////////////////////////////////////////////////////
38 else version (WatchOS
)
45 version (D_InlineAsm_X86
)
48 version = AsmX86_Windows
;
50 version = AsmX86_Posix
;
52 else version (D_InlineAsm_X86_64
)
56 version = AsmX86_64_Windows
;
60 version = AsmX86_64_Posix
;
67 version = AsmExternal
;
73 version (D_X32
) {} else
75 version = AsmExternal
;
82 version = AsmExternal
;
85 else version (MIPS_O32
)
89 version = AsmExternal
;
92 else version (AArch64
)
96 version = AsmExternal
;
103 version = AsmExternal
;
109 version (AsmX86_Windows
) {} else
110 version (AsmX86_Posix
) {} else
111 version (AsmX86_64_Windows
) {} else
112 version (AsmX86_64_Posix
) {} else
113 version (AsmExternal
) {} else
115 // NOTE: The ucontext implementation requires architecture specific
116 // data definitions to operate so testing for it must be done
117 // by checking for the existence of ucontext_t rather than by
118 // a version identifier. Please note that this is considered
119 // an obsolescent feature according to the POSIX spec, so a
120 // custom solution is still preferred.
121 import core
.sys
.posix
.ucontext
;
127 import core
.stdc
.stdint
: uintptr_t
; // for _beginthreadex decl below
128 import core
.stdc
.stdlib
; // for malloc, atexit
129 import core
.sys
.windows
.basetsd
/+: HANDLE+/;
130 import core
.sys
.windows
.threadaux
/+: getThreadStackBottom, impersonate_thread, OpenThreadHandle+/;
131 import core
.sys
.windows
.winbase
/+: CloseHandle, CREATE_SUSPENDED, DuplicateHandle, GetCurrentThread,
132 GetCurrentThreadId, GetCurrentProcess, GetExitCodeThread, GetSystemInfo, GetThreadContext,
133 GetThreadPriority, INFINITE, ResumeThread, SetThreadPriority, Sleep, STILL_ACTIVE,
134 SuspendThread, SwitchToThread, SYSTEM_INFO, THREAD_PRIORITY_IDLE, THREAD_PRIORITY_NORMAL,
135 THREAD_PRIORITY_TIME_CRITICAL, WAIT_OBJECT_0, WaitForSingleObject+/;
136 import core
.sys
.windows
.windef
/+: TRUE+/;
137 import core
.sys
.windows
.winnt
/+: CONTEXT, CONTEXT_CONTROL, CONTEXT_INTEGER+/;
139 private extern (Windows
) alias btex_fptr
= uint function(void*);
140 private extern (C
) uintptr_t
_beginthreadex(void*, uint, btex_fptr
, void*, uint, uint*) nothrow @nogc;
144 import core
.stdc
.errno
;
145 import core
.sys
.posix
.semaphore
;
146 import core
.sys
.posix
.stdlib
; // for malloc, valloc, free, atexit
147 import core
.sys
.posix
.pthread
;
148 import core
.sys
.posix
.signal
;
149 import core
.sys
.posix
.time
;
153 import core
.sys
.darwin
.mach
.thread_act
;
154 import core
.sys
.darwin
.pthread
: pthread_mach_thread_np
;
160 import core
.sys
.solaris
.sys
.priocntl
;
161 import core
.sys
.solaris
.sys
.types
;
162 import core
.sys
.posix
.sys
.wait : idtype_t
;
171 * Hook for whatever EH implementation is used to save/restore some data
175 * newContext = The return value of the prior call to this function
176 * where the stack was last swapped out, or null when a fiber stack
177 * is switched in for the first time.
179 private extern(C
) void* _d_eh_swapContext(void* newContext
) nothrow @nogc;
181 version (DigitalMars
)
185 extern(D
) void* swapContext(void* newContext
) nothrow @nogc
187 return _d_eh_swapContext(newContext
);
192 extern(C
) void* _d_eh_swapContextDwarf(void* newContext
) nothrow @nogc;
194 extern(D
) void* swapContext(void* newContext
) nothrow @nogc
196 /* Detect at runtime which scheme is being used.
197 * Eventually, determine it statically.
199 static int which
= 0;
204 assert(newContext
== null);
205 auto p
= _d_eh_swapContext(newContext
);
206 auto pdwarf
= _d_eh_swapContextDwarf(newContext
);
220 return _d_eh_swapContext(newContext
);
222 return _d_eh_swapContextDwarf(newContext
);
229 extern(D
) void* swapContext(void* newContext
) nothrow @nogc
231 return _d_eh_swapContext(newContext
);
235 ///////////////////////////////////////////////////////////////////////////////
237 ///////////////////////////////////////////////////////////////////////////////
240 * This class encapsulates all threading functionality for the D
241 * programming language. As thread manipulation is a required facility
242 * for garbage collection, all user threads should derive from this
243 * class, and instances of this class should never be explicitly deleted.
244 * A new thread may be created using either derivation or composition, as
245 * in the following example.
247 class Thread
: ThreadBase
250 // Standard thread data
254 private HANDLE m_hndl
;
259 private shared bool m_isRunning
;
264 private mach_port_t m_tmach
;
269 private __gshared
bool m_isRTClass
;
281 alias TLSKey
= pthread_key_t
;
284 ///////////////////////////////////////////////////////////////////////////
286 ///////////////////////////////////////////////////////////////////////////
290 * Initializes a thread object which is associated with a static
294 * fn = The thread function.
295 * sz = The stack size for this thread.
298 * fn must not be null.
300 this( void function() fn
, size_t sz
= 0 ) @safe pure nothrow @nogc
307 * Initializes a thread object which is associated with a dynamic
311 * dg = The thread function.
312 * sz = The stack size for this thread.
315 * dg must not be null.
317 this( void delegate() dg
, size_t sz
= 0 ) @safe pure nothrow @nogc
322 package this( size_t sz
= 0 ) @safe pure nothrow @nogc
328 * Cleans up any remaining resources used by this object.
330 ~this() nothrow @nogc
332 if (super.destructBeforeDtor())
337 m_addr
= m_addr
.init
;
338 CloseHandle( m_hndl
);
339 m_hndl
= m_hndl
.init
;
343 if (m_addr
!= m_addr
.init
)
344 pthread_detach( m_addr
);
345 m_addr
= m_addr
.init
;
349 m_tmach
= m_tmach
.init
;
354 // Thread entry point. Invokes the function or delegate passed on
355 // construction (if any).
357 private final void run()
363 * Provides a reference to the calling thread.
366 * The thread object representing the calling thread. The result of
367 * deleting this object is undefined. If the current thread is not
368 * attached to the runtime, a null reference is returned.
370 static Thread
getThis() @safe nothrow @nogc
372 return ThreadBase
.getThis().toThread
;
375 ///////////////////////////////////////////////////////////////////////////
376 // Thread Context and GC Scanning Support
377 ///////////////////////////////////////////////////////////////////////////
384 uint[8] m_reg
; // edi,esi,ebp,esp,ebx,edx,ecx,eax
386 else version (X86_64
)
388 ulong[16] m_reg
; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
389 // r8,r9,r10,r11,r12,r13,r14,r15
393 static assert(false, "Architecture not supported." );
396 else version (Darwin
)
400 uint[8] m_reg
; // edi,esi,ebp,esp,ebx,edx,ecx,eax
402 else version (X86_64
)
404 ulong[16] m_reg
; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
405 // r8,r9,r10,r11,r12,r13,r14,r15
407 else version (AArch64
)
409 ulong[33] m_reg
; // x0-x31, pc
413 uint[16] m_reg
; // r0-r15
417 // Make the assumption that we only care about non-fp and non-vr regs.
418 // ??? : it seems plausible that a valid address can be copied into a VR.
419 uint[32] m_reg
; // r0-31
424 ulong[32] m_reg
; // r0-31
428 static assert(false, "Architecture not supported." );
433 ///////////////////////////////////////////////////////////////////////////
435 ///////////////////////////////////////////////////////////////////////////
439 * Starts the thread and invokes the function or delegate passed upon
443 * This routine may only be called once per thread instance.
446 * ThreadException if the thread fails to start.
448 final Thread
start() nothrow
451 assert( !next
&& !prev
);
455 auto wasThreaded
= multiThreadedFlag
;
456 multiThreadedFlag
= true;
460 multiThreadedFlag
= false;
463 version (Windows
) {} else
466 size_t stksz
= adjustStackSize( m_sz
);
470 if ( pthread_attr_init( &attr
) )
471 onThreadError( "Error initializing thread attributes" );
472 if ( stksz
&& pthread_attr_setstacksize( &attr
, stksz
) )
473 onThreadError( "Error initializing thread stack size" );
478 // NOTE: If a thread is just executing DllMain()
479 // while another thread is started here, it holds an OS internal
480 // lock that serializes DllMain with CreateThread. As the code
481 // might request a synchronization on slock (e.g. in thread_findByAddr()),
482 // we cannot hold that lock while creating the thread without
483 // creating a deadlock
485 // Solution: Create the thread in suspended state and then
486 // add and resume it with slock acquired
487 assert(m_sz
<= uint.max
, "m_sz must be less than or equal to uint.max");
488 m_hndl
= cast(HANDLE
) _beginthreadex( null, cast(uint) m_sz
, &thread_entryPoint
, cast(void*) this, CREATE_SUSPENDED
, &m_addr
);
489 if ( cast(size_t
) m_hndl
== 0 )
490 onThreadError( "Error creating thread" );
493 slock
.lock_nothrow();
494 scope(exit
) slock
.unlock_nothrow();
496 incrementAboutToStart(this);
500 if ( ResumeThread( m_hndl
) == -1 )
501 onThreadError( "Error resuming thread" );
505 // NOTE: This is also set to true by thread_entryPoint, but set it
506 // here as well so the calling thread will see the isRunning
507 // state immediately.
508 atomicStore
!(MemoryOrder
.raw
)(m_isRunning
, true);
509 scope( failure
) atomicStore
!(MemoryOrder
.raw
)(m_isRunning
, false);
515 auto libs
= externDFunc
!("gcc.sections.pinLoadedLibraries",
516 void* function() @nogc nothrow)();
520 auto libs
= externDFunc
!("rt.sections_elf_shared.pinLoadedLibraries",
521 void* function() @nogc nothrow)();
524 auto ps
= cast(void**).malloc(2 * size_t
.sizeof
);
525 if (ps
is null) onOutOfMemoryError();
526 ps
[0] = cast(void*)this;
527 ps
[1] = cast(void*)libs
;
528 if ( pthread_create( &m_addr
, &attr
, &thread_entryPoint
, ps
) != 0 )
532 externDFunc
!("gcc.sections.unpinLoadedLibraries",
533 void function(void*) @nogc nothrow)(libs
);
537 externDFunc
!("rt.sections_elf_shared.unpinLoadedLibraries",
538 void function(void*) @nogc nothrow)(libs
);
541 onThreadError( "Error creating thread" );
546 if ( pthread_create( &m_addr
, &attr
, &thread_entryPoint
, cast(void*) this ) != 0 )
547 onThreadError( "Error creating thread" );
549 if ( pthread_attr_destroy( &attr
) != 0 )
550 onThreadError( "Error destroying thread attributes" );
554 m_tmach
= pthread_mach_thread_np( m_addr
);
555 if ( m_tmach
== m_tmach
.init
)
556 onThreadError( "Error creating thread" );
564 * Waits for this thread to complete. If the thread terminated as the
565 * result of an unhandled exception, this exception will be rethrown.
568 * rethrow = Rethrow any unhandled exception which may have caused this
569 * thread to terminate.
572 * ThreadException if the operation fails.
573 * Any exception not handled by the joined thread.
576 * Any exception not handled by this thread if rethrow = false, null
579 override final Throwable
join( bool rethrow
= true )
583 if ( m_addr
!= m_addr
.init
&& WaitForSingleObject( m_hndl
, INFINITE
) != WAIT_OBJECT_0
)
584 throw new ThreadException( "Unable to join thread" );
585 // NOTE: m_addr must be cleared before m_hndl is closed to avoid
586 // a race condition with isRunning. The operation is done
587 // with atomicStore to prevent compiler reordering.
588 atomicStore
!(MemoryOrder
.raw
)(*cast(shared)&m_addr
, m_addr
.init
);
589 CloseHandle( m_hndl
);
590 m_hndl
= m_hndl
.init
;
594 if ( m_addr
!= m_addr
.init
&& pthread_join( m_addr
, null ) != 0 )
595 throw new ThreadException( "Unable to join thread" );
596 // NOTE: pthread_join acts as a substitute for pthread_detach,
597 // which is normally called by the dtor. Setting m_addr
598 // to zero ensures that pthread_detach will not be called
599 // on object destruction.
600 m_addr
= m_addr
.init
;
612 ///////////////////////////////////////////////////////////////////////////
613 // Thread Priority Actions
614 ///////////////////////////////////////////////////////////////////////////
618 @property static int PRIORITY_MIN() @nogc nothrow pure @safe
620 return THREAD_PRIORITY_IDLE
;
623 @property static const(int) PRIORITY_MAX() @nogc nothrow pure @safe
625 return THREAD_PRIORITY_TIME_CRITICAL
;
628 @property static int PRIORITY_DEFAULT() @nogc nothrow pure @safe
630 return THREAD_PRIORITY_NORMAL
;
635 private struct Priority
637 int PRIORITY_MIN
= int.min
;
638 int PRIORITY_DEFAULT
= int.min
;
639 int PRIORITY_MAX
= int.min
;
643 Lazily loads one of the members stored in a hidden global variable of
644 type `Priority`. Upon the first access of either member, the entire
645 `Priority` structure is initialized. Multiple initializations from
646 different threads calling this function are tolerated.
648 `which` must be one of `PRIORITY_MIN`, `PRIORITY_DEFAULT`,
651 private static shared Priority cache
;
652 private static int loadGlobal(string which
)()
654 auto local
= atomicLoad(mixin("cache." ~ which
));
655 if (local
!= local
.min
) return local
;
656 // There will be benign races
657 cache
= loadPriorities
;
658 return atomicLoad(mixin("cache." ~ which
));
662 Loads all priorities and returns them as a `Priority` structure. This
663 function is thread-neutral.
665 private static Priority
loadPriorities() @nogc nothrow @trusted
673 pcParms
.pc_cid
= PC_CLNULL
;
674 if (priocntl(idtype_t
.P_PID
, P_MYID
, PC_GETPARMS
, &pcParms
) == -1)
675 assert( 0, "Unable to get scheduling class" );
677 pcInfo
.pc_cid
= pcParms
.pc_cid
;
678 // PC_GETCLINFO ignores the first two args, use dummy values
679 if (priocntl(idtype_t
.P_PID
, 0, PC_GETCLINFO
, &pcInfo
) == -1)
680 assert( 0, "Unable to get scheduling class info" );
682 pri_t
* clparms
= cast(pri_t
*)&pcParms
.pc_clparms
;
683 pri_t
* clinfo
= cast(pri_t
*)&pcInfo
.pc_clinfo
;
685 result
.PRIORITY_MAX
= clparms
[0];
687 if (pcInfo
.pc_clname
== "RT")
691 // For RT class, just assume it can't be changed
692 result
.PRIORITY_MIN
= clparms
[0];
693 result
.PRIORITY_DEFAULT
= clparms
[0];
699 // For all other scheduling classes, there are
700 // two key values -- uprilim and maxupri.
701 // maxupri is the maximum possible priority defined
702 // for the scheduling class, and valid priorities
703 // range are in [-maxupri, maxupri].
705 // However, uprilim is an upper limit that the
706 // current thread can set for the current scheduling
707 // class, which can be less than maxupri. As such,
708 // use this value for priorityMax since this is
709 // the effective maximum.
712 result
.PRIORITY_MIN
= -cast(int)(clinfo
[0]);
714 result
.PRIORITY_DEFAULT
= 0;
721 pthread_getschedparam( pthread_self(), &policy
, ¶m
) == 0
722 ||
assert(0, "Internal error in pthread_getschedparam");
724 result
.PRIORITY_MIN
= sched_get_priority_min( policy
);
725 result
.PRIORITY_MIN
!= -1
726 ||
assert(0, "Internal error in sched_get_priority_min");
727 result
.PRIORITY_DEFAULT
= param
.sched_priority
;
728 result
.PRIORITY_MAX
= sched_get_priority_max( policy
);
729 result
.PRIORITY_MAX
!= -1 ||
730 assert(0, "Internal error in sched_get_priority_max");
734 static assert(0, "Your code here.");
740 * The minimum scheduling priority that may be set for a thread. On
741 * systems where multiple scheduling policies are defined, this value
742 * represents the minimum valid priority for the scheduling policy of
745 @property static int PRIORITY_MIN() @nogc nothrow pure @trusted
747 return (cast(int function() @nogc nothrow pure @safe)
748 &loadGlobal
!"PRIORITY_MIN")();
752 * The maximum scheduling priority that may be set for a thread. On
753 * systems where multiple scheduling policies are defined, this value
754 * represents the maximum valid priority for the scheduling policy of
757 @property static const(int) PRIORITY_MAX() @nogc nothrow pure @trusted
759 return (cast(int function() @nogc nothrow pure @safe)
760 &loadGlobal
!"PRIORITY_MAX")();
764 * The default scheduling priority that is set for a thread. On
765 * systems where multiple scheduling policies are defined, this value
766 * represents the default priority for the scheduling policy of
769 @property static int PRIORITY_DEFAULT() @nogc nothrow pure @trusted
771 return (cast(int function() @nogc nothrow pure @safe)
772 &loadGlobal
!"PRIORITY_DEFAULT")();
778 //NetBSD does not support priority for default policy
779 // and it is not possible change policy without root access
780 int fakePriority
= int.max
;
784 * Gets the scheduling priority for the associated thread.
786 * Note: Getting the priority of a thread that already terminated
787 * might return the default priority.
790 * The scheduling priority of this thread.
792 final @property int priority()
796 return GetThreadPriority( m_hndl
);
798 else version (NetBSD
)
800 return fakePriority
==int.max? PRIORITY_DEFAULT
: fakePriority
;
807 if (auto err
= pthread_getschedparam(m_addr
, &policy
, ¶m
))
809 // ignore error if thread is not running => Bugzilla 8960
810 if (!atomicLoad(m_isRunning
)) return PRIORITY_DEFAULT
;
811 throw new ThreadException("Unable to get thread priority");
813 return param
.sched_priority
;
819 * Sets the scheduling priority for the associated thread.
821 * Note: Setting the priority of a thread that already terminated
822 * might have no effect.
825 * val = The new scheduling priority of this thread.
827 final @property void priority( int val
)
830 assert(val
>= PRIORITY_MIN
);
831 assert(val
<= PRIORITY_MAX
);
837 if ( !SetThreadPriority( m_hndl
, val
) )
838 throw new ThreadException( "Unable to set thread priority" );
840 else version (Solaris
)
842 // the pthread_setschedprio(3c) and pthread_setschedparam functions
843 // are broken for the default (TS / time sharing) scheduling class.
844 // instead, we use priocntl(2) which gives us the desired behavior.
846 // We hardcode the min and max priorities to the current value
847 // so this is a no-op for RT threads.
853 pcparm
.pc_cid
= PC_CLNULL
;
854 if (priocntl(idtype_t
.P_LWPID
, P_MYID
, PC_GETPARMS
, &pcparm
) == -1)
855 throw new ThreadException( "Unable to get scheduling class" );
857 pri_t
* clparms
= cast(pri_t
*)&pcparm
.pc_clparms
;
859 // clparms is filled in by the PC_GETPARMS call, only necessary
860 // to adjust the element that contains the thread priority
861 clparms
[1] = cast(pri_t
) val
;
863 if (priocntl(idtype_t
.P_LWPID
, P_MYID
, PC_SETPARMS
, &pcparm
) == -1)
864 throw new ThreadException( "Unable to set scheduling class" );
866 else version (NetBSD
)
872 static if (__traits(compiles
, pthread_setschedprio
))
874 if (auto err
= pthread_setschedprio(m_addr
, val
))
876 // ignore error if thread is not running => Bugzilla 8960
877 if (!atomicLoad(m_isRunning
)) return;
878 throw new ThreadException("Unable to set thread priority");
883 // NOTE: pthread_setschedprio is not implemented on Darwin, FreeBSD, OpenBSD,
884 // or DragonFlyBSD, so use the more complicated get/set sequence below.
888 if (auto err
= pthread_getschedparam(m_addr
, &policy
, ¶m
))
890 // ignore error if thread is not running => Bugzilla 8960
891 if (!atomicLoad(m_isRunning
)) return;
892 throw new ThreadException("Unable to set thread priority");
894 param
.sched_priority
= val
;
895 if (auto err
= pthread_setschedparam(m_addr
, policy
, ¶m
))
897 // ignore error if thread is not running => Bugzilla 8960
898 if (!atomicLoad(m_isRunning
)) return;
899 throw new ThreadException("Unable to set thread priority");
908 auto thr
= Thread
.getThis();
909 immutable prio
= thr
.priority
;
910 scope (exit
) thr
.priority
= prio
;
912 assert(prio
== PRIORITY_DEFAULT
);
913 assert(prio
>= PRIORITY_MIN
&& prio
<= PRIORITY_MAX
);
914 thr
.priority
= PRIORITY_MIN
;
915 assert(thr
.priority
== PRIORITY_MIN
);
916 thr
.priority
= PRIORITY_MAX
;
917 assert(thr
.priority
== PRIORITY_MAX
);
920 unittest // Bugzilla 8960
922 import core
.sync
.semaphore
;
924 auto thr
= new Thread({});
926 Thread
.sleep(1.msecs
); // wait a little so the thread likely has finished
927 thr
.priority
= PRIORITY_MAX
; // setting priority doesn't cause error
928 auto prio
= thr
.priority
; // getting priority doesn't cause error
929 assert(prio
>= PRIORITY_MIN
&& prio
<= PRIORITY_MAX
);
933 * Tests whether this thread is running.
936 * true if the thread is running, false if not.
938 override final @property bool isRunning() nothrow @nogc
940 if (!super.isRunning())
946 GetExitCodeThread( m_hndl
, &ecode
);
947 return ecode
== STILL_ACTIVE
;
951 return atomicLoad(m_isRunning
);
956 ///////////////////////////////////////////////////////////////////////////
957 // Actions on Calling Thread
958 ///////////////////////////////////////////////////////////////////////////
962 * Suspends the calling thread for at least the supplied period. This may
963 * result in multiple OS calls if period is greater than the maximum sleep
964 * duration supported by the operating system.
967 * val = The minimum duration the calling thread should be suspended.
970 * period must be non-negative.
973 * ------------------------------------------------------------------------
975 * Thread.sleep( dur!("msecs")( 50 ) ); // sleep for 50 milliseconds
976 * Thread.sleep( dur!("seconds")( 5 ) ); // sleep for 5 seconds
978 * ------------------------------------------------------------------------
980 static void sleep( Duration val
) @nogc nothrow
983 assert( !val
.isNegative
);
989 auto maxSleepMillis
= dur
!("msecs")( uint.max
- 1 );
991 // avoid a non-zero time to be round down to 0
992 if ( val
> dur
!"msecs"( 0 ) && val
< dur
!"msecs"( 1 ) )
993 val
= dur
!"msecs"( 1 );
995 // NOTE: In instances where all other threads in the process have a
996 // lower priority than the current thread, the current thread
997 // will not yield with a sleep time of zero. However, unlike
998 // yield(), the user is not asking for a yield to occur but
999 // only for execution to suspend for the requested interval.
1000 // Therefore, expected performance may not be met if a yield
1001 // is forced upon the user.
1002 while ( val
> maxSleepMillis
)
1005 maxSleepMillis
.total
!"msecs" );
1006 val
-= maxSleepMillis
;
1008 Sleep( cast(uint) val
.total
!"msecs" );
1010 else version (Posix
)
1012 timespec tin
= void;
1013 timespec tout
= void;
1015 val
.split
!("seconds", "nsecs")(tin
.tv_sec
, tin
.tv_nsec
);
1016 if ( val
.total
!"seconds" > tin
.tv_sec
.max
)
1017 tin
.tv_sec
= tin
.tv_sec
.max
;
1020 if ( !nanosleep( &tin
, &tout
) )
1022 if ( errno
!= EINTR
)
1023 assert(0, "Unable to sleep for the specified duration");
1031 * Forces a context switch to occur away from the calling thread.
1033 static void yield() @nogc nothrow
1037 else version (Posix
)
1042 private Thread
toThread(return scope ThreadBase t
) @trusted nothrow @nogc pure
1044 return cast(Thread
) cast(void*) t
;
1047 private extern(D
) static void thread_yield() @nogc nothrow
1055 class DerivedThread
: Thread
1065 // Derived thread running.
1071 // Composed thread running.
1074 // create and start instances of each type
1075 auto derived
= new DerivedThread().start();
1076 auto composed
= new Thread(&threadFunc
).start();
1078 // Codes to run in the newly created thread.
1096 enum MSG
= "Test message.";
1104 throw new Exception( MSG
);
1106 assert( false, "Expected rethrown exception." );
1108 catch ( Throwable t
)
1110 assert( t
.msg
== MSG
);
1117 // use >pageSize to avoid stack overflow (e.g. in an syscall)
1118 auto thr
= new Thread(function{}, 4096 + 1).start();
1125 import core
.memory
: GC
;
1127 auto t1
= new Thread({
1128 foreach (_
; 0 .. 20)
1131 auto t2
= new Thread({
1132 foreach (_
; 0 .. 20)
1141 import core
.sync
.semaphore
;
1142 auto sem
= new Semaphore();
1144 auto t
= new Thread(
1147 Thread
.sleep(100.msecs
);
1150 sem
.wait(); // thread cannot be detached while being started
1151 thread_detachInstance(t
);
1152 foreach (t2
; Thread
)
1159 // NOTE: This entire test is based on the assumption that no
1160 // memory is allocated after the child thread is
1161 // started. If an allocation happens, a collection could
1162 // trigger, which would cause the synchronization below
1163 // to cause a deadlock.
1164 // NOTE: DO NOT USE LOCKS IN CRITICAL REGIONS IN NORMAL CODE.
1166 import core
.sync
.semaphore
;
1168 auto sema
= new Semaphore(),
1169 semb
= new Semaphore();
1171 auto thr
= new Thread(
1173 thread_enterCriticalRegion();
1174 assert(thread_inCriticalRegion());
1178 assert(thread_inCriticalRegion());
1180 thread_exitCriticalRegion();
1181 assert(!thread_inCriticalRegion());
1185 assert(!thread_inCriticalRegion());
1191 synchronized (ThreadBase
.criticalRegionLock
)
1192 assert(thr
.m_isInCriticalRegion
);
1196 synchronized (ThreadBase
.criticalRegionLock
)
1197 assert(!thr
.m_isInCriticalRegion
);
1203 // https://issues.dlang.org/show_bug.cgi?id=22124
1206 Thread thread
= new Thread({});
1207 auto fun(Thread t
, int x
)
1212 static assert(!__traits(compiles
, () @nogc => fun(thread
, 3) ));
1217 import core
.sync
.semaphore
;
1219 shared bool inCriticalRegion
;
1220 auto sema
= new Semaphore(),
1221 semb
= new Semaphore();
1223 auto thr
= new Thread(
1225 thread_enterCriticalRegion();
1226 inCriticalRegion
= true;
1230 Thread
.sleep(dur
!"msecs"(1));
1231 inCriticalRegion
= false;
1232 thread_exitCriticalRegion();
1237 assert(inCriticalRegion
);
1240 thread_suspendAll();
1241 assert(!inCriticalRegion
);
1245 ///////////////////////////////////////////////////////////////////////////////
1246 // GC Support Routines
1247 ///////////////////////////////////////////////////////////////////////////////
1252 * Instruct the thread module, when initialized, to use a different set of
1253 * signals besides SIGRTMIN and SIGRTMIN + 1 for suspension and resumption of threads.
1254 * This function should be called at most once, prior to thread_init().
1255 * This function is Posix-only.
1257 extern (C
) void thread_setGCSignals(int suspendSignalNo
, int resumeSignalNo
) nothrow @nogc
1261 else version (Posix
)
1263 extern (C
) void thread_setGCSignals(int suspendSignalNo
, int resumeSignalNo
) nothrow @nogc
1266 assert(suspendSignalNo
!= 0);
1267 assert(resumeSignalNo
!= 0);
1271 assert(suspendSignalNumber
!= 0);
1272 assert(resumeSignalNumber
!= 0);
1276 suspendSignalNumber
= suspendSignalNo
;
1277 resumeSignalNumber
= resumeSignalNo
;
1283 private __gshared
int suspendSignalNumber
;
1284 private __gshared
int resumeSignalNumber
;
1287 private extern (D
) ThreadBase
attachThread(ThreadBase _thisThread
) @nogc nothrow
1289 Thread thisThread
= _thisThread
.toThread();
1291 StackContext
* thisContext
= &thisThread
.m_main
;
1292 assert( thisContext
== thisThread
.m_curr
);
1296 thisThread
.m_addr
= GetCurrentThreadId();
1297 thisThread
.m_hndl
= GetCurrentThreadHandle();
1298 thisContext
.bstack
= getStackBottom();
1299 thisContext
.tstack
= thisContext
.bstack
;
1301 else version (Posix
)
1303 thisThread
.m_addr
= pthread_self();
1304 thisContext
.bstack
= getStackBottom();
1305 thisContext
.tstack
= thisContext
.bstack
;
1307 atomicStore
!(MemoryOrder
.raw
)(thisThread
.toThread
.m_isRunning
, true);
1309 thisThread
.m_isDaemon
= true;
1310 thisThread
.tlsGCdataInit();
1311 Thread
.setThis( thisThread
);
1315 thisThread
.m_tmach
= pthread_mach_thread_np( thisThread
.m_addr
);
1316 assert( thisThread
.m_tmach
!= thisThread
.m_tmach
.init
);
1319 Thread
.add( thisThread
, false );
1320 Thread
.add( thisContext
);
1321 if ( Thread
.sm_main
!is null )
1322 multiThreadedFlag
= true;
1327 * Registers the calling thread for use with the D Runtime. If this routine
1328 * is called for a thread which is already registered, no action is performed.
1330 * NOTE: This routine does not run thread-local static constructors when called.
1331 * If full functionality as a D thread is desired, the following function
1332 * must be called after thread_attachThis:
1334 * extern (C) void rt_moduleTlsCtor();
1337 * $(REF thread_detachThis, core,thread,threadbase)
1339 extern(C
) Thread
thread_attachThis()
1341 return thread_attachThis_tpl
!Thread();
1347 // NOTE: These calls are not safe on Posix systems that use signals to
1348 // perform garbage collection. The suspendHandler uses getThis()
1349 // to get the thread handle so getThis() must be a simple call.
1350 // Mutexes can't safely be acquired inside signal handlers, and
1351 // even if they could, the mutex needed (Thread.slock) is held by
1352 // thread_suspendAll(). So in short, these routines will remain
1353 // Windows-specific. If they are truly needed elsewhere, the
1354 // suspendHandler will need a way to call a version of getThis()
1355 // that only does the TLS lookup without the fancy fallback stuff.
1358 extern (C
) Thread
thread_attachByAddr( ThreadID addr
)
1360 return thread_attachByAddrB( addr
, getThreadStackBottom( addr
) );
1365 extern (C
) Thread
thread_attachByAddrB( ThreadID addr
, void* bstack
)
1367 GC
.disable(); scope(exit
) GC
.enable();
1369 if (auto t
= thread_findByAddr(addr
).toThread
)
1372 Thread thisThread
= new Thread();
1373 StackContext
* thisContext
= &thisThread
.m_main
;
1374 assert( thisContext
== thisThread
.m_curr
);
1376 thisThread
.m_addr
= addr
;
1377 thisContext
.bstack
= bstack
;
1378 thisContext
.tstack
= thisContext
.bstack
;
1380 thisThread
.m_isDaemon
= true;
1382 if ( addr
== GetCurrentThreadId() )
1384 thisThread
.m_hndl
= GetCurrentThreadHandle();
1385 thisThread
.tlsGCdataInit();
1386 Thread
.setThis( thisThread
);
1390 thisThread
.m_hndl
= OpenThreadHandle( addr
);
1391 impersonate_thread(addr
,
1393 thisThread
.tlsGCdataInit();
1394 Thread
.setThis( thisThread
);
1398 Thread
.add( thisThread
, false );
1399 Thread
.add( thisContext
);
1400 if ( Thread
.sm_main
!is null )
1401 multiThreadedFlag
= true;
1407 // Calls the given delegate, passing the current thread's stack pointer to it.
1408 package extern(D
) void callWithStackShell(scope callWithStackShellDg fn
) nothrow
1411 // The purpose of the 'shell' is to ensure all the registers get
1412 // put on the stack so they'll be scanned. We only need to push
1413 // the callee-save registers.
1417 // The generic solution below using a call to __builtin_unwind_init ()
1418 // followed by an assignment to sp has two issues:
1419 // 1) On some archs it stores a huge amount of FP and Vector state which
1420 // is not the subject of the scan - and, indeed might produce false
1422 // 2) Even on archs like X86, where there are no callee-saved FPRs/VRs there
1423 // tend to be 'holes' in the frame allocations (to deal with alignment) which
1424 // also will contain random data which could produce false positives.
1425 // This solution stores only the integer callee-saved registers.
1428 void*[3] regs
= void;
1429 asm pure nothrow @nogc
1431 "movl %%ebx, %0" : "=m" (regs
[0]);
1432 "movl %%esi, %0" : "=m" (regs
[1]);
1433 "movl %%edi, %0" : "=m" (regs
[2]);
1435 sp
= cast(void*)®s
[0];
1437 else version (X86_64
)
1439 void*[5] regs
= void;
1440 asm pure nothrow @nogc
1442 "movq %%rbx, %0" : "=m" (regs
[0]);
1443 "movq %%r12, %0" : "=m" (regs
[1]);
1444 "movq %%r13, %0" : "=m" (regs
[2]);
1445 "movq %%r14, %0" : "=m" (regs
[3]);
1446 "movq %%r15, %0" : "=m" (regs
[4]);
1448 sp
= cast(void*)®s
[0];
1452 void*[19] regs
= void;
1457 static foreach (i
; 0 .. regs
.length
)
1459 enum int j
= 13 + i
; // source register
1460 asm pure nothrow @nogc
1462 "stw "~regname
~j
.stringof
~", %0" : "=m" (regs
[i
]);
1465 sp
= cast(void*)®s
[0];
1467 else version (PPC64
)
1469 void*[19] regs
= void;
1474 static foreach (i
; 0 .. regs
.length
)
1476 enum int j
= 13 + i
; // source register
1477 asm pure nothrow @nogc
1479 "std "~regname
~j
.stringof
~", %0" : "=m" (regs
[i
]);
1482 sp
= cast(void*)®s
[0];
1484 else version (AArch64
)
1486 // Callee-save registers, x19-x28 according to AAPCS64, section
1487 // 5.1.1. Include x29 fp because it optionally can be a callee
1489 size_t
[11] regs
= void;
1490 // store the registers in pairs
1491 asm pure nothrow @nogc
1493 "stp x19, x20, %0" : "=m" (regs
[ 0]), "=m" (regs
[1]);
1494 "stp x21, x22, %0" : "=m" (regs
[ 2]), "=m" (regs
[3]);
1495 "stp x23, x24, %0" : "=m" (regs
[ 4]), "=m" (regs
[5]);
1496 "stp x25, x26, %0" : "=m" (regs
[ 6]), "=m" (regs
[7]);
1497 "stp x27, x28, %0" : "=m" (regs
[ 8]), "=m" (regs
[9]);
1498 "str x29, %0" : "=m" (regs
[10]);
1499 "mov %0, sp" : "=r" (sp
);
1504 // Callee-save registers, according to AAPCS, section 5.1.1.
1505 // arm and thumb2 instructions
1506 size_t
[8] regs
= void;
1507 asm pure nothrow @nogc
1509 "stm %0, {r4-r11}" : : "r" (regs
.ptr
) : "memory";
1510 "mov %0, sp" : "=r" (sp
);
1515 __builtin_unwind_init();
1519 else version (AsmX86_Posix
)
1521 size_t
[3] regs
= void;
1522 asm pure nothrow @nogc
1524 mov [regs
+ 0 * 4], EBX
;
1525 mov [regs
+ 1 * 4], ESI
;
1526 mov [regs
+ 2 * 4], EDI
;
1531 else version (AsmX86_Windows
)
1533 size_t
[3] regs
= void;
1534 asm pure nothrow @nogc
1536 mov [regs
+ 0 * 4], EBX
;
1537 mov [regs
+ 1 * 4], ESI
;
1538 mov [regs
+ 2 * 4], EDI
;
1543 else version (AsmX86_64_Posix
)
1545 size_t
[5] regs
= void;
1546 asm pure nothrow @nogc
1548 mov [regs
+ 0 * 8], RBX
;
1549 mov [regs
+ 1 * 8], R12
;
1550 mov [regs
+ 2 * 8], R13
;
1551 mov [regs
+ 3 * 8], R14
;
1552 mov [regs
+ 4 * 8], R15
;
1557 else version (AsmX86_64_Windows
)
1559 size_t
[7] regs
= void;
1560 asm pure nothrow @nogc
1562 mov [regs
+ 0 * 8], RBX
;
1563 mov [regs
+ 1 * 8], RSI
;
1564 mov [regs
+ 2 * 8], RDI
;
1565 mov [regs
+ 3 * 8], R12
;
1566 mov [regs
+ 4 * 8], R13
;
1567 mov [regs
+ 5 * 8], R14
;
1568 mov [regs
+ 6 * 8], R15
;
1575 static assert(false, "Architecture not supported.");
1582 private extern (D
) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan
, ThreadBase _t
) nothrow
1584 auto t
= _t
.toThread
;
1586 scan( ScanType
.stack
, t
.m_reg
.ptr
, t
.m_reg
.ptr
+ t
.m_reg
.length
);
1591 * Returns the process ID of the calling process, which is guaranteed to be
1592 * unique on the system. This call is always successful.
1596 * writefln("Current process id: %s", getpid());
1601 import core
.sys
.posix
.unistd
;
1603 alias getpid
= core
.sys
.posix
.unistd
.getpid
;
1605 else version (Windows
)
1607 alias getpid
= core
.sys
.windows
.winbase
.GetCurrentProcessId
;
1610 extern (C
) @nogc nothrow
1612 version (CRuntime_Glibc
) version = PThread_Getattr_NP
;
1613 version (CRuntime_Bionic
) version = PThread_Getattr_NP
;
1614 version (CRuntime_Musl
) version = PThread_Getattr_NP
;
1615 version (CRuntime_UClibc
) version = PThread_Getattr_NP
;
1617 version (FreeBSD
) version = PThread_Attr_Get_NP
;
1618 version (NetBSD
) version = PThread_Attr_Get_NP
;
1619 version (DragonFlyBSD
) version = PThread_Attr_Get_NP
;
1621 version (PThread_Getattr_NP
) int pthread_getattr_np(pthread_t thread
, pthread_attr_t
* attr
);
1622 version (PThread_Attr_Get_NP
) int pthread_attr_get_np(pthread_t thread
, pthread_attr_t
* attr
);
1623 version (Solaris
) int thr_stksegment(stack_t
* stk
);
1624 version (OpenBSD
) int pthread_stackseg_np(pthread_t thread
, stack_t
* sinfo
);
1628 package extern(D
) void* getStackTop() nothrow @nogc
1630 version (D_InlineAsm_X86
)
1631 asm pure nothrow @nogc { naked
; mov EAX
, ESP
; ret; }
1632 else version (D_InlineAsm_X86_64
)
1633 asm pure nothrow @nogc { naked
; mov RAX
, RSP
; ret; }
1635 return __builtin_frame_address(0);
1637 static assert(false, "Architecture not supported.");
1641 package extern(D
) void* getStackBottom() nothrow @nogc
1645 version (D_InlineAsm_X86
)
1646 asm pure nothrow @nogc { naked
; mov EAX
, FS
:4; ret; }
1647 else version (D_InlineAsm_X86_64
)
1648 asm pure nothrow @nogc
1654 else version (GNU_InlineAsm
)
1659 asm pure nothrow @nogc { "movl %%fs:4, %0;" : "=r" (bottom
); }
1660 else version (X86_64
)
1661 asm pure nothrow @nogc { "movq %%gs:8, %0;" : "=r" (bottom
); }
1663 static assert(false, "Platform not supported.");
1668 static assert(false, "Architecture not supported.");
1670 else version (Darwin
)
1672 import core
.sys
.darwin
.pthread
;
1673 return pthread_get_stackaddr_np(pthread_self());
1675 else version (PThread_Getattr_NP
)
1677 pthread_attr_t attr
;
1678 void* addr
; size_t size
;
1680 pthread_attr_init(&attr
);
1681 pthread_getattr_np(pthread_self(), &attr
);
1682 pthread_attr_getstack(&attr
, &addr
, &size
);
1683 pthread_attr_destroy(&attr
);
1684 static if (isStackGrowingDown
)
1688 else version (PThread_Attr_Get_NP
)
1690 pthread_attr_t attr
;
1691 void* addr
; size_t size
;
1693 pthread_attr_init(&attr
);
1694 pthread_attr_get_np(pthread_self(), &attr
);
1695 pthread_attr_getstack(&attr
, &addr
, &size
);
1696 pthread_attr_destroy(&attr
);
1697 static if (isStackGrowingDown
)
1701 else version (OpenBSD
)
1705 pthread_stackseg_np(pthread_self(), &stk
);
1708 else version (Solaris
)
1712 thr_stksegment(&stk
);
1716 static assert(false, "Platform not supported.");
1720 * Suspend the specified thread and load stack and register information for
1721 * use by thread_scanAll. If the supplied thread is the calling thread,
1722 * stack and register information will be loaded but the thread will not
1723 * be suspended. If the suspend operation fails and the thread is not
1724 * running then it will be removed from the global thread list, otherwise
1725 * an exception will be thrown.
1728 * t = The thread to suspend.
1731 * ThreadError if the suspend operation fails for a running thread.
1733 * Whether the thread is now suspended (true) or terminated (false).
1735 private extern (D
) bool suspend( Thread t
) nothrow @nogc
1737 Duration waittime
= dur
!"usecs"(10);
1744 else if (t
.m_isInCriticalRegion
)
1746 Thread
.criticalRegionLock
.unlock_nothrow();
1747 Thread
.sleep(waittime
);
1748 if (waittime
< dur
!"msecs"(10)) waittime
*= 2;
1749 Thread
.criticalRegionLock
.lock_nothrow();
1755 if ( t
.m_addr
!= GetCurrentThreadId() && SuspendThread( t
.m_hndl
) == 0xFFFFFFFF )
1762 onThreadError( "Unable to suspend thread" );
1765 CONTEXT context
= void;
1766 context
.ContextFlags
= CONTEXT_INTEGER | CONTEXT_CONTROL
;
1768 if ( !GetThreadContext( t
.m_hndl
, &context
) )
1769 onThreadError( "Unable to load thread context" );
1773 t
.m_curr
.tstack
= cast(void*) context
.Esp
;
1774 // eax,ebx,ecx,edx,edi,esi,ebp,esp
1775 t
.m_reg
[0] = context
.Eax
;
1776 t
.m_reg
[1] = context
.Ebx
;
1777 t
.m_reg
[2] = context
.Ecx
;
1778 t
.m_reg
[3] = context
.Edx
;
1779 t
.m_reg
[4] = context
.Edi
;
1780 t
.m_reg
[5] = context
.Esi
;
1781 t
.m_reg
[6] = context
.Ebp
;
1782 t
.m_reg
[7] = context
.Esp
;
1784 else version (X86_64
)
1787 t
.m_curr
.tstack
= cast(void*) context
.Rsp
;
1788 // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp
1789 t
.m_reg
[0] = context
.Rax
;
1790 t
.m_reg
[1] = context
.Rbx
;
1791 t
.m_reg
[2] = context
.Rcx
;
1792 t
.m_reg
[3] = context
.Rdx
;
1793 t
.m_reg
[4] = context
.Rdi
;
1794 t
.m_reg
[5] = context
.Rsi
;
1795 t
.m_reg
[6] = context
.Rbp
;
1796 t
.m_reg
[7] = context
.Rsp
;
1797 // r8,r9,r10,r11,r12,r13,r14,r15
1798 t
.m_reg
[8] = context
.R8
;
1799 t
.m_reg
[9] = context
.R9
;
1800 t
.m_reg
[10] = context
.R10
;
1801 t
.m_reg
[11] = context
.R11
;
1802 t
.m_reg
[12] = context
.R12
;
1803 t
.m_reg
[13] = context
.R13
;
1804 t
.m_reg
[14] = context
.R14
;
1805 t
.m_reg
[15] = context
.R15
;
1809 static assert(false, "Architecture not supported." );
1812 else version (Darwin
)
1814 if ( t
.m_addr
!= pthread_self() && thread_suspend( t
.m_tmach
) != KERN_SUCCESS
)
1821 onThreadError( "Unable to suspend thread" );
1826 x86_thread_state32_t state
= void;
1827 mach_msg_type_number_t count
= x86_THREAD_STATE32_COUNT
;
1829 if ( thread_get_state( t
.m_tmach
, x86_THREAD_STATE32
, &state
, &count
) != KERN_SUCCESS
)
1830 onThreadError( "Unable to load thread state" );
1832 t
.m_curr
.tstack
= cast(void*) state
.esp
;
1833 // eax,ebx,ecx,edx,edi,esi,ebp,esp
1834 t
.m_reg
[0] = state
.eax
;
1835 t
.m_reg
[1] = state
.ebx
;
1836 t
.m_reg
[2] = state
.ecx
;
1837 t
.m_reg
[3] = state
.edx
;
1838 t
.m_reg
[4] = state
.edi
;
1839 t
.m_reg
[5] = state
.esi
;
1840 t
.m_reg
[6] = state
.ebp
;
1841 t
.m_reg
[7] = state
.esp
;
1843 else version (X86_64
)
1845 x86_thread_state64_t state
= void;
1846 mach_msg_type_number_t count
= x86_THREAD_STATE64_COUNT
;
1848 if ( thread_get_state( t
.m_tmach
, x86_THREAD_STATE64
, &state
, &count
) != KERN_SUCCESS
)
1849 onThreadError( "Unable to load thread state" );
1851 t
.m_curr
.tstack
= cast(void*) state
.rsp
;
1852 // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp
1853 t
.m_reg
[0] = state
.rax
;
1854 t
.m_reg
[1] = state
.rbx
;
1855 t
.m_reg
[2] = state
.rcx
;
1856 t
.m_reg
[3] = state
.rdx
;
1857 t
.m_reg
[4] = state
.rdi
;
1858 t
.m_reg
[5] = state
.rsi
;
1859 t
.m_reg
[6] = state
.rbp
;
1860 t
.m_reg
[7] = state
.rsp
;
1861 // r8,r9,r10,r11,r12,r13,r14,r15
1862 t
.m_reg
[8] = state
.r8
;
1863 t
.m_reg
[9] = state
.r9
;
1864 t
.m_reg
[10] = state
.r10
;
1865 t
.m_reg
[11] = state
.r11
;
1866 t
.m_reg
[12] = state
.r12
;
1867 t
.m_reg
[13] = state
.r13
;
1868 t
.m_reg
[14] = state
.r14
;
1869 t
.m_reg
[15] = state
.r15
;
1871 else version (AArch64
)
1873 arm_thread_state64_t state
= void;
1874 mach_msg_type_number_t count
= ARM_THREAD_STATE64_COUNT
;
1876 if (thread_get_state(t
.m_tmach
, ARM_THREAD_STATE64
, &state
, &count
) != KERN_SUCCESS
)
1877 onThreadError("Unable to load thread state");
1878 // TODO: ThreadException here recurses forever! Does it
1879 //still using onThreadError?
1880 //printf("state count %d (expect %d)\n", count ,ARM_THREAD_STATE64_COUNT);
1882 t
.m_curr
.tstack
= cast(void*) state
.sp
;
1884 t
.m_reg
[0..29] = state
.x
; // x0-x28
1885 t
.m_reg
[29] = state
.fp
; // x29
1886 t
.m_reg
[30] = state
.lr
; // x30
1887 t
.m_reg
[31] = state
.sp
; // x31
1888 t
.m_reg
[32] = state
.pc
;
1892 arm_thread_state32_t state
= void;
1893 mach_msg_type_number_t count
= ARM_THREAD_STATE32_COUNT
;
1895 // Thought this would be ARM_THREAD_STATE32, but that fails.
1897 if (thread_get_state(t
.m_tmach
, ARM_THREAD_STATE
, &state
, &count
) != KERN_SUCCESS
)
1898 onThreadError("Unable to load thread state");
1899 // TODO: in past, ThreadException here recurses forever! Does it
1900 //still using onThreadError?
1901 //printf("state count %d (expect %d)\n", count ,ARM_THREAD_STATE32_COUNT);
1903 t
.m_curr
.tstack
= cast(void*) state
.sp
;
1905 t
.m_reg
[0..13] = state
.r
; // r0 - r13
1906 t
.m_reg
[13] = state
.sp
;
1907 t
.m_reg
[14] = state
.lr
;
1908 t
.m_reg
[15] = state
.pc
;
1912 ppc_thread_state_t state
= void;
1913 mach_msg_type_number_t count
= PPC_THREAD_STATE_COUNT
;
1915 if (thread_get_state(t
.m_tmach
, PPC_THREAD_STATE
, &state
, &count
) != KERN_SUCCESS
)
1916 onThreadError("Unable to load thread state");
1918 t
.m_curr
.tstack
= cast(void*) state
.r
[1];
1919 t
.m_reg
[] = state
.r
[];
1921 else version (PPC64
)
1923 ppc_thread_state64_t state
= void;
1924 mach_msg_type_number_t count
= PPC_THREAD_STATE64_COUNT
;
1926 if (thread_get_state(t
.m_tmach
, PPC_THREAD_STATE64
, &state
, &count
) != KERN_SUCCESS
)
1927 onThreadError("Unable to load thread state");
1929 t
.m_curr
.tstack
= cast(void*) state
.r
[1];
1930 t
.m_reg
[] = state
.r
[];
1934 static assert(false, "Architecture not supported." );
1937 else version (Posix
)
1939 if ( t
.m_addr
!= pthread_self() )
1941 if ( pthread_kill( t
.m_addr
, suspendSignalNumber
) != 0 )
1948 onThreadError( "Unable to suspend thread" );
1951 else if ( !t
.m_lock
)
1953 t
.m_curr
.tstack
= getStackTop();
1960 * Suspend all threads but the calling thread for "stop the world" garbage
1961 * collection runs. This function may be called multiple times, and must
1962 * be followed by a matching number of calls to thread_resumeAll before
1963 * processing is resumed.
1966 * ThreadError if the suspend operation fails for a running thread.
1968 extern (C
) void thread_suspendAll() nothrow
1970 // NOTE: We've got an odd chicken & egg problem here, because while the GC
1971 // is required to call thread_init before calling any other thread
1972 // routines, thread_init may allocate memory which could in turn
1973 // trigger a collection. Thus, thread_suspendAll, thread_scanAll,
1974 // and thread_resumeAll must be callable before thread_init
1975 // completes, with the assumption that no other GC memory has yet
1976 // been allocated by the system, and thus there is no risk of losing
1977 // data if the global thread list is empty. The check of
1978 // Thread.sm_tbeg below is done to ensure thread_init has completed,
1979 // and therefore that calling Thread.getThis will not result in an
1980 // error. For the short time when Thread.sm_tbeg is null, there is
1981 // no reason not to simply call the multithreaded code below, with
1982 // the expectation that the foreach loop will never be entered.
1983 if ( !multiThreadedFlag
&& Thread
.sm_tbeg
)
1985 if ( ++suspendDepth
== 1 )
1986 suspend( Thread
.getThis() );
1991 Thread
.slock
.lock_nothrow();
1993 if ( ++suspendDepth
> 1 )
1996 Thread
.criticalRegionLock
.lock_nothrow();
1997 scope (exit
) Thread
.criticalRegionLock
.unlock_nothrow();
2000 Thread t
= ThreadBase
.sm_tbeg
.toThread
;
2003 auto tn
= t
.next
.toThread
;
2006 if (t
is ThreadBase
.getThis())
2007 suspendedSelf
= true;
2015 else version (Posix
)
2017 // Subtract own thread if we called suspend() on ourselves.
2018 // For example, suspendedSelf would be false if the current
2019 // thread ran thread_detachThis().
2023 // wait for semaphore notifications
2026 while (sem_wait(&suspendCount
) != 0)
2029 onThreadError("Unable to wait for semaphore");
2038 * Resume the specified thread and unload stack and register information.
2039 * If the supplied thread is the calling thread, stack and register
2040 * information will be unloaded but the thread will not be resumed. If
2041 * the resume operation fails and the thread is not running then it will
2042 * be removed from the global thread list, otherwise an exception will be
2046 * t = The thread to resume.
2049 * ThreadError if the resume fails for a running thread.
2051 private extern (D
) void resume(ThreadBase _t
) nothrow @nogc
2053 Thread t
= _t
.toThread
;
2057 if ( t
.m_addr
!= GetCurrentThreadId() && ResumeThread( t
.m_hndl
) == 0xFFFFFFFF )
2064 onThreadError( "Unable to resume thread" );
2068 t
.m_curr
.tstack
= t
.m_curr
.bstack
;
2069 t
.m_reg
[0 .. $] = 0;
2071 else version (Darwin
)
2073 if ( t
.m_addr
!= pthread_self() && thread_resume( t
.m_tmach
) != KERN_SUCCESS
)
2080 onThreadError( "Unable to resume thread" );
2084 t
.m_curr
.tstack
= t
.m_curr
.bstack
;
2085 t
.m_reg
[0 .. $] = 0;
2087 else version (Posix
)
2089 if ( t
.m_addr
!= pthread_self() )
2091 if ( pthread_kill( t
.m_addr
, resumeSignalNumber
) != 0 )
2098 onThreadError( "Unable to resume thread" );
2101 else if ( !t
.m_lock
)
2103 t
.m_curr
.tstack
= t
.m_curr
.bstack
;
2110 * Initializes the thread module. This function must be called by the
2111 * garbage collector on startup and before any other thread routines
2114 extern (C
) void thread_init() @nogc nothrow
2116 // NOTE: If thread_init itself performs any allocations then the thread
2117 // routines reserved for garbage collector use may be called while
2118 // thread_init is being processed. However, since no memory should
2119 // exist to be scanned at this point, it is sufficient for these
2120 // functions to detect the condition and return immediately.
2122 initLowlevelThreads();
2127 // thread id different in forked child process
2128 static extern(C
) void initChildAfterFork()
2130 auto thisThread
= Thread
.getThis();
2133 // It is possible that runtime was not properly initialized in the current process or thread -
2134 // it may happen after `fork` call when using a dynamically loaded shared library written in D from a multithreaded non-D program.
2135 // In such case getThis will return null.
2138 thisThread
.m_addr
= pthread_self();
2139 assert( thisThread
.m_addr
!= thisThread
.m_addr
.init
);
2140 thisThread
.m_tmach
= pthread_mach_thread_np( thisThread
.m_addr
);
2141 assert( thisThread
.m_tmach
!= thisThread
.m_tmach
.init
);
2143 pthread_atfork(null, null, &initChildAfterFork
);
2145 else version (Posix
)
2149 // OpenBSD does not support SIGRTMIN or SIGRTMAX
2150 // Use SIGUSR1 for SIGRTMIN, SIGUSR2 for SIGRTMIN + 1
2151 // And use 32 for SIGRTMAX (32 is the max signal number on OpenBSD)
2152 enum SIGRTMIN
= SIGUSR1
;
2156 if ( suspendSignalNumber
== 0 )
2158 suspendSignalNumber
= SIGRTMIN
;
2161 if ( resumeSignalNumber
== 0 )
2163 resumeSignalNumber
= SIGRTMIN
+ 1;
2164 assert(resumeSignalNumber
<= SIGRTMAX
);
2167 sigaction_t suspend
= void;
2168 sigaction_t resume
= void;
2170 // This is a quick way to zero-initialize the structs without using
2171 // memset or creating a link dependency on their static initializer.
2172 (cast(byte*) &suspend
)[0 .. sigaction_t
.sizeof
] = 0;
2173 (cast(byte*) &resume
)[0 .. sigaction_t
.sizeof
] = 0;
2175 // NOTE: SA_RESTART indicates that system calls should restart if they
2176 // are interrupted by a signal, but this is not available on all
2177 // Posix systems, even those that support multithreading.
2178 static if ( __traits( compiles
, SA_RESTART
) )
2179 suspend
.sa_flags
= SA_RESTART
;
2181 suspend
.sa_handler
= &thread_suspendHandler
;
2182 // NOTE: We want to ignore all signals while in this handler, so fill
2183 // sa_mask to indicate this.
2184 status
= sigfillset( &suspend
.sa_mask
);
2185 assert( status
== 0 );
2187 // NOTE: Since resumeSignalNumber should only be issued for threads within the
2188 // suspend handler, we don't want this signal to trigger a
2190 resume
.sa_flags
= 0;
2191 resume
.sa_handler
= &thread_resumeHandler
;
2192 // NOTE: We want to ignore all signals while in this handler, so fill
2193 // sa_mask to indicate this.
2194 status
= sigfillset( &resume
.sa_mask
);
2195 assert( status
== 0 );
2197 status
= sigaction( suspendSignalNumber
, &suspend
, null );
2198 assert( status
== 0 );
2200 status
= sigaction( resumeSignalNumber
, &resume
, null );
2201 assert( status
== 0 );
2203 status
= sem_init( &suspendCount
, 0, 0 );
2204 assert( status
== 0 );
2206 _mainThreadStore
[] = __traits(initSymbol
, Thread
)[];
2207 Thread
.sm_main
= attachThread((cast(Thread
)_mainThreadStore
.ptr
).__ctor());
2210 private alias MainThreadStore
= void[__traits(classInstanceSize
, Thread
)];
2211 package __gshared
align(__traits(classInstanceAlignment
, Thread
)) MainThreadStore _mainThreadStore
;
2214 * Terminates the thread module. No other thread routine may be called
2217 extern (C
) void thread_term() @nogc nothrow
2219 thread_term_tpl
!(Thread
)(_mainThreadStore
);
2223 ///////////////////////////////////////////////////////////////////////////////
2224 // Thread Entry Point and Signal Handlers
2225 ///////////////////////////////////////////////////////////////////////////////
2233 // Entry point for Windows threads
2235 extern (Windows
) uint thread_entryPoint( void* arg
) nothrow
2237 Thread obj
= cast(Thread
) arg
;
2240 obj
.initDataStorage();
2242 Thread
.setThis(obj
);
2247 obj
.destroyDataStorage();
2249 Thread
.add(&obj
.m_main
);
2251 // NOTE: No GC allocations may occur until the stack pointers have
2252 // been set and Thread.getThis returns a valid reference to
2253 // this thread object (this latter condition is not strictly
2254 // necessary on Windows but it should be followed for the
2255 // sake of consistency).
2257 // TODO: Consider putting an auto exception object here (using
2258 // alloca) forOutOfMemoryError plus something to track
2259 // whether an exception is in-flight?
2261 void append( Throwable t
)
2263 obj
.m_unhandled
= Throwable
.chainTogether(obj
.m_unhandled
, t
);
2266 version (D_InlineAsm_X86
)
2268 asm nothrow @nogc { fninit; }
2278 catch ( Throwable t
)
2284 catch ( Throwable t
)
2292 HANDLE
GetCurrentThreadHandle() nothrow @nogc
2294 const uint DUPLICATE_SAME_ACCESS
= 0x00000002;
2296 HANDLE curr
= GetCurrentThread(),
2297 proc
= GetCurrentProcess(),
2300 DuplicateHandle( proc
, curr
, proc
, &hndl
, 0, TRUE
, DUPLICATE_SAME_ACCESS
);
2305 else version (Posix
)
2309 import core
.stdc
.errno
;
2310 import core
.sys
.posix
.semaphore
;
2311 import core
.sys
.posix
.stdlib
; // for malloc, valloc, free, atexit
2312 import core
.sys
.posix
.pthread
;
2313 import core
.sys
.posix
.signal
;
2314 import core
.sys
.posix
.time
;
2318 import core
.sys
.darwin
.mach
.thread_act
;
2319 import core
.sys
.darwin
.pthread
: pthread_mach_thread_np
;
2323 // Entry point for POSIX threads
2325 extern (C
) void* thread_entryPoint( void* arg
) nothrow
2329 Thread obj
= cast(Thread
)(cast(void**)arg
)[0];
2330 auto loadedLibraries
= (cast(void**)arg
)[1];
2335 Thread obj
= cast(Thread
)arg
;
2339 // loadedLibraries need to be inherited from parent thread
2340 // before initilizing GC for TLS (rt_tlsgc_init)
2343 externDFunc
!("gcc.sections.inheritLoadedLibraries",
2344 void function(void*) @nogc nothrow)(loadedLibraries
);
2346 else version (Shared
)
2348 externDFunc
!("rt.sections_elf_shared.inheritLoadedLibraries",
2349 void function(void*) @nogc nothrow)(loadedLibraries
);
2352 obj
.initDataStorage();
2354 atomicStore
!(MemoryOrder
.raw
)(obj
.m_isRunning
, true);
2355 Thread
.setThis(obj
); // allocates lazy TLS (see Issue 11981)
2356 Thread
.add(obj
); // can only receive signals from here on
2360 atomicStore
!(MemoryOrder
.raw
)(obj
.m_isRunning
, false);
2361 obj
.destroyDataStorage();
2363 Thread
.add(&obj
.m_main
);
2365 static extern (C
) void thread_cleanupHandler( void* arg
) nothrow @nogc
2367 Thread obj
= cast(Thread
) arg
;
2370 // NOTE: If the thread terminated abnormally, just set it as
2371 // not running and let thread_suspendAll remove it from
2372 // the thread list. This is safer and is consistent
2373 // with the Windows thread code.
2374 atomicStore
!(MemoryOrder
.raw
)(obj
.m_isRunning
,false);
2377 // NOTE: Using void to skip the initialization here relies on
2378 // knowledge of how pthread_cleanup is implemented. It may
2379 // not be appropriate for all platforms. However, it does
2380 // avoid the need to link the pthread module. If any
2381 // implementation actually requires default initialization
2382 // then pthread_cleanup should be restructured to maintain
2383 // the current lack of a link dependency.
2384 static if ( __traits( compiles
, pthread_cleanup
) )
2386 pthread_cleanup cleanup
= void;
2387 cleanup
.push( &thread_cleanupHandler
, cast(void*) obj
);
2389 else static if ( __traits( compiles
, pthread_cleanup_push
) )
2391 pthread_cleanup_push( &thread_cleanupHandler
, cast(void*) obj
);
2395 static assert( false, "Platform not supported." );
2398 // NOTE: No GC allocations may occur until the stack pointers have
2399 // been set and Thread.getThis returns a valid reference to
2400 // this thread object (this latter condition is not strictly
2401 // necessary on Windows but it should be followed for the
2402 // sake of consistency).
2404 // TODO: Consider putting an auto exception object here (using
2405 // alloca) forOutOfMemoryError plus something to track
2406 // whether an exception is in-flight?
2408 void append( Throwable t
)
2410 obj
.m_unhandled
= Throwable
.chainTogether(obj
.m_unhandled
, t
);
2419 catch ( Throwable t
)
2426 externDFunc
!("gcc.sections.cleanupLoadedLibraries",
2427 void function() @nogc nothrow)();
2429 else version (Shared
)
2431 externDFunc
!("rt.sections_elf_shared.cleanupLoadedLibraries",
2432 void function() @nogc nothrow)();
2435 catch ( Throwable t
)
2440 // NOTE: Normal cleanup is handled by scope(exit).
2442 static if ( __traits( compiles
, pthread_cleanup
) )
2446 else static if ( __traits( compiles
, pthread_cleanup_push
) )
2448 pthread_cleanup_pop( 0 );
2456 // Used to track the number of suspended threads
2458 __gshared sem_t suspendCount
;
2461 extern (C
) void thread_suspendHandler( int sig
) nothrow
2464 assert( sig
== suspendSignalNumber
);
2468 void op(void* sp
) nothrow
2470 // NOTE: Since registers are being pushed and popped from the
2471 // stack, any other stack data used by this function should
2472 // be gone before the stack cleanup code is called below.
2473 Thread obj
= Thread
.getThis();
2474 assert(obj
!is null);
2478 obj
.m_curr
.tstack
= getStackTop();
2481 sigset_t sigres
= void;
2484 status
= sigfillset( &sigres
);
2485 assert( status
== 0 );
2487 status
= sigdelset( &sigres
, resumeSignalNumber
);
2488 assert( status
== 0 );
2490 status
= sem_post( &suspendCount
);
2491 assert( status
== 0 );
2493 sigsuspend( &sigres
);
2497 obj
.m_curr
.tstack
= obj
.m_curr
.bstack
;
2500 callWithStackShell(&op
);
2504 extern (C
) void thread_resumeHandler( int sig
) nothrow
2507 assert( sig
== resumeSignalNumber
);
2517 // NOTE: This is the only place threading versions are checked. If a new
2518 // version is added, the module code will need to be searched for
2519 // places where version-specific code may be required. This can be
2520 // easily accomlished by searching for 'Windows' or 'Posix'.
2521 static assert( false, "Unknown threading implementation." );
2525 // exposed by compiler runtime
2527 extern (C
) void rt_moduleTlsCtor();
2528 extern (C
) void rt_moduleTlsDtor();
2531 // regression test for Issue 13416
2532 version (FreeBSD
) unittest
2536 pthread_attr_t attr
;
2537 pthread_attr_init(&attr
);
2538 auto thr
= pthread_self();
2539 foreach (i
; 0 .. 50)
2540 pthread_attr_get_np(thr
, &attr
);
2541 pthread_attr_destroy(&attr
);
2544 auto thr
= new Thread(&loop).start();
2545 foreach (i
; 0 .. 50)
2547 thread_suspendAll();
2553 version (DragonFlyBSD
) unittest
2557 pthread_attr_t attr
;
2558 pthread_attr_init(&attr
);
2559 auto thr
= pthread_self();
2560 foreach (i
; 0 .. 50)
2561 pthread_attr_get_np(thr
, &attr
);
2562 pthread_attr_destroy(&attr
);
2565 auto thr
= new Thread(&loop).start();
2566 foreach (i
; 0 .. 50)
2568 thread_suspendAll();
2575 ///////////////////////////////////////////////////////////////////////////////
2576 // lowlovel threading support
2577 ///////////////////////////////////////////////////////////////////////////////
2582 // If the runtime is dynamically loaded as a DLL, there is a problem with
2583 // threads still running when the DLL is supposed to be unloaded:
2585 // - with the VC runtime starting with VS2015 (i.e. using the Universal CRT)
2586 // a thread created with _beginthreadex increments the DLL reference count
2587 // and decrements it when done, so that the DLL is no longer unloaded unless
2588 // all the threads have terminated. With the DLL reference count held up
2589 // by a thread that is only stopped by a signal from a static destructor or
2590 // the termination of the runtime will cause the DLL to never be unloaded.
2592 // - with the DigitalMars runtime and VC runtime up to VS2013, the thread
2593 // continues to run, but crashes once the DLL is unloaded from memory as
2594 // the code memory is no longer accessible. Stopping the threads is not possible
2595 // from within the runtime termination as it is invoked from
2596 // DllMain(DLL_PROCESS_DETACH) holding a lock that prevents threads from
2599 // Solution: start a watchdog thread that keeps the DLL reference count above 0 and
2600 // checks it periodically. If it is equal to 1 (plus the number of started threads), no
2601 // external references to the DLL exist anymore, threads can be stopped
2602 // and runtime termination and DLL unload can be invoked via FreeLibraryAndExitThread.
2603 // Note: runtime termination is then performed by a different thread than at startup.
2605 // Note: if the DLL is never unloaded, process termination kills all threads
2606 // and signals their handles before unconditionally calling DllMain(DLL_PROCESS_DETACH).
2608 import core
.sys
.windows
.winbase
: FreeLibraryAndExitThread
, GetModuleHandleExW
,
2609 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
;
2610 import core
.sys
.windows
.windef
: HMODULE
;
2611 import core
.sys
.windows
.dll
: dll_getRefCount
;
2613 version (CRuntime_Microsoft
)
2614 extern(C
) extern __gshared
ubyte msvcUsesUCRT
; // from rt/msvc.d
2616 /// set during termination of a DLL on Windows, i.e. while executing DllMain(DLL_PROCESS_DETACH)
2617 public __gshared
bool thread_DLLProcessDetaching
;
2619 __gshared HMODULE ll_dllModule
;
2620 __gshared ThreadID ll_dllMonitorThread
;
2622 int ll_countLowLevelThreadsWithDLLUnloadCallback() nothrow
2624 lowlevelLock
.lock_nothrow();
2625 scope(exit
) lowlevelLock
.unlock_nothrow();
2628 foreach (i
; 0 .. ll_nThreads
)
2629 if (ll_pThreads
[i
].cbDllUnload
)
2634 bool ll_dllHasExternalReferences() nothrow
2636 version (CRuntime_DigitalMars
)
2637 enum internalReferences
= 1; // only the watchdog thread
2639 int internalReferences
= msvcUsesUCRT ?
1 + ll_countLowLevelThreadsWithDLLUnloadCallback() : 1;
2641 int refcnt
= dll_getRefCount(ll_dllModule
);
2642 return refcnt
> internalReferences
;
2645 private void monitorDLLRefCnt() nothrow
2647 // this thread keeps the DLL alive until all external references are gone
2648 while (ll_dllHasExternalReferences())
2650 Thread
.sleep(100.msecs
);
2653 // the current thread will be terminated below
2654 ll_removeThread(GetCurrentThreadId());
2659 void delegate() nothrow cbDllUnload
;
2661 lowlevelLock
.lock_nothrow();
2662 scope(exit
) lowlevelLock
.unlock_nothrow();
2664 foreach (i
; 0 .. ll_nThreads
)
2665 if (ll_pThreads
[i
].cbDllUnload
)
2667 cbDllUnload
= ll_pThreads
[i
].cbDllUnload
;
2668 tid
= ll_pThreads
[0].tid
;
2674 assert(!findLowLevelThread(tid
));
2677 FreeLibraryAndExitThread(ll_dllModule
, 0);
2680 int ll_getDLLRefCount() nothrow @nogc
2682 if (!ll_dllModule
&&
2683 !GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
2684 cast(const(wchar)*) &ll_getDLLRefCount
, &ll_dllModule
))
2686 return dll_getRefCount(ll_dllModule
);
2689 bool ll_startDLLUnloadThread() nothrow @nogc
2691 int refcnt
= ll_getDLLRefCount();
2693 return false; // not a dynamically loaded DLL
2695 if (ll_dllMonitorThread
!is ThreadID
.init
)
2698 // if a thread is created from a DLL, the MS runtime (starting with VC2015) increments the DLL reference count
2699 // to avoid the DLL being unloaded while the thread is still running. Mimick this behavior here for all
2700 // runtimes not doing this
2701 version (CRuntime_DigitalMars
)
2702 enum needRef
= true;
2704 bool needRef
= !msvcUsesUCRT
;
2709 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
, cast(const(wchar)*) &ll_getDLLRefCount
, &hmod
);
2712 ll_dllMonitorThread
= createLowLevelThread(() { monitorDLLRefCnt(); });
2713 return ll_dllMonitorThread
!= ThreadID
.init
;
2718 * Create a thread not under control of the runtime, i.e. TLS module constructors are
2719 * not run and the GC does not suspend it during a collection.
2722 * dg = delegate to execute in the created thread.
2723 * stacksize = size of the stack of the created thread. The default of 0 will select the
2724 * platform-specific default size.
2725 * cbDllUnload = Windows only: if running in a dynamically loaded DLL, this delegate will be called
2726 * if the DLL is supposed to be unloaded, but the thread is still running.
2727 * The thread must be terminated via `joinLowLevelThread` by the callback.
2729 * Returns: the platform specific thread ID of the new thread. If an error occurs, `ThreadID.init`
2732 ThreadID
createLowLevelThread(void delegate() nothrow dg
, uint stacksize
= 0,
2733 void delegate() nothrow cbDllUnload
= null) nothrow @nogc
2735 void delegate() nothrow* context
= cast(void delegate() nothrow*)malloc(dg
.sizeof
);
2741 // the thread won't start until after the DLL is unloaded
2742 if (thread_DLLProcessDetaching
)
2743 return ThreadID
.init
;
2745 static extern (Windows
) uint thread_lowlevelEntry(void* ctx
) nothrow
2747 auto dg
= *cast(void delegate() nothrow*)ctx
;
2751 ll_removeThread(GetCurrentThreadId());
2755 // see Thread.start() for why thread is created in suspended state
2756 HANDLE hThread
= cast(HANDLE
) _beginthreadex(null, stacksize
, &thread_lowlevelEntry
,
2757 context
, CREATE_SUSPENDED
, &tid
);
2759 return ThreadID
.init
;
2762 lowlevelLock
.lock_nothrow();
2763 scope(exit
) lowlevelLock
.unlock_nothrow();
2766 ll_pThreads
= cast(ll_ThreadData
*)realloc(ll_pThreads
, ll_ThreadData
.sizeof
* ll_nThreads
);
2770 ll_pThreads
[ll_nThreads
- 1].tid
= tid
;
2771 ll_pThreads
[ll_nThreads
- 1].cbDllUnload
= cbDllUnload
;
2772 if (ResumeThread(hThread
) == -1)
2773 onThreadError("Error resuming thread");
2774 CloseHandle(hThread
);
2777 ll_startDLLUnloadThread();
2779 else version (Posix
)
2781 static extern (C
) void* thread_lowlevelEntry(void* ctx
) nothrow
2783 auto dg
= *cast(void delegate() nothrow*)ctx
;
2787 ll_removeThread(pthread_self());
2791 size_t stksz
= adjustStackSize(stacksize
);
2793 pthread_attr_t attr
;
2796 if ((rc
= pthread_attr_init(&attr
)) != 0)
2797 return ThreadID
.init
;
2798 if (stksz
&& (rc
= pthread_attr_setstacksize(&attr
, stksz
)) != 0)
2799 return ThreadID
.init
;
2800 if ((rc
= pthread_create(&tid
, &attr
, &thread_lowlevelEntry
, context
)) != 0)
2801 return ThreadID
.init
;
2802 if ((rc
= pthread_attr_destroy(&attr
)) != 0)
2803 return ThreadID
.init
;
2805 ll_pThreads
[ll_nThreads
- 1].tid
= tid
;
2811 * Wait for a thread created with `createLowLevelThread` to terminate.
2813 * Note: In a Windows DLL, if this function is called via DllMain with
2814 * argument DLL_PROCESS_DETACH, the thread is terminated forcefully
2815 * without proper cleanup as a deadlock would happen otherwise.
2818 * tid = the thread ID returned by `createLowLevelThread`.
2820 void joinLowLevelThread(ThreadID tid
) nothrow @nogc
2824 HANDLE handle
= OpenThreadHandle(tid
);
2828 if (thread_DLLProcessDetaching
)
2830 // When being called from DllMain/DLL_DETACH_PROCESS, threads cannot stop
2831 // due to the loader lock being held by the current thread.
2832 // On the other hand, the thread must not continue to run as it will crash
2833 // if the DLL is unloaded. The best guess is to terminate it immediately.
2834 TerminateThread(handle
, 1);
2835 WaitForSingleObject(handle
, 10); // give it some time to terminate, but don't wait indefinitely
2838 WaitForSingleObject(handle
, INFINITE
);
2839 CloseHandle(handle
);
2841 else version (Posix
)
2843 if (pthread_join(tid
, null) != 0)
2844 onThreadError("Unable to join thread");
2848 nothrow @nogc unittest
2850 struct TaskWithContect
2858 TaskWithContect task
;
2861 for (int i
= 0; i
< tids
.length
; i
++)
2863 tids
[i
] = createLowLevelThread(&task
.run
);
2864 assert(tids
[i
] != ThreadID
.init
);
2867 for (int i
= 0; i
< tids
.length
; i
++)
2868 joinLowLevelThread(tids
[i
]);
2870 assert(task
.n
== tids
.length
);
2874 private size_t
adjustStackSize(size_t sz
) nothrow @nogc
2879 // stack size must be at least PTHREAD_STACK_MIN for most platforms.
2880 if (PTHREAD_STACK_MIN
> sz
)
2881 sz
= PTHREAD_STACK_MIN
;
2883 version (CRuntime_Glibc
)
2885 // On glibc, TLS uses the top of the stack, so add its size to the requested size
2888 sz
+= externDFunc
!("gcc.sections.elf.sizeOfTLS",
2889 size_t
function() @nogc nothrow)();
2893 sz
+= externDFunc
!("rt.sections_elf_shared.sizeOfTLS",
2894 size_t
function() @nogc nothrow)();
2898 // stack size must be a multiple of pageSize
2899 sz
= ((sz
+ pageSize
- 1) & ~(pageSize
- 1));