The OpenD Programming Language

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