2 * The threadbase module provides OS-independent code
3 * for thread storage 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
.threadbase
;
18 import core
.thread
.context
;
19 import core
.thread
.types
;
21 import core
.sync
.mutex
;
22 import core
.stdc
.stdlib
: free
, realloc
;
26 import core
.internal
.traits
: externDFunc
;
28 // interface to rt.tlsgc
29 alias rt_tlsgc_init
= externDFunc
!("rt.tlsgc.init", void* function() nothrow @nogc);
30 alias rt_tlsgc_destroy
= externDFunc
!("rt.tlsgc.destroy", void function(void*) nothrow @nogc);
32 alias ScanDg
= void delegate(void* pstart
, void* pend
) nothrow;
34 externDFunc
!("rt.tlsgc.scan", void function(void*, scope ScanDg
) nothrow);
36 alias rt_tlsgc_processGCMarks
=
37 externDFunc
!("rt.tlsgc.processGCMarks", void function(void*, scope IsMarkedDg
) nothrow);
41 ///////////////////////////////////////////////////////////////////////////////
42 // Thread and Fiber Exceptions
43 ///////////////////////////////////////////////////////////////////////////////
47 * Base class for thread exceptions.
49 class ThreadException
: Exception
51 @nogc @safe pure nothrow this(string msg
, string file
= __FILE__
, size_t line
= __LINE__
, Throwable next
= null)
53 super(msg
, file
, line
, next
);
56 @nogc @safe pure nothrow this(string msg
, Throwable next
, string file
= __FILE__
, size_t line
= __LINE__
)
58 super(msg
, file
, line
, next
);
64 * Base class for thread errors to be used for function inside GC when allocations are unavailable.
66 class ThreadError
: Error
68 @nogc @safe pure nothrow this(string msg
, string file
= __FILE__
, size_t line
= __LINE__
, Throwable next
= null)
70 super(msg
, file
, line
, next
);
73 @nogc @safe pure nothrow this(string msg
, Throwable next
, string file
= __FILE__
, size_t line
= __LINE__
)
75 super(msg
, file
, line
, next
);
81 // Handling unaligned mutexes are not supported on all platforms, so we must
82 // ensure that the address of all shared data are appropriately aligned.
83 import core
.internal
.traits
: classInstanceAlignment
;
85 enum mutexAlign
= classInstanceAlignment
!Mutex
;
86 enum mutexClassInstanceSize
= __traits(classInstanceSize
, Mutex
);
88 alias swapContext
= externDFunc
!("core.thread.osthread.swapContext", void* function(void*) nothrow @nogc);
90 alias getStackBottom
= externDFunc
!("core.thread.osthread.getStackBottom", void* function() nothrow @nogc);
91 alias getStackTop
= externDFunc
!("core.thread.osthread.getStackTop", void* function() nothrow @nogc);
95 ///////////////////////////////////////////////////////////////////////////////
97 ///////////////////////////////////////////////////////////////////////////////
102 ///////////////////////////////////////////////////////////////////////////
104 ///////////////////////////////////////////////////////////////////////////
106 this(void function() fn
, size_t sz
= 0) @safe pure nothrow @nogc
113 this(void delegate() dg
, size_t sz
= 0) @safe pure nothrow @nogc
121 * Cleans up any remaining resources used by this object.
123 package bool destructBeforeDtor() nothrow @nogc
125 destroyDataStorageIfAvail();
127 bool no_context
= m_addr
== m_addr
.init
;
128 bool not_registered
= !next
&& !prev
&& (sm_tbeg
!is this);
130 return (no_context || not_registered
);
133 package void tlsGCdataInit() nothrow @nogc
135 m_tlsgcdata
= rt_tlsgc_init();
138 package void initDataStorage() nothrow
140 assert(m_curr
is &m_main
);
142 m_main
.bstack
= getStackBottom();
143 m_main
.tstack
= m_main
.bstack
;
147 package void destroyDataStorage() nothrow @nogc
149 rt_tlsgc_destroy(m_tlsgcdata
);
153 package void destroyDataStorageIfAvail() nothrow @nogc
156 destroyDataStorage();
160 ///////////////////////////////////////////////////////////////////////////
162 ///////////////////////////////////////////////////////////////////////////
166 * Waits for this thread to complete. If the thread terminated as the
167 * result of an unhandled exception, this exception will be rethrown.
170 * rethrow = Rethrow any unhandled exception which may have caused this
171 * thread to terminate.
174 * ThreadException if the operation fails.
175 * Any exception not handled by the joined thread.
178 * Any exception not handled by this thread if rethrow = false, null
181 abstract Throwable
join(bool rethrow
= true);
184 ///////////////////////////////////////////////////////////////////////////
185 // General Properties
186 ///////////////////////////////////////////////////////////////////////////
190 * Gets the OS identifier for this thread.
193 * If the thread hasn't been started yet, returns $(LREF ThreadID)$(D.init).
194 * Otherwise, returns the result of $(D GetCurrentThreadId) on Windows,
195 * and $(D pthread_self) on POSIX.
197 * The value is unique for the current process.
199 final @property ThreadID
id() @safe @nogc
209 * Gets the user-readable label for this thread.
212 * The name of this thread.
214 final @property string
name() @safe @nogc
224 * Sets the user-readable label for this thread.
227 * val = The new name of this thread.
229 final @property void name(string val
) @safe @nogc
239 * Gets the daemon status for this thread. While the runtime will wait for
240 * all normal threads to complete before tearing down the process, daemon
241 * threads are effectively ignored and thus will not prevent the process
242 * from terminating. In effect, daemon threads will be terminated
243 * automatically by the OS when the process exits.
246 * true if this is a daemon thread.
248 final @property bool isDaemon() @safe @nogc
258 * Sets the daemon status for this thread. While the runtime will wait for
259 * all normal threads to complete before tearing down the process, daemon
260 * threads are effectively ignored and thus will not prevent the process
261 * from terminating. In effect, daemon threads will be terminated
262 * automatically by the OS when the process exits.
265 * val = The new daemon status for this thread.
267 final @property void isDaemon(bool val
) @safe @nogc
276 * Tests whether this thread is the main thread, i.e. the thread
277 * that initialized the runtime
280 * true if the thread is the main thread
282 final @property bool isMainThread() nothrow @nogc
284 return this is sm_main
;
288 * Tests whether this thread is running.
291 * true if the thread is running, false if not.
293 @property bool isRunning() nothrow @nogc
295 if (m_addr
== m_addr
.init
)
302 ///////////////////////////////////////////////////////////////////////////
304 ///////////////////////////////////////////////////////////////////////////
307 * Provides a reference to the calling thread.
310 * The thread object representing the calling thread. The result of
311 * deleting this object is undefined. If the current thread is not
312 * attached to the runtime, a null reference is returned.
314 static ThreadBase
getThis() @safe nothrow @nogc
316 // NOTE: This function may not be called until thread_init has
317 // completed. See thread_suspendAll for more information
318 // on why this might occur.
319 version (GNU
) pragma(inline
, false);
325 * Provides a list of all threads currently being tracked by the system.
326 * Note that threads in the returned array might no longer run (see
327 * $(D ThreadBase.)$(LREF isRunning)).
330 * An array containing references to all threads currently being
331 * tracked by the system. The result of deleting any contained
332 * objects is undefined.
334 static ThreadBase
[] getAll()
336 static void resize(ref ThreadBase
[] buf
, size_t nlen
)
340 return getAllImpl
!resize();
345 * Operates on all threads currently being tracked by the system. The
346 * result of deleting any Thread object is undefined.
347 * Note that threads passed to the callback might no longer run (see
348 * $(D ThreadBase.)$(LREF isRunning)).
351 * dg = The supplied code as a delegate.
354 * Zero if all elemented are visited, nonzero if not.
356 static int opApply(scope int delegate(ref ThreadBase
) dg
)
358 static void resize(ref ThreadBase
[] buf
, size_t nlen
)
360 import core
.exception
: onOutOfMemoryError
;
362 auto newBuf
= cast(ThreadBase
*)realloc(buf
.ptr
, nlen
* size_t
.sizeof
);
363 if (newBuf
is null) onOutOfMemoryError();
364 buf
= newBuf
[0 .. nlen
];
366 auto buf
= getAllImpl
!resize
;
367 scope(exit
) if (buf
.ptr
) free(buf
.ptr
);
371 if (auto res
= dg(t
))
377 private static ThreadBase
[] getAllImpl(alias resize
)()
384 immutable len
= atomicLoad
!(MemoryOrder
.raw
)(*cast(shared)&sm_tlen
);
386 assert(buf
.length
== len
);
392 for (ThreadBase t
= sm_tbeg
; t
; t
= t
.next
)
400 ///////////////////////////////////////////////////////////////////////////
401 // Actions on Calling Thread
402 ///////////////////////////////////////////////////////////////////////////
405 * Forces a context switch to occur away from the calling thread.
407 private static void yield() @nogc nothrow
412 ///////////////////////////////////////////////////////////////////////////
413 // Stuff That Should Go Away
414 ///////////////////////////////////////////////////////////////////////////
418 // Initializes a thread object which has no associated executable function.
419 // This is used for the main thread initialized in thread_init().
421 package this(size_t sz
= 0) @safe pure nothrow @nogc
428 // Thread entry point. Invokes the function or delegate passed on
429 // construction (if any).
431 package final void run()
441 static ThreadBase sm_this
;
445 // Main process thread
447 __gshared ThreadBase sm_main
;
451 // Standard thread data
458 bool m_isInCriticalRegion
;
459 Throwable m_unhandled
;
461 ///////////////////////////////////////////////////////////////////////////
462 // Storage of Active Thread
463 ///////////////////////////////////////////////////////////////////////////
467 // Sets a thread-local reference to the current thread object.
469 package static void setThis(ThreadBase t
) nothrow @nogc
474 package(core
.thread
):
477 StackContext
* m_curr
;
479 private void* m_tlsgcdata
;
481 ///////////////////////////////////////////////////////////////////////////
482 // Thread Context and GC Scanning Support
483 ///////////////////////////////////////////////////////////////////////////
486 final void pushContext(StackContext
* c
) nothrow @nogc
493 m_curr
.ehContext
= swapContext(c
.ehContext
);
499 final void popContext() nothrow @nogc
502 assert(m_curr
&& m_curr
.within
);
506 StackContext
* c
= m_curr
;
508 c
.ehContext
= swapContext(m_curr
.ehContext
);
512 private final StackContext
* topContext() nothrow @nogc
519 package(core
.thread
):
520 ///////////////////////////////////////////////////////////////////////////
521 // GC Scanning Support
522 ///////////////////////////////////////////////////////////////////////////
525 // NOTE: The GC scanning process works like so:
527 // 1. Suspend all threads.
528 // 2. Scan the stacks of all suspended threads for roots.
529 // 3. Resume all threads.
531 // Step 1 and 3 require a list of all threads in the system, while
532 // step 2 requires a list of all thread stacks (each represented by
533 // a Context struct). Traditionally, there was one stack per thread
534 // and the Context structs were not necessary. However, Fibers have
535 // changed things so that each thread has its own 'main' stack plus
536 // an arbitrary number of nested stacks (normally referenced via
537 // m_curr). Also, there may be 'free-floating' stacks in the system,
538 // which are Fibers that are not currently executing on any specific
539 // thread but are still being processed and still contain valid
542 // To support all of this, the Context struct has been created to
543 // represent a stack range, and a global list of Context structs has
544 // been added to enable scanning of these stack ranges. The lifetime
545 // (and presence in the Context list) of a thread's 'main' stack will
546 // be equivalent to the thread's lifetime. So the Ccontext will be
547 // added to the list on thread entry, and removed from the list on
548 // thread exit (which is essentially the same as the presence of a
549 // Thread object in its own global list). The lifetime of a Fiber's
550 // context, however, will be tied to the lifetime of the Fiber object
551 // itself, and Fibers are expected to add/remove their Context struct
552 // on construction/deletion.
556 // All use of the global thread lists/array should synchronize on this lock.
558 // Careful as the GC acquires this lock after the GC lock to suspend all
559 // threads any GC usage with slock held can result in a deadlock through
560 // lock order inversion.
561 @property static Mutex
slock() nothrow @nogc
563 return cast(Mutex
)_slock
.ptr
;
566 @property static Mutex
criticalRegionLock() nothrow @nogc
568 return cast(Mutex
)_criticalRegionLock
.ptr
;
571 __gshared
align(mutexAlign
) void[mutexClassInstanceSize
] _slock
;
572 __gshared
align(mutexAlign
) void[mutexClassInstanceSize
] _criticalRegionLock
;
574 static void initLocks() @nogc
576 import core
.lifetime
: emplace
;
577 emplace
!Mutex(_slock
[]);
578 emplace
!Mutex(_criticalRegionLock
[]);
581 static void termLocks() @nogc
583 (cast(Mutex
)_slock
.ptr
).__dtor();
584 (cast(Mutex
)_criticalRegionLock
.ptr
).__dtor();
587 __gshared StackContext
* sm_cbeg
;
589 __gshared ThreadBase sm_tbeg
;
590 __gshared size_t sm_tlen
;
592 // can't use core.internal.util.array in public code
593 __gshared ThreadBase
* pAboutToStart
;
594 __gshared size_t nAboutToStart
;
597 // Used for ordering threads in the global thread list.
603 ///////////////////////////////////////////////////////////////////////////
604 // Global Context List Operations
605 ///////////////////////////////////////////////////////////////////////////
609 // Add a context to the global context list.
611 static void add(StackContext
* c
) nothrow @nogc
615 assert(!c
.next
&& !c
.prev
);
619 slock
.lock_nothrow();
620 scope(exit
) slock
.unlock_nothrow();
621 assert(!suspendDepth
); // must be 0 b/c it's only set with slock held
632 // Remove a context from the global context list.
634 // This assumes slock being acquired. This isn't done here to
635 // avoid double locking when called from remove(Thread)
636 static void remove(StackContext
* c
) nothrow @nogc
640 assert(c
.next || c
.prev
);
645 c
.prev
.next
= c
.next
;
647 c
.next
.prev
= c
.prev
;
650 // NOTE: Don't null out c.next or c.prev because opApply currently
651 // follows c.next after removing a node. This could be easily
652 // addressed by simply returning the next node from this
653 // function, however, a context should never be re-added to the
654 // list anyway and having next and prev be non-null is a good way
659 ///////////////////////////////////////////////////////////////////////////
660 // Global Thread List Operations
661 ///////////////////////////////////////////////////////////////////////////
665 // Add a thread to the global thread list.
667 static void add(ThreadBase t
, bool rmAboutToStart
= true) nothrow @nogc
671 assert(!t
.next
&& !t
.prev
);
675 slock
.lock_nothrow();
676 scope(exit
) slock
.unlock_nothrow();
677 assert(t
.isRunning
); // check this with slock to ensure pthread_create already returned
678 assert(!suspendDepth
); // must be 0 b/c it's only set with slock held
683 foreach (i
, thr
; pAboutToStart
[0 .. nAboutToStart
])
692 import core
.stdc
.string
: memmove
;
693 memmove(pAboutToStart
+ idx
, pAboutToStart
+ idx
+ 1, size_t
.sizeof
* (nAboutToStart
- idx
- 1));
695 cast(ThreadBase
*)realloc(pAboutToStart
, size_t
.sizeof
* --nAboutToStart
);
709 // Remove a thread from the global thread list.
711 static void remove(ThreadBase t
) nothrow @nogc
718 // Thread was already removed earlier, might happen b/c of thread_detachInstance
719 if (!t
.next
&& !t
.prev
&& (sm_tbeg
!is t
))
722 slock
.lock_nothrow();
724 // NOTE: When a thread is removed from the global thread list its
725 // main context is invalid and should be removed as well.
726 // It is possible that t.m_curr could reference more
727 // than just the main context if the thread exited abnormally
728 // (if it was terminated), but we must assume that the user
729 // retains a reference to them and that they may be re-used
730 // elsewhere. Therefore, it is the responsibility of any
731 // object that creates contexts to clean them up properly
732 // when it is done with them.
736 t
.prev
.next
= t
.next
;
738 t
.next
.prev
= t
.prev
;
741 t
.prev
= t
.next
= null;
744 // NOTE: Don't null out t.next or t.prev because opApply currently
745 // follows t.next after removing a node. This could be easily
746 // addressed by simply returning the next node from this
747 // function, however, a thread should never be re-added to the
748 // list anyway and having next and prev be non-null is a good way
750 slock
.unlock_nothrow();
755 ///////////////////////////////////////////////////////////////////////////////
756 // GC Support Routines
757 ///////////////////////////////////////////////////////////////////////////////
759 private alias attachThread
= externDFunc
!("core.thread.osthread.attachThread", ThreadBase
function(ThreadBase
) @nogc nothrow);
761 extern (C
) void _d_monitordelete_nogc(Object h
) @nogc;
764 * Terminates the thread module. No other thread routine may be called
767 package void thread_term_tpl(ThreadT
, MainThreadStore
)(ref MainThreadStore _mainThreadStore
) @nogc
769 assert(_mainThreadStore
.ptr
is cast(void*) ThreadBase
.sm_main
);
771 // destruct manually as object.destroy is not @nogc
772 (cast(ThreadT
) cast(void*) ThreadBase
.sm_main
).__dtor();
773 _d_monitordelete_nogc(ThreadBase
.sm_main
);
774 _mainThreadStore
[] = __traits(initSymbol
, ThreadT
)[];
775 ThreadBase
.sm_main
= null;
777 assert(ThreadBase
.sm_tbeg
&& ThreadBase
.sm_tlen
== 1);
778 assert(!ThreadBase
.nAboutToStart
);
779 if (ThreadBase
.pAboutToStart
) // in case realloc(p, 0) doesn't return null
781 free(ThreadBase
.pAboutToStart
);
782 ThreadBase
.pAboutToStart
= null;
784 ThreadBase
.termLocks();
785 termLowlevelThreads();
792 extern (C
) bool thread_isMainThread() nothrow @nogc
794 return ThreadBase
.getThis() is ThreadBase
.sm_main
;
799 * Registers the calling thread for use with the D Runtime. If this routine
800 * is called for a thread which is already registered, no action is performed.
802 * NOTE: This routine does not run thread-local static constructors when called.
803 * If full functionality as a D thread is desired, the following function
804 * must be called after thread_attachThis:
806 * extern (C) void rt_moduleTlsCtor();
808 package ThreadT
thread_attachThis_tpl(ThreadT
)()
810 if (auto t
= ThreadT
.getThis())
813 return cast(ThreadT
) attachThread(new ThreadT());
818 * Deregisters the calling thread from use with the runtime. If this routine
819 * is called for a thread which is not registered, the result is undefined.
821 * NOTE: This routine does not run thread-local static destructors when called.
822 * If full functionality as a D thread is desired, the following function
823 * must be called after thread_detachThis, particularly if the thread is
824 * being detached at some indeterminate time before program termination:
826 * $(D extern(C) void rt_moduleTlsDtor();)
828 extern (C
) void thread_detachThis() nothrow @nogc
830 if (auto t
= ThreadBase
.getThis())
831 ThreadBase
.remove(t
);
836 * Deregisters the given thread from use with the runtime. If this routine
837 * is called for a thread which is not registered, the result is undefined.
839 * NOTE: This routine does not run thread-local static destructors when called.
840 * If full functionality as a D thread is desired, the following function
841 * must be called by the detached thread, particularly if the thread is
842 * being detached at some indeterminate time before program termination:
844 * $(D extern(C) void rt_moduleTlsDtor();)
846 extern (C
) void thread_detachByAddr(ThreadID addr
)
848 if (auto t
= thread_findByAddr(addr
))
849 ThreadBase
.remove(t
);
854 extern (C
) void thread_detachInstance(ThreadBase t
) nothrow @nogc
856 ThreadBase
.remove(t
);
861 * Search the list of all threads for a thread with the given thread identifier.
864 * addr = The thread identifier to search for.
866 * The thread object associated with the thread identifier, null if not found.
868 static ThreadBase
thread_findByAddr(ThreadID addr
)
870 ThreadBase
.slock
.lock_nothrow();
871 scope(exit
) ThreadBase
.slock
.unlock_nothrow();
873 // also return just spawned thread so that
874 // DLL_THREAD_ATTACH knows it's a D thread
875 foreach (t
; ThreadBase
.pAboutToStart
[0 .. ThreadBase
.nAboutToStart
])
876 if (t
.m_addr
== addr
)
879 foreach (t
; ThreadBase
)
880 if (t
.m_addr
== addr
)
888 * Sets the current thread to a specific reference. Only to be used
889 * when dealing with externally-created threads (in e.g. C code).
890 * The primary use of this function is when ThreadBase.getThis() must
891 * return a sensible value in, for example, TLS destructors. In
892 * other words, don't touch this unless you know what you're doing.
895 * t = A reference to the current thread. May be null.
897 extern (C
) void thread_setThis(ThreadBase t
) nothrow @nogc
899 ThreadBase
.setThis(t
);
904 * Joins all non-daemon threads that are currently running. This is done by
905 * performing successive scans through the thread list until a scan consists
906 * of only daemon threads.
908 extern (C
) void thread_joinAll()
911 ThreadBase
.slock
.lock_nothrow();
912 // wait for just spawned threads
913 if (ThreadBase
.nAboutToStart
)
915 ThreadBase
.slock
.unlock_nothrow();
920 // join all non-daemon threads, the main thread is also a daemon
921 auto t
= ThreadBase
.sm_tbeg
;
927 ThreadBase
.remove(t
);
936 ThreadBase
.slock
.unlock_nothrow();
937 t
.join(); // might rethrow
938 goto Lagain
; // must restart iteration b/c of unlock
941 ThreadBase
.slock
.unlock_nothrow();
946 * Performs intermediate shutdown of the thread module.
948 shared static ~this()
950 // NOTE: The functionality related to garbage collection must be minimally
951 // operable after this dtor completes. Therefore, only minimal
952 // cleanup may occur.
953 auto t
= ThreadBase
.sm_tbeg
;
958 ThreadBase
.remove(t
);
963 // Used for needLock below.
964 package __gshared
bool multiThreadedFlag
= false;
966 // Used for suspendAll/resumeAll below.
967 package __gshared
uint suspendDepth
= 0;
969 private alias resume
= externDFunc
!("core.thread.osthread.resume", void function(ThreadBase
) nothrow @nogc);
972 * Resume all threads but the calling thread for "stop the world" garbage
973 * collection runs. This function must be called once for each preceding
974 * call to thread_suspendAll before the threads are actually resumed.
977 * This routine must be preceded by a call to thread_suspendAll.
980 * ThreadError if the resume operation fails for a running thread.
982 extern (C
) void thread_resumeAll() nothrow
985 assert(suspendDepth
> 0);
989 // NOTE: See thread_suspendAll for the logic behind this.
990 if (!multiThreadedFlag
&& ThreadBase
.sm_tbeg
)
992 if (--suspendDepth
== 0)
993 resume(ThreadBase
.getThis());
997 scope(exit
) ThreadBase
.slock
.unlock_nothrow();
999 if (--suspendDepth
> 0)
1002 for (ThreadBase t
= ThreadBase
.sm_tbeg
; t
; t
= t
.next
)
1004 // NOTE: We do not need to care about critical regions at all
1005 // here. thread_suspendAll takes care of everything.
1012 * Indicates the kind of scan being performed by $(D thread_scanAllType).
1016 stack
, /// The stack and/or registers are being scanned.
1017 tls
, /// TLS data is being scanned.
1020 alias ScanAllThreadsFn
= void delegate(void*, void*) nothrow; /// The scanning function.
1021 alias ScanAllThreadsTypeFn
= void delegate(ScanType
, void*, void*) nothrow; /// ditto
1024 * The main entry point for garbage collection. The supplied delegate
1025 * will be passed ranges representing both stack and register values.
1028 * scan = The scanner function. It should scan from p1 through p2 - 1.
1031 * This routine must be preceded by a call to thread_suspendAll.
1033 extern (C
) void thread_scanAllType(scope ScanAllThreadsTypeFn scan
) nothrow
1036 assert(suspendDepth
> 0);
1040 callWithStackShell(sp
=> scanAllTypeImpl(scan
, sp
));
1043 package alias callWithStackShellDg
= void delegate(void* sp
) nothrow;
1044 private alias callWithStackShell
= externDFunc
!("core.thread.osthread.callWithStackShell", void function(scope callWithStackShellDg
) nothrow);
1046 private void scanAllTypeImpl(scope ScanAllThreadsTypeFn scan
, void* curStackTop
) nothrow
1048 ThreadBase thisThread
= null;
1049 void* oldStackTop
= null;
1051 if (ThreadBase
.sm_tbeg
)
1053 thisThread
= ThreadBase
.getThis();
1054 if (!thisThread
.m_lock
)
1056 oldStackTop
= thisThread
.m_curr
.tstack
;
1057 thisThread
.m_curr
.tstack
= curStackTop
;
1063 if (ThreadBase
.sm_tbeg
)
1065 if (!thisThread
.m_lock
)
1067 thisThread
.m_curr
.tstack
= oldStackTop
;
1072 // NOTE: Synchronizing on ThreadBase.slock is not needed because this
1073 // function may only be called after all other threads have
1074 // been suspended from within the same lock.
1075 if (ThreadBase
.nAboutToStart
)
1076 scan(ScanType
.stack
, ThreadBase
.pAboutToStart
, ThreadBase
.pAboutToStart
+ ThreadBase
.nAboutToStart
);
1078 for (StackContext
* c
= ThreadBase
.sm_cbeg
; c
; c
= c
.next
)
1080 static if (isStackGrowingDown
)
1082 assert(c
.tstack
<= c
.bstack
, "stack bottom can't be less than top");
1084 // NOTE: We can't index past the bottom of the stack
1085 // so don't do the "+1" if isStackGrowingDown.
1086 if (c
.tstack
&& c
.tstack
< c
.bstack
)
1087 scan(ScanType
.stack
, c
.tstack
, c
.bstack
);
1091 assert(c
.bstack
<= c
.tstack
, "stack top can't be less than bottom");
1093 if (c
.bstack
&& c
.bstack
< c
.tstack
)
1094 scan(ScanType
.stack
, c
.bstack
, c
.tstack
+ 1);
1098 for (ThreadBase t
= ThreadBase
.sm_tbeg
; t
; t
= t
.next
)
1102 // Ideally, we'd pass ScanType.regs or something like that, but this
1103 // would make portability annoying because it only makes sense on Windows.
1104 scanWindowsOnly(scan
, t
);
1107 if (t
.m_tlsgcdata
!is null)
1108 rt_tlsgc_scan(t
.m_tlsgcdata
, (p1
, p2
) => scan(ScanType
.tls
, p1
, p2
));
1114 // Currently scanWindowsOnly can't be handled properly by externDFunc
1115 // https://github.com/dlang/druntime/pull/3135#issuecomment-643673218
1116 pragma(mangle
, "_D4core6thread8osthread15scanWindowsOnlyFNbMDFNbEQBvQBt10threadbase8ScanTypePvQcZvCQDdQDbQBi10ThreadBaseZv")
1117 private extern (D
) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan
, ThreadBase
) nothrow;
1121 * The main entry point for garbage collection. The supplied delegate
1122 * will be passed ranges representing both stack and register values.
1125 * scan = The scanner function. It should scan from p1 through p2 - 1.
1128 * This routine must be preceded by a call to thread_suspendAll.
1130 extern (C
) void thread_scanAll(scope ScanAllThreadsFn scan
) nothrow
1132 thread_scanAllType((type
, p1
, p2
) => scan(p1
, p2
));
1135 private alias thread_yield
= externDFunc
!("core.thread.osthread.thread_yield", void function() @nogc nothrow);
1138 * Signals that the code following this call is a critical region. Any code in
1139 * this region must finish running before the calling thread can be suspended
1140 * by a call to thread_suspendAll.
1142 * This function is, in particular, meant to help maintain garbage collector
1143 * invariants when a lock is not used.
1145 * A critical region is exited with thread_exitCriticalRegion.
1148 * Using critical regions is extremely error-prone. For instance, using locks
1149 * inside a critical region can easily result in a deadlock when another thread
1150 * holding the lock already got suspended.
1152 * The term and concept of a 'critical region' comes from
1153 * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925, Mono's SGen garbage collector).
1156 * The calling thread must be attached to the runtime.
1158 extern (C
) void thread_enterCriticalRegion() @nogc
1161 assert(ThreadBase
.getThis());
1165 synchronized (ThreadBase
.criticalRegionLock
)
1166 ThreadBase
.getThis().m_isInCriticalRegion
= true;
1171 * Signals that the calling thread is no longer in a critical region. Following
1172 * a call to this function, the thread can once again be suspended.
1175 * The calling thread must be attached to the runtime.
1177 extern (C
) void thread_exitCriticalRegion() @nogc
1180 assert(ThreadBase
.getThis());
1184 synchronized (ThreadBase
.criticalRegionLock
)
1185 ThreadBase
.getThis().m_isInCriticalRegion
= false;
1190 * Returns true if the current thread is in a critical region; otherwise, false.
1193 * The calling thread must be attached to the runtime.
1195 extern (C
) bool thread_inCriticalRegion() @nogc
1198 assert(ThreadBase
.getThis());
1202 synchronized (ThreadBase
.criticalRegionLock
)
1203 return ThreadBase
.getThis().m_isInCriticalRegion
;
1208 * A callback for thread errors in D during collections. Since an allocation is not possible
1209 * a preallocated ThreadError will be used as the Error instance
1216 package void onThreadError(string msg
) nothrow @nogc
1218 __gshared ThreadError error
= new ThreadError(null);
1221 import core
.exception
: SuppressTraceInfo
;
1222 error
.info
= SuppressTraceInfo
.instance
;
1228 assert(!thread_inCriticalRegion());
1231 thread_enterCriticalRegion();
1234 thread_exitCriticalRegion();
1236 assert(thread_inCriticalRegion());
1239 assert(!thread_inCriticalRegion());
1244 * Indicates whether an address has been marked by the GC.
1248 no
, /// Address is not marked.
1249 yes
, /// Address is marked.
1250 unknown
, /// Address is not managed by the GC.
1253 alias IsMarkedDg
= int delegate(void* addr
) nothrow; /// The isMarked callback function.
1256 * This routine allows the runtime to process any special per-thread handling
1257 * for the GC. This is needed for taking into account any memory that is
1258 * referenced by non-scanned pointers but is about to be freed. That currently
1259 * means the array append cache.
1262 * isMarked = The function used to check if $(D addr) is marked.
1265 * This routine must be called just prior to resuming all threads.
1267 extern(C
) void thread_processGCMarks(scope IsMarkedDg isMarked
) nothrow
1269 for (ThreadBase t
= ThreadBase
.sm_tbeg
; t
; t
= t
.next
)
1271 /* Can be null if collection was triggered between adding a
1272 * thread and calling rt_tlsgc_init.
1274 if (t
.m_tlsgcdata
!is null)
1275 rt_tlsgc_processGCMarks(t
.m_tlsgcdata
, isMarked
);
1281 * Returns the stack top of the currently active stack within the calling
1285 * The calling thread must be attached to the runtime.
1288 * The address of the stack top.
1290 extern (C
) void* thread_stackTop() nothrow @nogc
1293 // Not strictly required, but it gives us more flexibility.
1294 assert(ThreadBase
.getThis());
1298 return getStackTop();
1303 * Returns the stack bottom of the currently active stack within the calling
1307 * The calling thread must be attached to the runtime.
1310 * The address of the stack bottom.
1312 extern (C
) void* thread_stackBottom() nothrow @nogc
1313 in (ThreadBase
.getThis())
1315 return ThreadBase
.getThis().topContext().bstack
;
1319 ///////////////////////////////////////////////////////////////////////////////
1320 // lowlovel threading support
1321 ///////////////////////////////////////////////////////////////////////////////
1324 __gshared size_t ll_nThreads
;
1325 __gshared ll_ThreadData
* ll_pThreads
;
1327 __gshared
align(mutexAlign
) void[mutexClassInstanceSize
] ll_lock
;
1329 @property Mutex
lowlevelLock() nothrow @nogc
1331 return cast(Mutex
)ll_lock
.ptr
;
1334 void initLowlevelThreads() @nogc
1336 import core
.lifetime
: emplace
;
1337 emplace(lowlevelLock());
1340 void termLowlevelThreads() @nogc
1342 lowlevelLock
.__dtor();
1345 void ll_removeThread(ThreadID tid
) nothrow @nogc
1347 lowlevelLock
.lock_nothrow();
1348 scope(exit
) lowlevelLock
.unlock_nothrow();
1350 foreach (i
; 0 .. ll_nThreads
)
1352 if (tid
is ll_pThreads
[i
].tid
)
1354 import core
.stdc
.string
: memmove
;
1355 memmove(ll_pThreads
+ i
, ll_pThreads
+ i
+ 1, ll_ThreadData
.sizeof
* (ll_nThreads
- i
- 1));
1357 // no need to minimize, next add will do
1365 * Check whether a thread was created by `createLowLevelThread`.
1368 * tid = the platform specific thread ID.
1370 * Returns: `true` if the thread was created by `createLowLevelThread` and is still running.
1372 bool findLowLevelThread(ThreadID tid
) nothrow @nogc
1374 lowlevelLock
.lock_nothrow();
1375 scope(exit
) lowlevelLock
.unlock_nothrow();
1377 foreach (i
; 0 .. ll_nThreads
)
1378 if (tid
is ll_pThreads
[i
].tid
)