"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/druntime/import/core/thread/threadbase.d" (20 Nov 2020, 39640 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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     import core.internal.traits : classInstanceAlignment;
   81 
   82     enum mutexAlign = classInstanceAlignment!Mutex;
   83     enum mutexClassInstanceSize = __traits(classInstanceSize, Mutex);
   84 
   85     alias swapContext = externDFunc!("core.thread.osthread.swapContext", void* function(void*) nothrow @nogc);
   86 
   87     alias getStackBottom = externDFunc!("core.thread.osthread.getStackBottom", void* function() nothrow @nogc);
   88     alias getStackTop = externDFunc!("core.thread.osthread.getStackTop", void* function() nothrow @nogc);
   89 }
   90 
   91 
   92 ///////////////////////////////////////////////////////////////////////////////
   93 // Thread
   94 ///////////////////////////////////////////////////////////////////////////////
   95 
   96 
   97 class ThreadBase
   98 {
   99     ///////////////////////////////////////////////////////////////////////////
  100     // Initialization
  101     ///////////////////////////////////////////////////////////////////////////
  102 
  103     this(void function() fn, size_t sz = 0) @safe pure nothrow @nogc
  104     in(fn)
  105     {
  106         this(sz);
  107         m_call = fn;
  108     }
  109 
  110     this(void delegate() dg, size_t sz = 0) @safe pure nothrow @nogc
  111     in(dg)
  112     {
  113         this(sz);
  114         m_call = dg;
  115     }
  116 
  117     /**
  118      * Cleans up any remaining resources used by this object.
  119      */
  120     package bool destructBeforeDtor() nothrow @nogc
  121     {
  122         destroyDataStorageIfAvail();
  123 
  124         bool no_context = m_addr == m_addr.init;
  125         bool not_registered = !next && !prev && (sm_tbeg !is this);
  126 
  127         return (no_context || not_registered);
  128     }
  129 
  130     package void tlsGCdataInit() nothrow @nogc
  131     {
  132         m_tlsgcdata = rt_tlsgc_init();
  133     }
  134 
  135     package void initDataStorage() nothrow
  136     {
  137         assert(m_curr is &m_main);
  138 
  139         m_main.bstack = getStackBottom();
  140         m_main.tstack = m_main.bstack;
  141         tlsGCdataInit();
  142     }
  143 
  144     package void destroyDataStorage() nothrow @nogc
  145     {
  146         rt_tlsgc_destroy(m_tlsgcdata);
  147         m_tlsgcdata = null;
  148     }
  149 
  150     package void destroyDataStorageIfAvail() nothrow @nogc
  151     {
  152         if (m_tlsgcdata)
  153             destroyDataStorage();
  154     }
  155 
  156 
  157     ///////////////////////////////////////////////////////////////////////////
  158     // General Actions
  159     ///////////////////////////////////////////////////////////////////////////
  160 
  161 
  162     /**
  163      * Waits for this thread to complete.  If the thread terminated as the
  164      * result of an unhandled exception, this exception will be rethrown.
  165      *
  166      * Params:
  167      *  rethrow = Rethrow any unhandled exception which may have caused this
  168      *            thread to terminate.
  169      *
  170      * Throws:
  171      *  ThreadException if the operation fails.
  172      *  Any exception not handled by the joined thread.
  173      *
  174      * Returns:
  175      *  Any exception not handled by this thread if rethrow = false, null
  176      *  otherwise.
  177      */
  178     abstract Throwable join(bool rethrow = true);
  179 
  180 
  181     ///////////////////////////////////////////////////////////////////////////
  182     // General Properties
  183     ///////////////////////////////////////////////////////////////////////////
  184 
  185 
  186     /**
  187      * Gets the OS identifier for this thread.
  188      *
  189      * Returns:
  190      *  If the thread hasn't been started yet, returns $(LREF ThreadID)$(D.init).
  191      *  Otherwise, returns the result of $(D GetCurrentThreadId) on Windows,
  192      *  and $(D pthread_self) on POSIX.
  193      *
  194      *  The value is unique for the current process.
  195      */
  196     final @property ThreadID id() @safe @nogc
  197     {
  198         synchronized(this)
  199         {
  200             return m_addr;
  201         }
  202     }
  203 
  204 
  205     /**
  206      * Gets the user-readable label for this thread.
  207      *
  208      * Returns:
  209      *  The name of this thread.
  210      */
  211     final @property string name() @safe @nogc
  212     {
  213         synchronized(this)
  214         {
  215             return m_name;
  216         }
  217     }
  218 
  219 
  220     /**
  221      * Sets the user-readable label for this thread.
  222      *
  223      * Params:
  224      *  val = The new name of this thread.
  225      */
  226     final @property void name(string val) @safe @nogc
  227     {
  228         synchronized(this)
  229         {
  230             m_name = val;
  231         }
  232     }
  233 
  234 
  235     /**
  236      * Gets the daemon status for this thread.  While the runtime will wait for
  237      * all normal threads to complete before tearing down the process, daemon
  238      * threads are effectively ignored and thus will not prevent the process
  239      * from terminating.  In effect, daemon threads will be terminated
  240      * automatically by the OS when the process exits.
  241      *
  242      * Returns:
  243      *  true if this is a daemon thread.
  244      */
  245     final @property bool isDaemon() @safe @nogc
  246     {
  247         synchronized(this)
  248         {
  249             return m_isDaemon;
  250         }
  251     }
  252 
  253 
  254     /**
  255      * Sets the daemon status for this thread.  While the runtime will wait for
  256      * all normal threads to complete before tearing down the process, daemon
  257      * threads are effectively ignored and thus will not prevent the process
  258      * from terminating.  In effect, daemon threads will be terminated
  259      * automatically by the OS when the process exits.
  260      *
  261      * Params:
  262      *  val = The new daemon status for this thread.
  263      */
  264     final @property void isDaemon(bool val) @safe @nogc
  265     {
  266         synchronized(this)
  267         {
  268             m_isDaemon = val;
  269         }
  270     }
  271 
  272     /**
  273      * Tests whether this thread is the main thread, i.e. the thread
  274      * that initialized the runtime
  275      *
  276      * Returns:
  277      *  true if the thread is the main thread
  278      */
  279     final @property bool isMainThread() nothrow @nogc
  280     {
  281         return this is sm_main;
  282     }
  283 
  284     /**
  285      * Tests whether this thread is running.
  286      *
  287      * Returns:
  288      *  true if the thread is running, false if not.
  289      */
  290     @property bool isRunning() nothrow @nogc
  291     {
  292         if (m_addr == m_addr.init)
  293             return false;
  294 
  295         return true;
  296     }
  297 
  298 
  299     ///////////////////////////////////////////////////////////////////////////
  300     // Thread Accessors
  301     ///////////////////////////////////////////////////////////////////////////
  302 
  303     /**
  304      * Provides a reference to the calling thread.
  305      *
  306      * Returns:
  307      *  The thread object representing the calling thread.  The result of
  308      *  deleting this object is undefined.  If the current thread is not
  309      *  attached to the runtime, a null reference is returned.
  310      */
  311     static ThreadBase getThis() @safe nothrow @nogc
  312     {
  313         // NOTE: This function may not be called until thread_init has
  314         //       completed.  See thread_suspendAll for more information
  315         //       on why this might occur.
  316         return sm_this;
  317     }
  318 
  319 
  320     /**
  321      * Provides a list of all threads currently being tracked by the system.
  322      * Note that threads in the returned array might no longer run (see
  323      * $(D ThreadBase.)$(LREF isRunning)).
  324      *
  325      * Returns:
  326      *  An array containing references to all threads currently being
  327      *  tracked by the system.  The result of deleting any contained
  328      *  objects is undefined.
  329      */
  330     static ThreadBase[] getAll()
  331     {
  332         static void resize(ref ThreadBase[] buf, size_t nlen)
  333         {
  334             buf.length = nlen;
  335         }
  336         return getAllImpl!resize();
  337     }
  338 
  339 
  340     /**
  341      * Operates on all threads currently being tracked by the system.  The
  342      * result of deleting any Thread object is undefined.
  343      * Note that threads passed to the callback might no longer run (see
  344      * $(D ThreadBase.)$(LREF isRunning)).
  345      *
  346      * Params:
  347      *  dg = The supplied code as a delegate.
  348      *
  349      * Returns:
  350      *  Zero if all elemented are visited, nonzero if not.
  351      */
  352     static int opApply(scope int delegate(ref ThreadBase) dg)
  353     {
  354         static void resize(ref ThreadBase[] buf, size_t nlen)
  355         {
  356             buf = (cast(ThreadBase*)realloc(buf.ptr, nlen * size_t.sizeof))[0 .. nlen];
  357         }
  358         auto buf = getAllImpl!resize;
  359         scope(exit) if (buf.ptr) free(buf.ptr);
  360 
  361         foreach (t; buf)
  362         {
  363             if (auto res = dg(t))
  364                 return res;
  365         }
  366         return 0;
  367     }
  368 
  369     private static ThreadBase[] getAllImpl(alias resize)()
  370     {
  371         import core.atomic;
  372 
  373         ThreadBase[] buf;
  374         while (true)
  375         {
  376             immutable len = atomicLoad!(MemoryOrder.raw)(*cast(shared)&sm_tlen);
  377             resize(buf, len);
  378             assert(buf.length == len);
  379             synchronized (slock)
  380             {
  381                 if (len == sm_tlen)
  382                 {
  383                     size_t pos;
  384                     for (ThreadBase t = sm_tbeg; t; t = t.next)
  385                         buf[pos++] = t;
  386                     return buf;
  387                 }
  388             }
  389         }
  390     }
  391 
  392     ///////////////////////////////////////////////////////////////////////////
  393     // Actions on Calling Thread
  394     ///////////////////////////////////////////////////////////////////////////
  395 
  396     /**
  397      * Forces a context switch to occur away from the calling thread.
  398      */
  399     private static void yield() @nogc nothrow
  400     {
  401         thread_yield();
  402     }
  403 
  404     ///////////////////////////////////////////////////////////////////////////
  405     // Stuff That Should Go Away
  406     ///////////////////////////////////////////////////////////////////////////
  407 
  408 
  409     //
  410     // Initializes a thread object which has no associated executable function.
  411     // This is used for the main thread initialized in thread_init().
  412     //
  413     package this(size_t sz = 0) @safe pure nothrow @nogc
  414     {
  415         m_sz = sz;
  416         m_curr = &m_main;
  417     }
  418 
  419     //
  420     // Thread entry point.  Invokes the function or delegate passed on
  421     // construction (if any).
  422     //
  423     package final void run()
  424     {
  425         m_call();
  426     }
  427 
  428 package:
  429 
  430     //
  431     // Local storage
  432     //
  433     static ThreadBase       sm_this;
  434 
  435 
  436     //
  437     // Main process thread
  438     //
  439     __gshared ThreadBase    sm_main;
  440 
  441 
  442     //
  443     // Standard thread data
  444     //
  445     ThreadID            m_addr;
  446     Callable            m_call;
  447     string              m_name;
  448     size_t              m_sz;
  449     bool                m_isDaemon;
  450     bool                m_isInCriticalRegion;
  451     Throwable           m_unhandled;
  452 
  453     ///////////////////////////////////////////////////////////////////////////
  454     // Storage of Active Thread
  455     ///////////////////////////////////////////////////////////////////////////
  456 
  457 
  458     //
  459     // Sets a thread-local reference to the current thread object.
  460     //
  461     package static void setThis(ThreadBase t) nothrow @nogc
  462     {
  463         sm_this = t;
  464     }
  465 
  466 package(core.thread):
  467 
  468     StackContext        m_main;
  469     StackContext*       m_curr;
  470     bool                m_lock;
  471     private void*       m_tlsgcdata;
  472 
  473     ///////////////////////////////////////////////////////////////////////////
  474     // Thread Context and GC Scanning Support
  475     ///////////////////////////////////////////////////////////////////////////
  476 
  477 
  478     final void pushContext(StackContext* c) nothrow @nogc
  479     in
  480     {
  481         assert(!c.within);
  482     }
  483     do
  484     {
  485         m_curr.ehContext = swapContext(c.ehContext);
  486         c.within = m_curr;
  487         m_curr = c;
  488     }
  489 
  490 
  491     final void popContext() nothrow @nogc
  492     in
  493     {
  494         assert(m_curr && m_curr.within);
  495     }
  496     do
  497     {
  498         StackContext* c = m_curr;
  499         m_curr = c.within;
  500         c.ehContext = swapContext(m_curr.ehContext);
  501         c.within = null;
  502     }
  503 
  504     private final StackContext* topContext() nothrow @nogc
  505     in(m_curr)
  506     {
  507         return m_curr;
  508     }
  509 
  510 
  511 package(core.thread):
  512     ///////////////////////////////////////////////////////////////////////////
  513     // GC Scanning Support
  514     ///////////////////////////////////////////////////////////////////////////
  515 
  516 
  517     // NOTE: The GC scanning process works like so:
  518     //
  519     //          1. Suspend all threads.
  520     //          2. Scan the stacks of all suspended threads for roots.
  521     //          3. Resume all threads.
  522     //
  523     //       Step 1 and 3 require a list of all threads in the system, while
  524     //       step 2 requires a list of all thread stacks (each represented by
  525     //       a Context struct).  Traditionally, there was one stack per thread
  526     //       and the Context structs were not necessary.  However, Fibers have
  527     //       changed things so that each thread has its own 'main' stack plus
  528     //       an arbitrary number of nested stacks (normally referenced via
  529     //       m_curr).  Also, there may be 'free-floating' stacks in the system,
  530     //       which are Fibers that are not currently executing on any specific
  531     //       thread but are still being processed and still contain valid
  532     //       roots.
  533     //
  534     //       To support all of this, the Context struct has been created to
  535     //       represent a stack range, and a global list of Context structs has
  536     //       been added to enable scanning of these stack ranges.  The lifetime
  537     //       (and presence in the Context list) of a thread's 'main' stack will
  538     //       be equivalent to the thread's lifetime.  So the Ccontext will be
  539     //       added to the list on thread entry, and removed from the list on
  540     //       thread exit (which is essentially the same as the presence of a
  541     //       Thread object in its own global list).  The lifetime of a Fiber's
  542     //       context, however, will be tied to the lifetime of the Fiber object
  543     //       itself, and Fibers are expected to add/remove their Context struct
  544     //       on construction/deletion.
  545 
  546 
  547     //
  548     // All use of the global thread lists/array should synchronize on this lock.
  549     //
  550     // Careful as the GC acquires this lock after the GC lock to suspend all
  551     // threads any GC usage with slock held can result in a deadlock through
  552     // lock order inversion.
  553     @property static Mutex slock() nothrow @nogc
  554     {
  555         return cast(Mutex)_slock.ptr;
  556     }
  557 
  558     @property static Mutex criticalRegionLock() nothrow @nogc
  559     {
  560         return cast(Mutex)_criticalRegionLock.ptr;
  561     }
  562 
  563     __gshared align(mutexAlign) void[mutexClassInstanceSize] _slock;
  564     __gshared align(mutexAlign) void[mutexClassInstanceSize] _criticalRegionLock;
  565 
  566     static void initLocks() @nogc
  567     {
  568         _slock[] = typeid(Mutex).initializer[];
  569         (cast(Mutex)_slock.ptr).__ctor();
  570 
  571         _criticalRegionLock[] = typeid(Mutex).initializer[];
  572         (cast(Mutex)_criticalRegionLock.ptr).__ctor();
  573     }
  574 
  575     static void termLocks() @nogc
  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
  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);
  754 
  755 extern (C) void _d_monitordelete_nogc(Object h) @nogc;
  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
  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     if (typeid(ThreadT).initializer.ptr)
  769         _mainThreadStore[] = typeid(ThreadT).initializer[];
  770     else
  771         (cast(ubyte[])_mainThreadStore)[] = 0;
  772     ThreadBase.sm_main = null;
  773 
  774     assert(ThreadBase.sm_tbeg && ThreadBase.sm_tlen == 1);
  775     assert(!ThreadBase.nAboutToStart);
  776     if (ThreadBase.pAboutToStart) // in case realloc(p, 0) doesn't return null
  777     {
  778         free(ThreadBase.pAboutToStart);
  779         ThreadBase.pAboutToStart = null;
  780     }
  781     ThreadBase.termLocks();
  782     termLowlevelThreads();
  783 }
  784 
  785 
  786 /**
  787  *
  788  */
  789 extern (C) bool thread_isMainThread() nothrow @nogc
  790 {
  791     return ThreadBase.getThis() is ThreadBase.sm_main;
  792 }
  793 
  794 
  795 /**
  796  * Registers the calling thread for use with the D Runtime.  If this routine
  797  * is called for a thread which is already registered, no action is performed.
  798  *
  799  * NOTE: This routine does not run thread-local static constructors when called.
  800  *       If full functionality as a D thread is desired, the following function
  801  *       must be called after thread_attachThis:
  802  *
  803  *       extern (C) void rt_moduleTlsCtor();
  804  */
  805 package ThreadT thread_attachThis_tpl(ThreadT)()
  806 {
  807     if (auto t = ThreadT.getThis())
  808         return t;
  809 
  810     return cast(ThreadT) attachThread(new ThreadT());
  811 }
  812 
  813 
  814 /**
  815  * Deregisters the calling thread from use with the runtime.  If this routine
  816  * is called for a thread which is not registered, the result is undefined.
  817  *
  818  * NOTE: This routine does not run thread-local static destructors when called.
  819  *       If full functionality as a D thread is desired, the following function
  820  *       must be called after thread_detachThis, particularly if the thread is
  821  *       being detached at some indeterminate time before program termination:
  822  *
  823  *       $(D extern(C) void rt_moduleTlsDtor();)
  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);
  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
 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             // NOTE: We can't index past the bottom of the stack
 1080             //       so don't do the "+1" if isStackGrowingDown.
 1081             if (c.tstack && c.tstack < c.bstack)
 1082                 scan(ScanType.stack, c.tstack, c.bstack);
 1083         }
 1084         else
 1085         {
 1086             if (c.bstack && c.bstack < c.tstack)
 1087                 scan(ScanType.stack, c.bstack, c.tstack + 1);
 1088         }
 1089     }
 1090 
 1091     for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next)
 1092     {
 1093         version (Windows)
 1094         {
 1095             // Ideally, we'd pass ScanType.regs or something like that, but this
 1096             // would make portability annoying because it only makes sense on Windows.
 1097             scanWindowsOnly(scan, t);
 1098         }
 1099 
 1100         if (t.m_tlsgcdata !is null)
 1101             rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2));
 1102     }
 1103 }
 1104 
 1105 version (Windows)
 1106 {
 1107     // Currently scanWindowsOnly can't be handled properly by externDFunc
 1108     // https://github.com/dlang/druntime/pull/3135#issuecomment-643673218
 1109     pragma(mangle, "_D4core6thread8osthread15scanWindowsOnlyFNbMDFNbEQBvQBt10threadbase8ScanTypePvQcZvCQDdQDbQBi10ThreadBaseZv")
 1110     private extern (D) void scanWindowsOnly(scope ScanAllThreadsTypeFn scan, ThreadBase) nothrow;
 1111 }
 1112 
 1113 /**
 1114  * The main entry point for garbage collection.  The supplied delegate
 1115  * will be passed ranges representing both stack and register values.
 1116  *
 1117  * Params:
 1118  *  scan        = The scanner function.  It should scan from p1 through p2 - 1.
 1119  *
 1120  * In:
 1121  *  This routine must be preceded by a call to thread_suspendAll.
 1122  */
 1123 extern (C) void thread_scanAll(scope ScanAllThreadsFn scan) nothrow
 1124 {
 1125     thread_scanAllType((type, p1, p2) => scan(p1, p2));
 1126 }
 1127 
 1128 private alias thread_yield = externDFunc!("core.thread.osthread.thread_yield", void function() @nogc nothrow);
 1129 
 1130 /**
 1131  * Signals that the code following this call is a critical region. Any code in
 1132  * this region must finish running before the calling thread can be suspended
 1133  * by a call to thread_suspendAll.
 1134  *
 1135  * This function is, in particular, meant to help maintain garbage collector
 1136  * invariants when a lock is not used.
 1137  *
 1138  * A critical region is exited with thread_exitCriticalRegion.
 1139  *
 1140  * $(RED Warning):
 1141  * Using critical regions is extremely error-prone. For instance, using locks
 1142  * inside a critical region can easily result in a deadlock when another thread
 1143  * holding the lock already got suspended.
 1144  *
 1145  * The term and concept of a 'critical region' comes from
 1146  * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925, Mono's SGen garbage collector).
 1147  *
 1148  * In:
 1149  *  The calling thread must be attached to the runtime.
 1150  */
 1151 extern (C) void thread_enterCriticalRegion() @nogc
 1152 in
 1153 {
 1154     assert(ThreadBase.getThis());
 1155 }
 1156 do
 1157 {
 1158     synchronized (ThreadBase.criticalRegionLock)
 1159         ThreadBase.getThis().m_isInCriticalRegion = true;
 1160 }
 1161 
 1162 
 1163 /**
 1164  * Signals that the calling thread is no longer in a critical region. Following
 1165  * a call to this function, the thread can once again be suspended.
 1166  *
 1167  * In:
 1168  *  The calling thread must be attached to the runtime.
 1169  */
 1170 extern (C) void thread_exitCriticalRegion() @nogc
 1171 in
 1172 {
 1173     assert(ThreadBase.getThis());
 1174 }
 1175 do
 1176 {
 1177     synchronized (ThreadBase.criticalRegionLock)
 1178         ThreadBase.getThis().m_isInCriticalRegion = false;
 1179 }
 1180 
 1181 
 1182 /**
 1183  * Returns true if the current thread is in a critical region; otherwise, false.
 1184  *
 1185  * In:
 1186  *  The calling thread must be attached to the runtime.
 1187  */
 1188 extern (C) bool thread_inCriticalRegion() @nogc
 1189 in
 1190 {
 1191     assert(ThreadBase.getThis());
 1192 }
 1193 do
 1194 {
 1195     synchronized (ThreadBase.criticalRegionLock)
 1196         return ThreadBase.getThis().m_isInCriticalRegion;
 1197 }
 1198 
 1199 
 1200 /**
 1201 * A callback for thread errors in D during collections. Since an allocation is not possible
 1202 *  a preallocated ThreadError will be used as the Error instance
 1203 *
 1204 * Returns:
 1205 *  never returns
 1206 * Throws:
 1207 *  ThreadError.
 1208 */
 1209 package void onThreadError(string msg) nothrow @nogc
 1210 {
 1211     __gshared ThreadError error = new ThreadError(null);
 1212     error.msg = msg;
 1213     error.next = null;
 1214     import core.exception : SuppressTraceInfo;
 1215     error.info = SuppressTraceInfo.instance;
 1216     throw error;
 1217 }
 1218 
 1219 unittest
 1220 {
 1221     assert(!thread_inCriticalRegion());
 1222 
 1223     {
 1224         thread_enterCriticalRegion();
 1225 
 1226         scope (exit)
 1227             thread_exitCriticalRegion();
 1228 
 1229         assert(thread_inCriticalRegion());
 1230     }
 1231 
 1232     assert(!thread_inCriticalRegion());
 1233 }
 1234 
 1235 
 1236 /**
 1237  * Indicates whether an address has been marked by the GC.
 1238  */
 1239 enum IsMarked : int
 1240 {
 1241          no, /// Address is not marked.
 1242         yes, /// Address is marked.
 1243     unknown, /// Address is not managed by the GC.
 1244 }
 1245 
 1246 alias IsMarkedDg = int delegate(void* addr) nothrow; /// The isMarked callback function.
 1247 
 1248 /**
 1249  * This routine allows the runtime to process any special per-thread handling
 1250  * for the GC.  This is needed for taking into account any memory that is
 1251  * referenced by non-scanned pointers but is about to be freed.  That currently
 1252  * means the array append cache.
 1253  *
 1254  * Params:
 1255  *  isMarked = The function used to check if $(D addr) is marked.
 1256  *
 1257  * In:
 1258  *  This routine must be called just prior to resuming all threads.
 1259  */
 1260 extern(C) void thread_processGCMarks(scope IsMarkedDg isMarked) nothrow
 1261 {
 1262     for (ThreadBase t = ThreadBase.sm_tbeg; t; t = t.next)
 1263     {
 1264         /* Can be null if collection was triggered between adding a
 1265          * thread and calling rt_tlsgc_init.
 1266          */
 1267         if (t.m_tlsgcdata !is null)
 1268             rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked);
 1269     }
 1270 }
 1271 
 1272 
 1273 /**
 1274  * Returns the stack top of the currently active stack within the calling
 1275  * thread.
 1276  *
 1277  * In:
 1278  *  The calling thread must be attached to the runtime.
 1279  *
 1280  * Returns:
 1281  *  The address of the stack top.
 1282  */
 1283 extern (C) void* thread_stackTop() nothrow @nogc
 1284 in
 1285 {
 1286     // Not strictly required, but it gives us more flexibility.
 1287     assert(ThreadBase.getThis());
 1288 }
 1289 do
 1290 {
 1291     return getStackTop();
 1292 }
 1293 
 1294 
 1295 /**
 1296  * Returns the stack bottom of the currently active stack within the calling
 1297  * thread.
 1298  *
 1299  * In:
 1300  *  The calling thread must be attached to the runtime.
 1301  *
 1302  * Returns:
 1303  *  The address of the stack bottom.
 1304  */
 1305 extern (C) void* thread_stackBottom() nothrow @nogc
 1306 in (ThreadBase.getThis())
 1307 {
 1308     return ThreadBase.getThis().topContext().bstack;
 1309 }
 1310 
 1311 
 1312 ///////////////////////////////////////////////////////////////////////////////
 1313 // lowlovel threading support
 1314 ///////////////////////////////////////////////////////////////////////////////
 1315 package
 1316 {
 1317     __gshared size_t ll_nThreads;
 1318     __gshared ll_ThreadData* ll_pThreads;
 1319 
 1320     __gshared align(mutexAlign) void[mutexClassInstanceSize] ll_lock;
 1321 
 1322     @property Mutex lowlevelLock() nothrow @nogc
 1323     {
 1324         return cast(Mutex)ll_lock.ptr;
 1325     }
 1326 
 1327     void initLowlevelThreads() @nogc
 1328     {
 1329         ll_lock[] = typeid(Mutex).initializer[];
 1330         lowlevelLock.__ctor();
 1331     }
 1332 
 1333     void termLowlevelThreads() @nogc
 1334     {
 1335         lowlevelLock.__dtor();
 1336     }
 1337 
 1338     void ll_removeThread(ThreadID tid) nothrow @nogc
 1339     {
 1340         lowlevelLock.lock_nothrow();
 1341         scope(exit) lowlevelLock.unlock_nothrow();
 1342 
 1343         foreach (i; 0 .. ll_nThreads)
 1344         {
 1345             if (tid is ll_pThreads[i].tid)
 1346             {
 1347                 import core.stdc.string : memmove;
 1348                 memmove(ll_pThreads + i, ll_pThreads + i + 1, ll_ThreadData.sizeof * (ll_nThreads - i - 1));
 1349                 --ll_nThreads;
 1350                 // no need to minimize, next add will do
 1351                 break;
 1352             }
 1353         }
 1354     }
 1355 }
 1356 
 1357 /**
 1358  * Check whether a thread was created by `createLowLevelThread`.
 1359  *
 1360  * Params:
 1361  *  tid = the platform specific thread ID.
 1362  *
 1363  * Returns: `true` if the thread was created by `createLowLevelThread` and is still running.
 1364  */
 1365 bool findLowLevelThread(ThreadID tid) nothrow @nogc
 1366 {
 1367     lowlevelLock.lock_nothrow();
 1368     scope(exit) lowlevelLock.unlock_nothrow();
 1369 
 1370     foreach (i; 0 .. ll_nThreads)
 1371         if (tid is ll_pThreads[i].tid)
 1372             return true;
 1373     return false;
 1374 }