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