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