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 */
16module core.thread.threadbase;
17
18import core.thread.context;
19import core.thread.types;
20import core.time;
21import core.sync.mutex;
22import core.stdc.stdlib : free, realloc;
23
24private
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 */
49class 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*/
66class 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
79private
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
100class ThreadBase
101{
102    ///////////////////////////////////////////////////////////////////////////
103    // Initialization
104    ///////////////////////////////////////////////////////////////////////////
105
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
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     */
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
133    package void tlsGCdataInit() nothrow @nogc
134    {
135        m_tlsgcdata = rt_tlsgc_init();
136    }
137
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
147    package void destroyDataStorage() nothrow @nogc
148    {
149        rt_tlsgc_destroy(m_tlsgcdata);
150        m_tlsgcdata = null;
151    }
152
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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    //
431    package final void run()
432    {
433        m_call();
434    }
435
436package:
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    //
469    package static void setThis(ThreadBase t) nothrow @nogc
470    {
471        sm_this = t;
472    }
473
474package(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
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
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
512    private final StackContext* topContext() nothrow @nogc
513    in(m_curr)
514    {
515        return m_curr;
516    }
517
518
519package(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.
561    @property static Mutex slock() nothrow @nogc
562    {
563        return cast(Mutex)_slock.ptr;
564    }
565
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
574    static void initLocks() @nogc
575    {
576        import core.lifetime : emplace;
577        emplace!Mutex(_slock[]);
578        emplace!Mutex(_criticalRegionLock[]);
579    }
580
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    //
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)
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;
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    //
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
759private alias attachThread = externDFunc!("core.thread.osthread.attachThread", ThreadBase function(ThreadBase) @nogc nothrow);
760
761extern (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 */
767package 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 */
792extern (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 */
808package 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 */
828extern (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 */
846extern (C) void thread_detachByAddr(ThreadID addr)
847{
848    if (auto t = thread_findByAddr(addr))
849        ThreadBase.remove(t);
850}
851
852
853/// ditto
854extern (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 */
868static 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 */
897extern (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 */
908extern (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 */
948shared 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.
964package __gshared bool multiThreadedFlag = false;
965
966// Used for suspendAll/resumeAll below.
967package __gshared uint suspendDepth = 0;
968
969private 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 */
982extern (C) void thread_resumeAll() nothrow
983in
984{
985    assert(suspendDepth > 0);
986}
987do
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 */
1014enum ScanType
1015{
1016    stack, /// The stack and/or registers are being scanned.
1017    tls, /// TLS data is being scanned.
1018}
1019
1020alias ScanAllThreadsFn = void delegate(void*, void*) nothrow; /// The scanning function.
1021alias 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 */
1033extern (C) void thread_scanAllType(scope ScanAllThreadsTypeFn scan) nothrow
1034in
1035{
1036    assert(suspendDepth > 0);
1037}
1038do
1039{
1040    callWithStackShell(sp => scanAllTypeImpl(scan, sp));
1041}
1042
1043package alias callWithStackShellDg = void delegate(void* sp) nothrow;
1044private alias callWithStackShell = externDFunc!("core.thread.osthread.callWithStackShell", void function(scope callWithStackShellDg) nothrow);
1045
1046private 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
1112version (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 */
1130extern (C) void thread_scanAll(scope ScanAllThreadsFn scan) nothrow
1131{
1132    thread_scanAllType((type, p1, p2) => scan(p1, p2));
1133}
1134
1135private 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 */
1158extern (C) void thread_enterCriticalRegion() @nogc
1159in
1160{
1161    assert(ThreadBase.getThis());
1162}
1163do
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 */
1177extern (C) void thread_exitCriticalRegion() @nogc
1178in
1179{
1180    assert(ThreadBase.getThis());
1181}
1182do
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 */
1195extern (C) bool thread_inCriticalRegion() @nogc
1196in
1197{
1198    assert(ThreadBase.getThis());
1199}
1200do
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*/
1216package 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
1226unittest
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 */
1246enum 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
1253alias 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 */
1267extern(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 */
1290extern (C) void* thread_stackTop() nothrow @nogc
1291in
1292{
1293    // Not strictly required, but it gives us more flexibility.
1294    assert(ThreadBase.getThis());
1295}
1296do
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 */
1312extern (C) void* thread_stackBottom() nothrow @nogc
1313in (ThreadBase.getThis())
1314{
1315    return ThreadBase.getThis().topContext().bstack;
1316}
1317
1318
1319///////////////////////////////////////////////////////////////////////////////
1320// lowlovel threading support
1321///////////////////////////////////////////////////////////////////////////////
1322package
1323{
1324    __gshared size_t ll_nThreads;
1325    __gshared ll_ThreadData* ll_pThreads;
1326
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
1334    void initLowlevelThreads() @nogc
1335    {
1336        import core.lifetime : emplace;
1337        emplace(lowlevelLock());
1338    }
1339
1340    void termLowlevelThreads() @nogc
1341    {
1342        lowlevelLock.__dtor();
1343    }
1344
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 */
1372bool 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