1 /**
2 * The thread module provides support for thread creation and management.
3 *
4 * Copyright: Copyright Sean Kelly 2005 - 2012.
5 * License: Distributed under the
6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7 * (See accompanying file LICENSE)
8 * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak
9 * Source: $(DRUNTIMESRC core/_thread.d)
10 */
11
12 /* NOTE: This file has been patched from the original DMD distribution to
13 * work with the GDC compiler.
14 */
15 module core.thread;
16
17
18 public import core.time; // for Duration
19 import core.exception : onOutOfMemoryError;
20
21 version (OSX)
22 version = Darwin;
23 else version (iOS)
24 version = Darwin;
25 else version (TVOS)
26 version = Darwin;
27 else version (WatchOS)
28 version = Darwin;
29
30 private
31 {
32 // interface to rt.tlsgc
33 import core.internal.traits : externDFunc;
34
35 alias rt_tlsgc_init = externDFunc!("rt.tlsgc.init", void* function() nothrow @nogc);
36 alias rt_tlsgc_destroy = externDFunc!("rt.tlsgc.destroy", void function(void*) nothrow @nogc);
37
38 alias ScanDg = void delegate(void* pstart, void* pend) nothrow;
39 alias rt_tlsgc_scan =
40 externDFunc!("rt.tlsgc.scan", void function(void*, scope ScanDg) nothrow);
41
42 alias rt_tlsgc_processGCMarks =
43 externDFunc!("rt.tlsgc.processGCMarks", void function(void*, scope IsMarkedDg) nothrow);
44 }
45
version(Solaris)46 version (Solaris)
47 {
48 import core.sys.solaris.sys.priocntl;
49 import core.sys.solaris.sys.types;
50 }
51
52 // this should be true for most architectures
53 version (GNU_StackGrowsDown)
54 version = StackGrowsDown;
55
56 /**
57 * Returns the process ID of the calling process, which is guaranteed to be
58 * unique on the system. This call is always successful.
59 *
60 * Example:
61 * ---
62 * writefln("Current process id: %s", getpid());
63 * ---
64 */
version(Posix)65 version (Posix)
66 {
67 alias getpid = core.sys.posix.unistd.getpid;
68 }
version(Windows)69 else version (Windows)
70 {
71 alias getpid = core.sys.windows.windows.GetCurrentProcessId;
72 }
73
74
75 ///////////////////////////////////////////////////////////////////////////////
76 // Thread and Fiber Exceptions
77 ///////////////////////////////////////////////////////////////////////////////
78
79
80 /**
81 * Base class for thread exceptions.
82 */
83 class ThreadException : Exception
84 {
85 @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
86 {
87 super(msg, file, line, next);
88 }
89
90 @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
91 {
92 super(msg, file, line, next);
93 }
94 }
95
96
97 /**
98 * Base class for thread errors to be used for function inside GC when allocations are unavailable.
99 */
100 class ThreadError : Error
101 {
102 @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
103 {
104 super(msg, file, line, next);
105 }
106
107 @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
108 {
109 super(msg, file, line, next);
110 }
111 }
112
113 private
114 {
115 import core.atomic, core.memory, core.sync.mutex;
116
117 // Handling unaligned mutexes are not supported on all platforms, so we must
118 // ensure that the address of all shared data are appropriately aligned.
119 import core.internal.traits : classInstanceAlignment;
120
121 enum mutexAlign = classInstanceAlignment!Mutex;
122 enum mutexClassInstanceSize = __traits(classInstanceSize, Mutex);
123
124 //
125 // exposed by compiler runtime
126 //
127 extern (C) void rt_moduleTlsCtor();
128 extern (C) void rt_moduleTlsDtor();
129
130 /**
131 * Hook for whatever EH implementation is used to save/restore some data
132 * per stack.
133 *
134 * Params:
135 * newContext = The return value of the prior call to this function
136 * where the stack was last swapped out, or null when a fiber stack
137 * is switched in for the first time.
138 */
139 extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc;
140
version(DigitalMars)141 version (DigitalMars)
142 {
143 version (Windows)
144 alias swapContext = _d_eh_swapContext;
145 else
146 {
147 extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc;
148
149 void* swapContext(void* newContext) nothrow @nogc
150 {
151 /* Detect at runtime which scheme is being used.
152 * Eventually, determine it statically.
153 */
154 static int which = 0;
155 final switch (which)
156 {
157 case 0:
158 {
159 assert(newContext == null);
160 auto p = _d_eh_swapContext(newContext);
161 auto pdwarf = _d_eh_swapContextDwarf(newContext);
162 if (p)
163 {
164 which = 1;
165 return p;
166 }
167 else if (pdwarf)
168 {
169 which = 2;
170 return pdwarf;
171 }
172 return null;
173 }
174 case 1:
175 return _d_eh_swapContext(newContext);
176 case 2:
177 return _d_eh_swapContextDwarf(newContext);
178 }
179 }
180 }
181 }
182 else
183 alias swapContext = _d_eh_swapContext;
184 }
185
186
187 ///////////////////////////////////////////////////////////////////////////////
188 // Thread Entry Point and Signal Handlers
189 ///////////////////////////////////////////////////////////////////////////////
190
191
version(Windows)192 version (Windows)
193 {
194 private
195 {
196 import core.stdc.stdint : uintptr_t; // for _beginthreadex decl below
197 import core.stdc.stdlib; // for malloc, atexit
198 import core.sys.windows.windows;
199 import core.sys.windows.threadaux; // for OpenThreadHandle
200
201 extern (Windows) alias btex_fptr = uint function(void*);
202 extern (C) uintptr_t _beginthreadex(void*, uint, btex_fptr, void*, uint, uint*) nothrow;
203
204 //
205 // Entry point for Windows threads
206 //
207 extern (Windows) uint thread_entryPoint( void* arg ) nothrow
208 {
209 Thread obj = cast(Thread) arg;
210 assert( obj );
211
212 assert( obj.m_curr is &obj.m_main );
213 obj.m_main.bstack = getStackBottom();
214 obj.m_main.tstack = obj.m_main.bstack;
215 obj.m_tlsgcdata = rt_tlsgc_init();
216
217 Thread.setThis(obj);
218 Thread.add(obj);
219 scope (exit)
220 {
221 Thread.remove(obj);
222 }
223 Thread.add(&obj.m_main);
224
225 // NOTE: No GC allocations may occur until the stack pointers have
226 // been set and Thread.getThis returns a valid reference to
227 // this thread object (this latter condition is not strictly
228 // necessary on Windows but it should be followed for the
229 // sake of consistency).
230
231 // TODO: Consider putting an auto exception object here (using
232 // alloca) forOutOfMemoryError plus something to track
233 // whether an exception is in-flight?
234
235 void append( Throwable t )
236 {
237 if ( obj.m_unhandled is null )
238 obj.m_unhandled = t;
239 else
240 {
241 Throwable last = obj.m_unhandled;
242 while ( last.next !is null )
243 last = last.next;
244 last.next = t;
245 }
246 }
247
248 version (D_InlineAsm_X86)
249 {
250 asm nothrow @nogc { fninit; }
251 }
252
253 try
254 {
255 rt_moduleTlsCtor();
256 try
257 {
258 obj.run();
259 }
260 catch ( Throwable t )
261 {
262 append( t );
263 }
264 rt_moduleTlsDtor();
265 }
266 catch ( Throwable t )
267 {
268 append( t );
269 }
270 return 0;
271 }
272
273
274 HANDLE GetCurrentThreadHandle() nothrow @nogc
275 {
276 const uint DUPLICATE_SAME_ACCESS = 0x00000002;
277
278 HANDLE curr = GetCurrentThread(),
279 proc = GetCurrentProcess(),
280 hndl;
281
282 DuplicateHandle( proc, curr, proc, &hndl, 0, TRUE, DUPLICATE_SAME_ACCESS );
283 return hndl;
284 }
285 }
286 }
version(Posix)287 else version (Posix)
288 {
289 private
290 {
291 import core.stdc.errno;
292 import core.sys.posix.semaphore;
293 import core.sys.posix.stdlib; // for malloc, valloc, free, atexit
294 import core.sys.posix.pthread;
295 import core.sys.posix.signal;
296 import core.sys.posix.time;
297
298 version (Darwin)
299 {
300 import core.sys.darwin.mach.thread_act;
301 import core.sys.darwin.pthread : pthread_mach_thread_np;
302 }
303
304 version (GNU)
305 {
306 import gcc.builtins;
307 }
308
309 //
310 // Entry point for POSIX threads
311 //
312 extern (C) void* thread_entryPoint( void* arg ) nothrow
313 {
314 version (Shared)
315 {
316 import rt.sections;
317 Thread obj = cast(Thread)(cast(void**)arg)[0];
318 auto loadedLibraries = (cast(void**)arg)[1];
319 .free(arg);
320 }
321 else
322 {
323 Thread obj = cast(Thread)arg;
324 }
325 assert( obj );
326
327 // loadedLibraries need to be inherited from parent thread
328 // before initilizing GC for TLS (rt_tlsgc_init)
329 version (Shared) inheritLoadedLibraries(loadedLibraries);
330
331 assert( obj.m_curr is &obj.m_main );
332 obj.m_main.bstack = getStackBottom();
333 obj.m_main.tstack = obj.m_main.bstack;
334 obj.m_tlsgcdata = rt_tlsgc_init();
335
336 atomicStore!(MemoryOrder.raw)(obj.m_isRunning, true);
337 Thread.setThis(obj); // allocates lazy TLS (see Issue 11981)
338 Thread.add(obj); // can only receive signals from here on
339 scope (exit)
340 {
341 Thread.remove(obj);
342 atomicStore!(MemoryOrder.raw)(obj.m_isRunning, false);
343 }
344 Thread.add(&obj.m_main);
345
346 static extern (C) void thread_cleanupHandler( void* arg ) nothrow @nogc
347 {
348 Thread obj = cast(Thread) arg;
349 assert( obj );
350
351 // NOTE: If the thread terminated abnormally, just set it as
352 // not running and let thread_suspendAll remove it from
353 // the thread list. This is safer and is consistent
354 // with the Windows thread code.
355 atomicStore!(MemoryOrder.raw)(obj.m_isRunning,false);
356 }
357
358 // NOTE: Using void to skip the initialization here relies on
359 // knowledge of how pthread_cleanup is implemented. It may
360 // not be appropriate for all platforms. However, it does
361 // avoid the need to link the pthread module. If any
362 // implementation actually requires default initialization
363 // then pthread_cleanup should be restructured to maintain
364 // the current lack of a link dependency.
365 static if ( __traits( compiles, pthread_cleanup ) )
366 {
367 pthread_cleanup cleanup = void;
368 cleanup.push( &thread_cleanupHandler, cast(void*) obj );
369 }
370 else static if ( __traits( compiles, pthread_cleanup_push ) )
371 {
372 pthread_cleanup_push( &thread_cleanupHandler, cast(void*) obj );
373 }
374 else
375 {
376 static assert( false, "Platform not supported." );
377 }
378
379 // NOTE: No GC allocations may occur until the stack pointers have
380 // been set and Thread.getThis returns a valid reference to
381 // this thread object (this latter condition is not strictly
382 // necessary on Windows but it should be followed for the
383 // sake of consistency).
384
385 // TODO: Consider putting an auto exception object here (using
386 // alloca) forOutOfMemoryError plus something to track
387 // whether an exception is in-flight?
388
389 void append( Throwable t )
390 {
391 if ( obj.m_unhandled is null )
392 obj.m_unhandled = t;
393 else
394 {
395 Throwable last = obj.m_unhandled;
396 while ( last.next !is null )
397 last = last.next;
398 last.next = t;
399 }
400 }
401
402 try
403 {
404 rt_moduleTlsCtor();
405 try
406 {
407 obj.run();
408 }
409 catch ( Throwable t )
410 {
411 append( t );
412 }
413 rt_moduleTlsDtor();
414 version (Shared) cleanupLoadedLibraries();
415 }
416 catch ( Throwable t )
417 {
418 append( t );
419 }
420
421 // NOTE: Normal cleanup is handled by scope(exit).
422
423 static if ( __traits( compiles, pthread_cleanup ) )
424 {
425 cleanup.pop( 0 );
426 }
427 else static if ( __traits( compiles, pthread_cleanup_push ) )
428 {
429 pthread_cleanup_pop( 0 );
430 }
431
432 return null;
433 }
434
435
436 //
437 // Used to track the number of suspended threads
438 //
439 __gshared sem_t suspendCount;
440
441
442 extern (C) void thread_suspendHandler( int sig ) nothrow
443 in
444 {
445 assert( sig == suspendSignalNumber );
446 }
447 body
448 {
449 void op(void* sp) nothrow
450 {
451 // NOTE: Since registers are being pushed and popped from the
452 // stack, any other stack data used by this function should
453 // be gone before the stack cleanup code is called below.
454 Thread obj = Thread.getThis();
455 assert(obj !is null);
456
457 if ( !obj.m_lock )
458 {
459 obj.m_curr.tstack = getStackTop();
460 }
461
462 sigset_t sigres = void;
463 int status;
464
465 status = sigfillset( &sigres );
466 assert( status == 0 );
467
468 status = sigdelset( &sigres, resumeSignalNumber );
469 assert( status == 0 );
470
471 version (FreeBSD) obj.m_suspendagain = false;
472 status = sem_post( &suspendCount );
473 assert( status == 0 );
474
475 sigsuspend( &sigres );
476
477 if ( !obj.m_lock )
478 {
479 obj.m_curr.tstack = obj.m_curr.bstack;
480 }
481 }
482
483 // avoid deadlocks on FreeBSD, see Issue 13416
484 version (FreeBSD)
485 {
486 auto obj = Thread.getThis();
487 if (THR_IN_CRITICAL(obj.m_addr))
488 {
489 obj.m_suspendagain = true;
490 if (sem_post(&suspendCount)) assert(0);
491 return;
492 }
493 }
494
495 callWithStackShell(&op);
496 }
497
498
499 extern (C) void thread_resumeHandler( int sig ) nothrow
500 in
501 {
502 assert( sig == resumeSignalNumber );
503 }
504 body
505 {
506
507 }
508
509 // HACK libthr internal (thr_private.h) macro, used to
510 // avoid deadlocks in signal handler, see Issue 13416
511 version (FreeBSD) bool THR_IN_CRITICAL(pthread_t p) nothrow @nogc
512 {
513 import core.sys.posix.config : c_long;
514 import core.sys.posix.sys.types : lwpid_t;
515
516 // If the begin of pthread would be changed in libthr (unlikely)
517 // we'll run into undefined behavior, compare with thr_private.h.
518 static struct pthread
519 {
520 c_long tid;
521 static struct umutex { lwpid_t owner; uint flags; uint[2] ceilings; uint[4] spare; }
522 umutex lock;
523 uint cycle;
524 int locklevel;
525 int critical_count;
526 // ...
527 }
528 auto priv = cast(pthread*)p;
529 return priv.locklevel > 0 || priv.critical_count > 0;
530 }
531 }
532 }
533 else
534 {
535 // NOTE: This is the only place threading versions are checked. If a new
536 // version is added, the module code will need to be searched for
537 // places where version-specific code may be required. This can be
538 // easily accomlished by searching for 'Windows' or 'Posix'.
539 static assert( false, "Unknown threading implementation." );
540 }
541
542
543 ///////////////////////////////////////////////////////////////////////////////
544 // Thread
545 ///////////////////////////////////////////////////////////////////////////////
546
547
548 /**
549 * This class encapsulates all threading functionality for the D
550 * programming language. As thread manipulation is a required facility
551 * for garbage collection, all user threads should derive from this
552 * class, and instances of this class should never be explicitly deleted.
553 * A new thread may be created using either derivation or composition, as
554 * in the following example.
555 */
556 class Thread
557 {
558 ///////////////////////////////////////////////////////////////////////////
559 // Initialization
560 ///////////////////////////////////////////////////////////////////////////
561
562
563 /**
564 * Initializes a thread object which is associated with a static
565 * D function.
566 *
567 * Params:
568 * fn = The thread function.
569 * sz = The stack size for this thread.
570 *
571 * In:
572 * fn must not be null.
573 */
function()574 this( void function() fn, size_t sz = 0 ) @safe pure nothrow @nogc
575 in
576 {
577 assert( fn );
578 }
579 body
580 {
581 this(sz);
582 () @trusted { m_fn = fn; }();
583 m_call = Call.FN;
584 m_curr = &m_main;
585 }
586
587
588 /**
589 * Initializes a thread object which is associated with a dynamic
590 * D function.
591 *
592 * Params:
593 * dg = The thread function.
594 * sz = The stack size for this thread.
595 *
596 * In:
597 * dg must not be null.
598 */
delegate()599 this( void delegate() dg, size_t sz = 0 ) @safe pure nothrow @nogc
600 in
601 {
602 assert( dg );
603 }
604 body
605 {
606 this(sz);
607 () @trusted { m_dg = dg; }();
608 m_call = Call.DG;
609 m_curr = &m_main;
610 }
611
612
613 /**
614 * Cleans up any remaining resources used by this object.
615 */
~this()616 ~this() nothrow @nogc
617 {
618 if ( m_addr == m_addr.init )
619 {
620 return;
621 }
622
623 version (Windows)
624 {
625 m_addr = m_addr.init;
626 CloseHandle( m_hndl );
627 m_hndl = m_hndl.init;
628 }
629 else version (Posix)
630 {
631 pthread_detach( m_addr );
632 m_addr = m_addr.init;
633 }
634 version (Darwin)
635 {
636 m_tmach = m_tmach.init;
637 }
638 rt_tlsgc_destroy( m_tlsgcdata );
639 m_tlsgcdata = null;
640 }
641
642
643 ///////////////////////////////////////////////////////////////////////////
644 // General Actions
645 ///////////////////////////////////////////////////////////////////////////
646
647
648 /**
649 * Starts the thread and invokes the function or delegate passed upon
650 * construction.
651 *
652 * In:
653 * This routine may only be called once per thread instance.
654 *
655 * Throws:
656 * ThreadException if the thread fails to start.
657 */
start()658 final Thread start() nothrow
659 in
660 {
661 assert( !next && !prev );
662 }
663 body
664 {
665 auto wasThreaded = multiThreadedFlag;
666 multiThreadedFlag = true;
scope(failure)667 scope( failure )
668 {
669 if ( !wasThreaded )
670 multiThreadedFlag = false;
671 }
672
version(Windows)673 version (Windows) {} else
version(Posix)674 version (Posix)
675 {
676 pthread_attr_t attr;
677
678 if ( pthread_attr_init( &attr ) )
679 onThreadError( "Error initializing thread attributes" );
680 if ( m_sz && pthread_attr_setstacksize( &attr, m_sz ) )
681 onThreadError( "Error initializing thread stack size" );
682 }
683
version(Windows)684 version (Windows)
685 {
686 // NOTE: If a thread is just executing DllMain()
687 // while another thread is started here, it holds an OS internal
688 // lock that serializes DllMain with CreateThread. As the code
689 // might request a synchronization on slock (e.g. in thread_findByAddr()),
690 // we cannot hold that lock while creating the thread without
691 // creating a deadlock
692 //
693 // Solution: Create the thread in suspended state and then
694 // add and resume it with slock acquired
695 assert(m_sz <= uint.max, "m_sz must be less than or equal to uint.max");
696 m_hndl = cast(HANDLE) _beginthreadex( null, cast(uint) m_sz, &thread_entryPoint, cast(void*) this, CREATE_SUSPENDED, &m_addr );
697 if ( cast(size_t) m_hndl == 0 )
698 onThreadError( "Error creating thread" );
699 }
700
701 slock.lock_nothrow();
702 scope(exit) slock.unlock_nothrow();
703 {
704 ++nAboutToStart;
705 pAboutToStart = cast(Thread*)realloc(pAboutToStart, Thread.sizeof * nAboutToStart);
706 pAboutToStart[nAboutToStart - 1] = this;
version(Windows)707 version (Windows)
708 {
709 if ( ResumeThread( m_hndl ) == -1 )
710 onThreadError( "Error resuming thread" );
711 }
version(Posix)712 else version (Posix)
713 {
714 // NOTE: This is also set to true by thread_entryPoint, but set it
715 // here as well so the calling thread will see the isRunning
716 // state immediately.
717 atomicStore!(MemoryOrder.raw)(m_isRunning, true);
718 scope( failure ) atomicStore!(MemoryOrder.raw)(m_isRunning, false);
719
720 version (Shared)
721 {
722 import rt.sections;
723 auto libs = pinLoadedLibraries();
724 auto ps = cast(void**).malloc(2 * size_t.sizeof);
725 if (ps is null) onOutOfMemoryError();
726 ps[0] = cast(void*)this;
727 ps[1] = cast(void*)libs;
728 if ( pthread_create( &m_addr, &attr, &thread_entryPoint, ps ) != 0 )
729 {
730 unpinLoadedLibraries(libs);
731 .free(ps);
732 onThreadError( "Error creating thread" );
733 }
734 }
735 else
736 {
737 if ( pthread_create( &m_addr, &attr, &thread_entryPoint, cast(void*) this ) != 0 )
738 onThreadError( "Error creating thread" );
739 }
740 }
version(Darwin)741 version (Darwin)
742 {
743 m_tmach = pthread_mach_thread_np( m_addr );
744 if ( m_tmach == m_tmach.init )
745 onThreadError( "Error creating thread" );
746 }
747
748 return this;
749 }
750 }
751
752 /**
753 * Waits for this thread to complete. If the thread terminated as the
754 * result of an unhandled exception, this exception will be rethrown.
755 *
756 * Params:
757 * rethrow = Rethrow any unhandled exception which may have caused this
758 * thread to terminate.
759 *
760 * Throws:
761 * ThreadException if the operation fails.
762 * Any exception not handled by the joined thread.
763 *
764 * Returns:
765 * Any exception not handled by this thread if rethrow = false, null
766 * otherwise.
767 */
768 final Throwable join( bool rethrow = true )
769 {
version(Windows)770 version (Windows)
771 {
772 if ( WaitForSingleObject( m_hndl, INFINITE ) != WAIT_OBJECT_0 )
773 throw new ThreadException( "Unable to join thread" );
774 // NOTE: m_addr must be cleared before m_hndl is closed to avoid
775 // a race condition with isRunning. The operation is done
776 // with atomicStore to prevent compiler reordering.
777 atomicStore!(MemoryOrder.raw)(*cast(shared)&m_addr, m_addr.init);
778 CloseHandle( m_hndl );
779 m_hndl = m_hndl.init;
780 }
version(Posix)781 else version (Posix)
782 {
783 if ( pthread_join( m_addr, null ) != 0 )
784 throw new ThreadException( "Unable to join thread" );
785 // NOTE: pthread_join acts as a substitute for pthread_detach,
786 // which is normally called by the dtor. Setting m_addr
787 // to zero ensures that pthread_detach will not be called
788 // on object destruction.
789 m_addr = m_addr.init;
790 }
791 if ( m_unhandled )
792 {
793 if ( rethrow )
794 throw m_unhandled;
795 return m_unhandled;
796 }
797 return null;
798 }
799
800
801 ///////////////////////////////////////////////////////////////////////////
802 // General Properties
803 ///////////////////////////////////////////////////////////////////////////
804
805
806 /**
807 * Gets the OS identifier for this thread.
808 *
809 * Returns:
810 * If the thread hasn't been started yet, returns $(LREF ThreadID)$(D.init).
811 * Otherwise, returns the result of $(D GetCurrentThreadId) on Windows,
812 * and $(D pthread_self) on POSIX.
813 *
814 * The value is unique for the current process.
815 */
id()816 final @property ThreadID id() @safe @nogc
817 {
818 synchronized( this )
819 {
820 return m_addr;
821 }
822 }
823
824
825 /**
826 * Gets the user-readable label for this thread.
827 *
828 * Returns:
829 * The name of this thread.
830 */
name()831 final @property string name() @safe @nogc
832 {
833 synchronized( this )
834 {
835 return m_name;
836 }
837 }
838
839
840 /**
841 * Sets the user-readable label for this thread.
842 *
843 * Params:
844 * val = The new name of this thread.
845 */
name(string val)846 final @property void name( string val ) @safe @nogc
847 {
848 synchronized( this )
849 {
850 m_name = val;
851 }
852 }
853
854
855 /**
856 * Gets the daemon status for this thread. While the runtime will wait for
857 * all normal threads to complete before tearing down the process, daemon
858 * threads are effectively ignored and thus will not prevent the process
859 * from terminating. In effect, daemon threads will be terminated
860 * automatically by the OS when the process exits.
861 *
862 * Returns:
863 * true if this is a daemon thread.
864 */
isDaemon()865 final @property bool isDaemon() @safe @nogc
866 {
867 synchronized( this )
868 {
869 return m_isDaemon;
870 }
871 }
872
873
874 /**
875 * Sets the daemon status for this thread. While the runtime will wait for
876 * all normal threads to complete before tearing down the process, daemon
877 * threads are effectively ignored and thus will not prevent the process
878 * from terminating. In effect, daemon threads will be terminated
879 * automatically by the OS when the process exits.
880 *
881 * Params:
882 * val = The new daemon status for this thread.
883 */
isDaemon(bool val)884 final @property void isDaemon( bool val ) @safe @nogc
885 {
886 synchronized( this )
887 {
888 m_isDaemon = val;
889 }
890 }
891
892
893 /**
894 * Tests whether this thread is running.
895 *
896 * Returns:
897 * true if the thread is running, false if not.
898 */
isRunning()899 final @property bool isRunning() nothrow @nogc
900 {
901 if ( m_addr == m_addr.init )
902 {
903 return false;
904 }
905
906 version (Windows)
907 {
908 uint ecode = 0;
909 GetExitCodeThread( m_hndl, &ecode );
910 return ecode == STILL_ACTIVE;
911 }
912 else version (Posix)
913 {
914 return atomicLoad(m_isRunning);
915 }
916 }
917
918
919 ///////////////////////////////////////////////////////////////////////////
920 // Thread Priority Actions
921 ///////////////////////////////////////////////////////////////////////////
922
version(Windows)923 version (Windows)
924 {
925 @property static int PRIORITY_MIN() @nogc nothrow pure @safe
926 {
927 return THREAD_PRIORITY_IDLE;
928 }
929
930 @property static const(int) PRIORITY_MAX() @nogc nothrow pure @safe
931 {
932 return THREAD_PRIORITY_TIME_CRITICAL;
933 }
934
935 @property static int PRIORITY_DEFAULT() @nogc nothrow pure @safe
936 {
937 return THREAD_PRIORITY_NORMAL;
938 }
939 }
940 else
941 {
942 private struct Priority
943 {
944 int PRIORITY_MIN = int.min;
945 int PRIORITY_DEFAULT = int.min;
946 int PRIORITY_MAX = int.min;
947 }
948
949 /*
950 Lazily loads one of the members stored in a hidden global variable of
951 type `Priority`. Upon the first access of either member, the entire
952 `Priority` structure is initialized. Multiple initializations from
953 different threads calling this function are tolerated.
954
955 `which` must be one of `PRIORITY_MIN`, `PRIORITY_DEFAULT`,
956 `PRIORITY_MAX`.
957 */
loadGlobal(string which)958 private static int loadGlobal(string which)()
959 {
960 static shared Priority cache;
961 auto local = atomicLoad(mixin("cache." ~ which));
962 if (local != local.min) return local;
963 // There will be benign races
964 cache = loadPriorities;
965 return atomicLoad(mixin("cache." ~ which));
966 }
967
968 /*
969 Loads all priorities and returns them as a `Priority` structure. This
970 function is thread-neutral.
971 */
loadPriorities()972 private static Priority loadPriorities() @nogc nothrow @trusted
973 {
974 Priority result;
975 version (Solaris)
976 {
977 pcparms_t pcParms;
978 pcinfo_t pcInfo;
979
980 pcParms.pc_cid = PC_CLNULL;
981 if (priocntl(idtype_t.P_PID, P_MYID, PC_GETPARMS, &pcParms) == -1)
982 assert( 0, "Unable to get scheduling class" );
983
984 pcInfo.pc_cid = pcParms.pc_cid;
985 // PC_GETCLINFO ignores the first two args, use dummy values
986 if (priocntl(idtype_t.P_PID, 0, PC_GETCLINFO, &pcInfo) == -1)
987 assert( 0, "Unable to get scheduling class info" );
988
989 pri_t* clparms = cast(pri_t*)&pcParms.pc_clparms;
990 pri_t* clinfo = cast(pri_t*)&pcInfo.pc_clinfo;
991
992 result.PRIORITY_MAX = clparms[0];
993
994 if (pcInfo.pc_clname == "RT")
995 {
996 m_isRTClass = true;
997
998 // For RT class, just assume it can't be changed
999 result.PRIORITY_MIN = clparms[0];
1000 result.PRIORITY_DEFAULT = clparms[0];
1001 }
1002 else
1003 {
1004 m_isRTClass = false;
1005
1006 // For all other scheduling classes, there are
1007 // two key values -- uprilim and maxupri.
1008 // maxupri is the maximum possible priority defined
1009 // for the scheduling class, and valid priorities
1010 // range are in [-maxupri, maxupri].
1011 //
1012 // However, uprilim is an upper limit that the
1013 // current thread can set for the current scheduling
1014 // class, which can be less than maxupri. As such,
1015 // use this value for priorityMax since this is
1016 // the effective maximum.
1017
1018 // maxupri
1019 result.PRIORITY_MIN = -clinfo[0];
1020 // by definition
1021 result.PRIORITY_DEFAULT = 0;
1022 }
1023 }
1024 else version (Posix)
1025 {
1026 int policy;
1027 sched_param param;
1028 pthread_getschedparam( pthread_self(), &policy, ¶m ) == 0
1029 || assert(0, "Internal error in pthread_getschedparam");
1030
1031 result.PRIORITY_MIN = sched_get_priority_min( policy );
1032 result.PRIORITY_MIN != -1
1033 || assert(0, "Internal error in sched_get_priority_min");
1034 result.PRIORITY_DEFAULT = param.sched_priority;
1035 result.PRIORITY_MAX = sched_get_priority_max( policy );
1036 result.PRIORITY_MAX != -1 ||
1037 assert(0, "Internal error in sched_get_priority_max");
1038 }
1039 else
1040 {
1041 static assert(0, "Your code here.");
1042 }
1043 return result;
1044 }
1045
1046 /**
1047 * The minimum scheduling priority that may be set for a thread. On
1048 * systems where multiple scheduling policies are defined, this value
1049 * represents the minimum valid priority for the scheduling policy of
1050 * the process.
1051 */
PRIORITY_MIN()1052 @property static int PRIORITY_MIN() @nogc nothrow pure @trusted
1053 {
1054 return (cast(int function() @nogc nothrow pure @safe)
1055 &loadGlobal!"PRIORITY_MIN")();
1056 }
1057
1058 /**
1059 * The maximum scheduling priority that may be set for a thread. On
1060 * systems where multiple scheduling policies are defined, this value
1061 * represents the maximum valid priority for the scheduling policy of
1062 * the process.
1063 */
PRIORITY_MAX()1064 @property static const(int) PRIORITY_MAX() @nogc nothrow pure @trusted
1065 {
1066 return (cast(int function() @nogc nothrow pure @safe)
1067 &loadGlobal!"PRIORITY_MAX")();
1068 }
1069
1070 /**
1071 * The default scheduling priority that is set for a thread. On
1072 * systems where multiple scheduling policies are defined, this value
1073 * represents the default priority for the scheduling policy of
1074 * the process.
1075 */
PRIORITY_DEFAULT()1076 @property static int PRIORITY_DEFAULT() @nogc nothrow pure @trusted
1077 {
1078 return (cast(int function() @nogc nothrow pure @safe)
1079 &loadGlobal!"PRIORITY_DEFAULT")();
1080 }
1081 }
1082
version(NetBSD)1083 version (NetBSD)
1084 {
1085 //NetBSD does not support priority for default policy
1086 // and it is not possible change policy without root access
1087 int fakePriority = int.max;
1088 }
1089
1090 /**
1091 * Gets the scheduling priority for the associated thread.
1092 *
1093 * Note: Getting the priority of a thread that already terminated
1094 * might return the default priority.
1095 *
1096 * Returns:
1097 * The scheduling priority of this thread.
1098 */
priority()1099 final @property int priority()
1100 {
1101 version (Windows)
1102 {
1103 return GetThreadPriority( m_hndl );
1104 }
1105 else version (NetBSD)
1106 {
1107 return fakePriority==int.max? PRIORITY_DEFAULT : fakePriority;
1108 }
1109 else version (Posix)
1110 {
1111 int policy;
1112 sched_param param;
1113
1114 if (auto err = pthread_getschedparam(m_addr, &policy, ¶m))
1115 {
1116 // ignore error if thread is not running => Bugzilla 8960
1117 if (!atomicLoad(m_isRunning)) return PRIORITY_DEFAULT;
1118 throw new ThreadException("Unable to get thread priority");
1119 }
1120 return param.sched_priority;
1121 }
1122 }
1123
1124
1125 /**
1126 * Sets the scheduling priority for the associated thread.
1127 *
1128 * Note: Setting the priority of a thread that already terminated
1129 * might have no effect.
1130 *
1131 * Params:
1132 * val = The new scheduling priority of this thread.
1133 */
priority(int val)1134 final @property void priority( int val )
1135 in
1136 {
1137 assert(val >= PRIORITY_MIN);
1138 assert(val <= PRIORITY_MAX);
1139 }
1140 body
1141 {
version(Windows)1142 version (Windows)
1143 {
1144 if ( !SetThreadPriority( m_hndl, val ) )
1145 throw new ThreadException( "Unable to set thread priority" );
1146 }
version(Solaris)1147 else version (Solaris)
1148 {
1149 // the pthread_setschedprio(3c) and pthread_setschedparam functions
1150 // are broken for the default (TS / time sharing) scheduling class.
1151 // instead, we use priocntl(2) which gives us the desired behavior.
1152
1153 // We hardcode the min and max priorities to the current value
1154 // so this is a no-op for RT threads.
1155 if (m_isRTClass)
1156 return;
1157
1158 pcparms_t pcparm;
1159
1160 pcparm.pc_cid = PC_CLNULL;
1161 if (priocntl(idtype_t.P_LWPID, P_MYID, PC_GETPARMS, &pcparm) == -1)
1162 throw new ThreadException( "Unable to get scheduling class" );
1163
1164 pri_t* clparms = cast(pri_t*)&pcparm.pc_clparms;
1165
1166 // clparms is filled in by the PC_GETPARMS call, only necessary
1167 // to adjust the element that contains the thread priority
1168 clparms[1] = cast(pri_t) val;
1169
1170 if (priocntl(idtype_t.P_LWPID, P_MYID, PC_SETPARMS, &pcparm) == -1)
1171 throw new ThreadException( "Unable to set scheduling class" );
1172 }
version(NetBSD)1173 else version (NetBSD)
1174 {
1175 fakePriority = val;
1176 }
version(Posix)1177 else version (Posix)
1178 {
1179 static if (__traits(compiles, pthread_setschedprio))
1180 {
1181 if (auto err = pthread_setschedprio(m_addr, val))
1182 {
1183 // ignore error if thread is not running => Bugzilla 8960
1184 if (!atomicLoad(m_isRunning)) return;
1185 throw new ThreadException("Unable to set thread priority");
1186 }
1187 }
1188 else
1189 {
1190 // NOTE: pthread_setschedprio is not implemented on Darwin, FreeBSD or DragonFlyBSD, so use
1191 // the more complicated get/set sequence below.
1192 int policy;
1193 sched_param param;
1194
1195 if (auto err = pthread_getschedparam(m_addr, &policy, ¶m))
1196 {
1197 // ignore error if thread is not running => Bugzilla 8960
1198 if (!atomicLoad(m_isRunning)) return;
1199 throw new ThreadException("Unable to set thread priority");
1200 }
1201 param.sched_priority = val;
1202 if (auto err = pthread_setschedparam(m_addr, policy, ¶m))
1203 {
1204 // ignore error if thread is not running => Bugzilla 8960
1205 if (!atomicLoad(m_isRunning)) return;
1206 throw new ThreadException("Unable to set thread priority");
1207 }
1208 }
1209 }
1210 }
1211
1212
1213 unittest
1214 {
1215 auto thr = Thread.getThis();
1216 immutable prio = thr.priority;
1217 scope (exit) thr.priority = prio;
1218
1219 assert(prio == PRIORITY_DEFAULT);
1220 assert(prio >= PRIORITY_MIN && prio <= PRIORITY_MAX);
1221 thr.priority = PRIORITY_MIN;
1222 assert(thr.priority == PRIORITY_MIN);
1223 thr.priority = PRIORITY_MAX;
1224 assert(thr.priority == PRIORITY_MAX);
1225 }
1226
1227 unittest // Bugzilla 8960
1228 {
1229 import core.sync.semaphore;
1230
1231 auto thr = new Thread({});
1232 thr.start();
1233 Thread.sleep(1.msecs); // wait a little so the thread likely has finished
1234 thr.priority = PRIORITY_MAX; // setting priority doesn't cause error
1235 auto prio = thr.priority; // getting priority doesn't cause error
1236 assert(prio >= PRIORITY_MIN && prio <= PRIORITY_MAX);
1237 }
1238
1239 ///////////////////////////////////////////////////////////////////////////
1240 // Actions on Calling Thread
1241 ///////////////////////////////////////////////////////////////////////////
1242
1243
1244 /**
1245 * Suspends the calling thread for at least the supplied period. This may
1246 * result in multiple OS calls if period is greater than the maximum sleep
1247 * duration supported by the operating system.
1248 *
1249 * Params:
1250 * val = The minimum duration the calling thread should be suspended.
1251 *
1252 * In:
1253 * period must be non-negative.
1254 *
1255 * Example:
1256 * ------------------------------------------------------------------------
1257 *
1258 * Thread.sleep( dur!("msecs")( 50 ) ); // sleep for 50 milliseconds
1259 * Thread.sleep( dur!("seconds")( 5 ) ); // sleep for 5 seconds
1260 *
1261 * ------------------------------------------------------------------------
1262 */
sleep(Duration val)1263 static void sleep( Duration val ) @nogc nothrow
1264 in
1265 {
1266 assert( !val.isNegative );
1267 }
1268 body
1269 {
version(Windows)1270 version (Windows)
1271 {
1272 auto maxSleepMillis = dur!("msecs")( uint.max - 1 );
1273
1274 // avoid a non-zero time to be round down to 0
1275 if ( val > dur!"msecs"( 0 ) && val < dur!"msecs"( 1 ) )
1276 val = dur!"msecs"( 1 );
1277
1278 // NOTE: In instances where all other threads in the process have a
1279 // lower priority than the current thread, the current thread
1280 // will not yield with a sleep time of zero. However, unlike
1281 // yield(), the user is not asking for a yield to occur but
1282 // only for execution to suspend for the requested interval.
1283 // Therefore, expected performance may not be met if a yield
1284 // is forced upon the user.
1285 while ( val > maxSleepMillis )
1286 {
1287 Sleep( cast(uint)
1288 maxSleepMillis.total!"msecs" );
1289 val -= maxSleepMillis;
1290 }
1291 Sleep( cast(uint) val.total!"msecs" );
1292 }
version(Posix)1293 else version (Posix)
1294 {
1295 timespec tin = void;
1296 timespec tout = void;
1297
1298 val.split!("seconds", "nsecs")(tin.tv_sec, tin.tv_nsec);
1299 if ( val.total!"seconds" > tin.tv_sec.max )
1300 tin.tv_sec = tin.tv_sec.max;
1301 while ( true )
1302 {
1303 if ( !nanosleep( &tin, &tout ) )
1304 return;
1305 if ( errno != EINTR )
1306 assert(0, "Unable to sleep for the specified duration");
1307 tin = tout;
1308 }
1309 }
1310 }
1311
1312
1313 /**
1314 * Forces a context switch to occur away from the calling thread.
1315 */
yield()1316 static void yield() @nogc nothrow
1317 {
1318 version (Windows)
1319 SwitchToThread();
1320 else version (Posix)
1321 sched_yield();
1322 }
1323
1324
1325 ///////////////////////////////////////////////////////////////////////////
1326 // Thread Accessors
1327 ///////////////////////////////////////////////////////////////////////////
1328
1329 /**
1330 * Provides a reference to the calling thread.
1331 *
1332 * Returns:
1333 * The thread object representing the calling thread. The result of
1334 * deleting this object is undefined. If the current thread is not
1335 * attached to the runtime, a null reference is returned.
1336 */
getThis()1337 static Thread getThis() @safe nothrow @nogc
1338 {
1339 // NOTE: This function may not be called until thread_init has
1340 // completed. See thread_suspendAll for more information
1341 // on why this might occur.
1342 return sm_this;
1343 }
1344
1345
1346 /**
1347 * Provides a list of all threads currently being tracked by the system.
1348 * Note that threads in the returned array might no longer run (see
1349 * $(D Thread.)$(LREF isRunning)).
1350 *
1351 * Returns:
1352 * An array containing references to all threads currently being
1353 * tracked by the system. The result of deleting any contained
1354 * objects is undefined.
1355 */
getAll()1356 static Thread[] getAll()
1357 {
1358 static void resize(ref Thread[] buf, size_t nlen)
1359 {
1360 buf.length = nlen;
1361 }
1362 return getAllImpl!resize();
1363 }
1364
1365
1366 /**
1367 * Operates on all threads currently being tracked by the system. The
1368 * result of deleting any Thread object is undefined.
1369 * Note that threads passed to the callback might no longer run (see
1370 * $(D Thread.)$(LREF isRunning)).
1371 *
1372 * Params:
1373 * dg = The supplied code as a delegate.
1374 *
1375 * Returns:
1376 * Zero if all elemented are visited, nonzero if not.
1377 */
opApply(scope int delegate (ref Thread)dg)1378 static int opApply(scope int delegate(ref Thread) dg)
1379 {
1380 import core.stdc.stdlib : free, realloc;
1381
1382 static void resize(ref Thread[] buf, size_t nlen)
1383 {
1384 buf = (cast(Thread*)realloc(buf.ptr, nlen * Thread.sizeof))[0 .. nlen];
1385 }
1386 auto buf = getAllImpl!resize;
1387 scope(exit) if (buf.ptr) free(buf.ptr);
1388
1389 foreach (t; buf)
1390 {
1391 if (auto res = dg(t))
1392 return res;
1393 }
1394 return 0;
1395 }
1396
1397 unittest
1398 {
1399 auto t1 = new Thread({
1400 foreach (_; 0 .. 20)
1401 Thread.getAll;
1402 }).start;
1403 auto t2 = new Thread({
1404 foreach (_; 0 .. 20)
1405 GC.collect;
1406 }).start;
1407 t1.join();
1408 t2.join();
1409 }
1410
getAllImpl(alias resize)1411 private static Thread[] getAllImpl(alias resize)()
1412 {
1413 import core.atomic;
1414
1415 Thread[] buf;
1416 while (true)
1417 {
1418 immutable len = atomicLoad!(MemoryOrder.raw)(*cast(shared)&sm_tlen);
1419 resize(buf, len);
1420 assert(buf.length == len);
1421 synchronized (slock)
1422 {
1423 if (len == sm_tlen)
1424 {
1425 size_t pos;
1426 for (Thread t = sm_tbeg; t; t = t.next)
1427 buf[pos++] = t;
1428 return buf;
1429 }
1430 }
1431 }
1432 }
1433
1434 ///////////////////////////////////////////////////////////////////////////
1435 // Stuff That Should Go Away
1436 ///////////////////////////////////////////////////////////////////////////
1437
1438
1439 private:
1440 //
1441 // Initializes a thread object which has no associated executable function.
1442 // This is used for the main thread initialized in thread_init().
1443 //
1444 this(size_t sz = 0) @safe pure nothrow @nogc
1445 {
1446 if (sz)
1447 {
version(Posix)1448 version (Posix)
1449 {
1450 // stack size must be a multiple of PAGESIZE
1451 sz += PAGESIZE - 1;
1452 sz -= sz % PAGESIZE;
1453 // and at least PTHREAD_STACK_MIN
1454 if (PTHREAD_STACK_MIN > sz)
1455 sz = PTHREAD_STACK_MIN;
1456 }
1457 m_sz = sz;
1458 }
1459 m_call = Call.NO;
1460 m_curr = &m_main;
1461 }
1462
1463
1464 //
1465 // Thread entry point. Invokes the function or delegate passed on
1466 // construction (if any).
1467 //
run()1468 final void run()
1469 {
1470 switch ( m_call )
1471 {
1472 case Call.FN:
1473 m_fn();
1474 break;
1475 case Call.DG:
1476 m_dg();
1477 break;
1478 default:
1479 break;
1480 }
1481 }
1482
1483
1484 private:
1485 //
1486 // The type of routine passed on thread construction.
1487 //
1488 enum Call
1489 {
1490 NO,
1491 FN,
1492 DG
1493 }
1494
1495
1496 //
1497 // Standard types
1498 //
version(Windows)1499 version (Windows)
1500 {
1501 alias TLSKey = uint;
1502 }
version(Posix)1503 else version (Posix)
1504 {
1505 alias TLSKey = pthread_key_t;
1506 }
1507
1508
1509 //
1510 // Local storage
1511 //
1512 static Thread sm_this;
1513
1514
1515 //
1516 // Main process thread
1517 //
1518 __gshared Thread sm_main;
1519
version(FreeBSD)1520 version (FreeBSD)
1521 {
1522 // set when suspend failed and should be retried, see Issue 13416
1523 shared bool m_suspendagain;
1524 }
1525
1526
1527 //
1528 // Standard thread data
1529 //
version(Windows)1530 version (Windows)
1531 {
1532 HANDLE m_hndl;
1533 }
version(Darwin)1534 else version (Darwin)
1535 {
1536 mach_port_t m_tmach;
1537 }
1538 ThreadID m_addr;
1539 Call m_call;
1540 string m_name;
1541 union
1542 {
1543 void function() m_fn;
1544 void delegate() m_dg;
1545 }
1546 size_t m_sz;
version(Posix)1547 version (Posix)
1548 {
1549 shared bool m_isRunning;
1550 }
1551 bool m_isDaemon;
1552 bool m_isInCriticalRegion;
1553 Throwable m_unhandled;
1554
version(Solaris)1555 version (Solaris)
1556 {
1557 __gshared bool m_isRTClass;
1558 }
1559
1560 private:
1561 ///////////////////////////////////////////////////////////////////////////
1562 // Storage of Active Thread
1563 ///////////////////////////////////////////////////////////////////////////
1564
1565
1566 //
1567 // Sets a thread-local reference to the current thread object.
1568 //
setThis(Thread t)1569 static void setThis( Thread t ) nothrow @nogc
1570 {
1571 sm_this = t;
1572 }
1573
1574
1575 private:
1576 ///////////////////////////////////////////////////////////////////////////
1577 // Thread Context and GC Scanning Support
1578 ///////////////////////////////////////////////////////////////////////////
1579
1580
pushContext(Context * c)1581 final void pushContext( Context* c ) nothrow @nogc
1582 in
1583 {
1584 assert( !c.within );
1585 }
1586 body
1587 {
1588 m_curr.ehContext = swapContext(c.ehContext);
1589 c.within = m_curr;
1590 m_curr = c;
1591 }
1592
1593
popContext()1594 final void popContext() nothrow @nogc
1595 in
1596 {
1597 assert( m_curr && m_curr.within );
1598 }
1599 body
1600 {
1601 Context* c = m_curr;
1602 m_curr = c.within;
1603 c.ehContext = swapContext(m_curr.ehContext);
1604 c.within = null;
1605 }
1606
1607
topContext()1608 final Context* topContext() nothrow @nogc
1609 in
1610 {
1611 assert( m_curr );
1612 }
1613 body
1614 {
1615 return m_curr;
1616 }
1617
1618
1619 static struct Context
1620 {
1621 void* bstack,
1622 tstack;
1623
1624 /// Slot for the EH implementation to keep some state for each stack
1625 /// (will be necessary for exception chaining, etc.). Opaque as far as
1626 /// we are concerned here.
1627 void* ehContext;
1628
1629 Context* within;
1630 Context* next,
1631 prev;
1632 }
1633
1634
1635 Context m_main;
1636 Context* m_curr;
1637 bool m_lock;
1638 void* m_tlsgcdata;
1639
version(Windows)1640 version (Windows)
1641 {
1642 version (X86)
1643 {
1644 uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
1645 }
1646 else version (X86_64)
1647 {
1648 ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
1649 // r8,r9,r10,r11,r12,r13,r14,r15
1650 }
1651 else
1652 {
1653 static assert(false, "Architecture not supported." );
1654 }
1655 }
version(Darwin)1656 else version (Darwin)
1657 {
1658 version (X86)
1659 {
1660 uint[8] m_reg; // edi,esi,ebp,esp,ebx,edx,ecx,eax
1661 }
1662 else version (X86_64)
1663 {
1664 ulong[16] m_reg; // rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax
1665 // r8,r9,r10,r11,r12,r13,r14,r15
1666 }
1667 else
1668 {
1669 static assert(false, "Architecture not supported." );
1670 }
1671 }
1672
1673
1674 private:
1675 ///////////////////////////////////////////////////////////////////////////
1676 // GC Scanning Support
1677 ///////////////////////////////////////////////////////////////////////////
1678
1679
1680 // NOTE: The GC scanning process works like so:
1681 //
1682 // 1. Suspend all threads.
1683 // 2. Scan the stacks of all suspended threads for roots.
1684 // 3. Resume all threads.
1685 //
1686 // Step 1 and 3 require a list of all threads in the system, while
1687 // step 2 requires a list of all thread stacks (each represented by
1688 // a Context struct). Traditionally, there was one stack per thread
1689 // and the Context structs were not necessary. However, Fibers have
1690 // changed things so that each thread has its own 'main' stack plus
1691 // an arbitrary number of nested stacks (normally referenced via
1692 // m_curr). Also, there may be 'free-floating' stacks in the system,
1693 // which are Fibers that are not currently executing on any specific
1694 // thread but are still being processed and still contain valid
1695 // roots.
1696 //
1697 // To support all of this, the Context struct has been created to
1698 // represent a stack range, and a global list of Context structs has
1699 // been added to enable scanning of these stack ranges. The lifetime
1700 // (and presence in the Context list) of a thread's 'main' stack will
1701 // be equivalent to the thread's lifetime. So the Ccontext will be
1702 // added to the list on thread entry, and removed from the list on
1703 // thread exit (which is essentially the same as the presence of a
1704 // Thread object in its own global list). The lifetime of a Fiber's
1705 // context, however, will be tied to the lifetime of the Fiber object
1706 // itself, and Fibers are expected to add/remove their Context struct
1707 // on construction/deletion.
1708
1709
1710 //
1711 // All use of the global thread lists/array should synchronize on this lock.
1712 //
1713 // Careful as the GC acquires this lock after the GC lock to suspend all
1714 // threads any GC usage with slock held can result in a deadlock through
1715 // lock order inversion.
slock()1716 @property static Mutex slock() nothrow @nogc
1717 {
1718 return cast(Mutex)_slock.ptr;
1719 }
1720
criticalRegionLock()1721 @property static Mutex criticalRegionLock() nothrow @nogc
1722 {
1723 return cast(Mutex)_criticalRegionLock.ptr;
1724 }
1725
1726 __gshared align(mutexAlign) void[mutexClassInstanceSize] _slock;
1727 __gshared align(mutexAlign) void[mutexClassInstanceSize] _criticalRegionLock;
1728
initLocks()1729 static void initLocks()
1730 {
1731 _slock[] = typeid(Mutex).initializer[];
1732 (cast(Mutex)_slock.ptr).__ctor();
1733
1734 _criticalRegionLock[] = typeid(Mutex).initializer[];
1735 (cast(Mutex)_criticalRegionLock.ptr).__ctor();
1736 }
1737
termLocks()1738 static void termLocks()
1739 {
1740 (cast(Mutex)_slock.ptr).__dtor();
1741 (cast(Mutex)_criticalRegionLock.ptr).__dtor();
1742 }
1743
1744 __gshared Context* sm_cbeg;
1745
1746 __gshared Thread sm_tbeg;
1747 __gshared size_t sm_tlen;
1748
1749 // can't use rt.util.array in public code
1750 __gshared Thread* pAboutToStart;
1751 __gshared size_t nAboutToStart;
1752
1753 //
1754 // Used for ordering threads in the global thread list.
1755 //
1756 Thread prev;
1757 Thread next;
1758
1759
1760 ///////////////////////////////////////////////////////////////////////////
1761 // Global Context List Operations
1762 ///////////////////////////////////////////////////////////////////////////
1763
1764
1765 //
1766 // Add a context to the global context list.
1767 //
add(Context * c)1768 static void add( Context* c ) nothrow @nogc
1769 in
1770 {
1771 assert( c );
1772 assert( !c.next && !c.prev );
1773 }
1774 body
1775 {
1776 slock.lock_nothrow();
1777 scope(exit) slock.unlock_nothrow();
1778 assert(!suspendDepth); // must be 0 b/c it's only set with slock held
1779
1780 if (sm_cbeg)
1781 {
1782 c.next = sm_cbeg;
1783 sm_cbeg.prev = c;
1784 }
1785 sm_cbeg = c;
1786 }
1787
1788
1789 //
1790 // Remove a context from the global context list.
1791 //
1792 // This assumes slock being acquired. This isn't done here to
1793 // avoid double locking when called from remove(Thread)
remove(Context * c)1794 static void remove( Context* c ) nothrow @nogc
1795 in
1796 {
1797 assert( c );
1798 assert( c.next || c.prev );
1799 }
1800 body
1801 {
1802 if ( c.prev )
1803 c.prev.next = c.next;
1804 if ( c.next )
1805 c.next.prev = c.prev;
1806 if ( sm_cbeg == c )
1807 sm_cbeg = c.next;
1808 // NOTE: Don't null out c.next or c.prev because opApply currently
1809 // follows c.next after removing a node. This could be easily
1810 // addressed by simply returning the next node from this
1811 // function, however, a context should never be re-added to the
1812 // list anyway and having next and prev be non-null is a good way
1813 // to ensure that.
1814 }
1815
1816
1817 ///////////////////////////////////////////////////////////////////////////
1818 // Global Thread List Operations
1819 ///////////////////////////////////////////////////////////////////////////
1820
1821
1822 //
1823 // Add a thread to the global thread list.
1824 //
1825 static void add( Thread t, bool rmAboutToStart = true ) nothrow @nogc
1826 in
1827 {
1828 assert( t );
1829 assert( !t.next && !t.prev );
1830 }
1831 body
1832 {
1833 slock.lock_nothrow();
1834 scope(exit) slock.unlock_nothrow();
1835 assert(t.isRunning); // check this with slock to ensure pthread_create already returned
1836 assert(!suspendDepth); // must be 0 b/c it's only set with slock held
1837
1838 if (rmAboutToStart)
1839 {
1840 size_t idx = -1;
foreach(i,thr;pAboutToStart[0..nAboutToStart])1841 foreach (i, thr; pAboutToStart[0 .. nAboutToStart])
1842 {
1843 if (thr is t)
1844 {
1845 idx = i;
1846 break;
1847 }
1848 }
1849 assert(idx != -1);
1850 import core.stdc.string : memmove;
1851 memmove(pAboutToStart + idx, pAboutToStart + idx + 1, Thread.sizeof * (nAboutToStart - idx - 1));
1852 pAboutToStart =
1853 cast(Thread*)realloc(pAboutToStart, Thread.sizeof * --nAboutToStart);
1854 }
1855
1856 if (sm_tbeg)
1857 {
1858 t.next = sm_tbeg;
1859 sm_tbeg.prev = t;
1860 }
1861 sm_tbeg = t;
1862 ++sm_tlen;
1863 }
1864
1865
1866 //
1867 // Remove a thread from the global thread list.
1868 //
remove(Thread t)1869 static void remove( Thread t ) nothrow @nogc
1870 in
1871 {
1872 assert( t );
1873 }
1874 body
1875 {
1876 // Thread was already removed earlier, might happen b/c of thread_detachInstance
1877 if (!t.next && !t.prev)
1878 return;
1879 slock.lock_nothrow();
1880 {
1881 // NOTE: When a thread is removed from the global thread list its
1882 // main context is invalid and should be removed as well.
1883 // It is possible that t.m_curr could reference more
1884 // than just the main context if the thread exited abnormally
1885 // (if it was terminated), but we must assume that the user
1886 // retains a reference to them and that they may be re-used
1887 // elsewhere. Therefore, it is the responsibility of any
1888 // object that creates contexts to clean them up properly
1889 // when it is done with them.
1890 remove( &t.m_main );
1891
1892 if ( t.prev )
1893 t.prev.next = t.next;
1894 if ( t.next )
1895 t.next.prev = t.prev;
1896 if ( sm_tbeg is t )
1897 sm_tbeg = t.next;
1898 t.prev = t.next = null;
1899 --sm_tlen;
1900 }
1901 // NOTE: Don't null out t.next or t.prev because opApply currently
1902 // follows t.next after removing a node. This could be easily
1903 // addressed by simply returning the next node from this
1904 // function, however, a thread should never be re-added to the
1905 // list anyway and having next and prev be non-null is a good way
1906 // to ensure that.
1907 slock.unlock_nothrow();
1908 }
1909 }
1910
1911 ///
1912 unittest
1913 {
1914 class DerivedThread : Thread
1915 {
this()1916 this()
1917 {
1918 super(&run);
1919 }
1920
1921 private:
run()1922 void run()
1923 {
1924 // Derived thread running.
1925 }
1926 }
1927
threadFunc()1928 void threadFunc()
1929 {
1930 // Composed thread running.
1931 }
1932
1933 // create and start instances of each type
1934 auto derived = new DerivedThread().start();
1935 auto composed = new Thread(&threadFunc).start();
1936 new Thread({
1937 // Codes to run in the newly created thread.
1938 }).start();
1939 }
1940
1941 unittest
1942 {
1943 int x = 0;
1944
1945 new Thread(
1946 {
1947 x++;
1948 }).start().join();
1949 assert( x == 1 );
1950 }
1951
1952
1953 unittest
1954 {
1955 enum MSG = "Test message.";
1956 string caughtMsg;
1957
1958 try
1959 {
1960 new Thread(
1961 {
1962 throw new Exception( MSG );
1963 }).start().join();
1964 assert( false, "Expected rethrown exception." );
1965 }
catch(Throwable t)1966 catch ( Throwable t )
1967 {
1968 assert( t.msg == MSG );
1969 }
1970 }
1971
1972
1973 ///////////////////////////////////////////////////////////////////////////////
1974 // GC Support Routines
1975 ///////////////////////////////////////////////////////////////////////////////
1976
version(CoreDdoc)1977 version (CoreDdoc)
1978 {
1979 /**
1980 * Instruct the thread module, when initialized, to use a different set of
1981 * signals besides SIGUSR1 and SIGUSR2 for suspension and resumption of threads.
1982 * This function should be called at most once, prior to thread_init().
1983 * This function is Posix-only.
1984 */
1985 extern (C) void thread_setGCSignals(int suspendSignalNo, int resumeSignalNo) nothrow @nogc
1986 {
1987 }
1988 }
version(Posix)1989 else version (Posix)
1990 {
1991 extern (C) void thread_setGCSignals(int suspendSignalNo, int resumeSignalNo) nothrow @nogc
1992 in
1993 {
1994 assert(suspendSignalNumber == 0);
1995 assert(resumeSignalNumber == 0);
1996 assert(suspendSignalNo != 0);
1997 assert(resumeSignalNo != 0);
1998 }
1999 out
2000 {
2001 assert(suspendSignalNumber != 0);
2002 assert(resumeSignalNumber != 0);
2003 }
2004 body
2005 {
2006 suspendSignalNumber = suspendSignalNo;
2007 resumeSignalNumber = resumeSignalNo;
2008 }
2009 }
2010
version(Posix)2011 version (Posix)
2012 {
2013 __gshared int suspendSignalNumber;
2014 __gshared int resumeSignalNumber;
2015 }
2016
2017 /**
2018 * Initializes the thread module. This function must be called by the
2019 * garbage collector on startup and before any other thread routines
2020 * are called.
2021 */
thread_init()2022 extern (C) void thread_init()
2023 {
2024 // NOTE: If thread_init itself performs any allocations then the thread
2025 // routines reserved for garbage collector use may be called while
2026 // thread_init is being processed. However, since no memory should
2027 // exist to be scanned at this point, it is sufficient for these
2028 // functions to detect the condition and return immediately.
2029
2030 Thread.initLocks();
2031 // The Android VM runtime intercepts SIGUSR1 and apparently doesn't allow
2032 // its signal handler to run, so swap the two signals on Android, since
2033 // thread_resumeHandler does nothing.
2034 version (Android) thread_setGCSignals(SIGUSR2, SIGUSR1);
2035
2036 version (Darwin)
2037 {
2038 }
2039 else version (Posix)
2040 {
2041 if ( suspendSignalNumber == 0 )
2042 {
2043 suspendSignalNumber = SIGUSR1;
2044 }
2045
2046 if ( resumeSignalNumber == 0 )
2047 {
2048 resumeSignalNumber = SIGUSR2;
2049 }
2050
2051 int status;
2052 sigaction_t sigusr1 = void;
2053 sigaction_t sigusr2 = void;
2054
2055 // This is a quick way to zero-initialize the structs without using
2056 // memset or creating a link dependency on their static initializer.
2057 (cast(byte*) &sigusr1)[0 .. sigaction_t.sizeof] = 0;
2058 (cast(byte*) &sigusr2)[0 .. sigaction_t.sizeof] = 0;
2059
2060 // NOTE: SA_RESTART indicates that system calls should restart if they
2061 // are interrupted by a signal, but this is not available on all
2062 // Posix systems, even those that support multithreading.
2063 static if ( __traits( compiles, SA_RESTART ) )
2064 sigusr1.sa_flags = SA_RESTART;
2065 else
2066 sigusr1.sa_flags = 0;
2067 sigusr1.sa_handler = &thread_suspendHandler;
2068 // NOTE: We want to ignore all signals while in this handler, so fill
2069 // sa_mask to indicate this.
2070 status = sigfillset( &sigusr1.sa_mask );
2071 assert( status == 0 );
2072
2073 // NOTE: Since resumeSignalNumber should only be issued for threads within the
2074 // suspend handler, we don't want this signal to trigger a
2075 // restart.
2076 sigusr2.sa_flags = 0;
2077 sigusr2.sa_handler = &thread_resumeHandler;
2078 // NOTE: We want to ignore all signals while in this handler, so fill
2079 // sa_mask to indicate this.
2080 status = sigfillset( &sigusr2.sa_mask );
2081 assert( status == 0 );
2082
2083 status = sigaction( suspendSignalNumber, &sigusr1, null );
2084 assert( status == 0 );
2085
2086 status = sigaction( resumeSignalNumber, &sigusr2, null );
2087 assert( status == 0 );
2088
2089 status = sem_init( &suspendCount, 0, 0 );
2090 assert( status == 0 );
2091 }
2092 Thread.sm_main = thread_attachThis();
2093 }
2094
2095
2096 /**
2097 * Terminates the thread module. No other thread routine may be called
2098 * afterwards.
2099 */
thread_term()2100 extern (C) void thread_term()
2101 {
2102 assert(Thread.sm_tbeg && Thread.sm_tlen == 1);
2103 assert(!Thread.nAboutToStart);
2104 if (Thread.pAboutToStart) // in case realloc(p, 0) doesn't return null
2105 {
2106 free(Thread.pAboutToStart);
2107 Thread.pAboutToStart = null;
2108 }
2109 Thread.termLocks();
2110 }
2111
2112
2113 /**
2114 *
2115 */
thread_isMainThread()2116 extern (C) bool thread_isMainThread() nothrow @nogc
2117 {
2118 return Thread.getThis() is Thread.sm_main;
2119 }
2120
2121
2122 /**
2123 * Registers the calling thread for use with the D Runtime. If this routine
2124 * is called for a thread which is already registered, no action is performed.
2125 *
2126 * NOTE: This routine does not run thread-local static constructors when called.
2127 * If full functionality as a D thread is desired, the following function
2128 * must be called after thread_attachThis:
2129 *
2130 * extern (C) void rt_moduleTlsCtor();
2131 */
thread_attachThis()2132 extern (C) Thread thread_attachThis()
2133 {
2134 GC.disable(); scope(exit) GC.enable();
2135
2136 if (auto t = Thread.getThis())
2137 return t;
2138
2139 Thread thisThread = new Thread();
2140 Thread.Context* thisContext = &thisThread.m_main;
2141 assert( thisContext == thisThread.m_curr );
2142
2143 version (Windows)
2144 {
2145 thisThread.m_addr = GetCurrentThreadId();
2146 thisThread.m_hndl = GetCurrentThreadHandle();
2147 thisContext.bstack = getStackBottom();
2148 thisContext.tstack = thisContext.bstack;
2149 }
2150 else version (Posix)
2151 {
2152 thisThread.m_addr = pthread_self();
2153 thisContext.bstack = getStackBottom();
2154 thisContext.tstack = thisContext.bstack;
2155
2156 atomicStore!(MemoryOrder.raw)(thisThread.m_isRunning, true);
2157 }
2158 thisThread.m_isDaemon = true;
2159 thisThread.m_tlsgcdata = rt_tlsgc_init();
2160 Thread.setThis( thisThread );
2161
2162 version (Darwin)
2163 {
2164 thisThread.m_tmach = pthread_mach_thread_np( thisThread.m_addr );
2165 assert( thisThread.m_tmach != thisThread.m_tmach.init );
2166 }
2167
2168 Thread.add( thisThread, false );
2169 Thread.add( thisContext );
2170 if ( Thread.sm_main !is null )
2171 multiThreadedFlag = true;
2172 return thisThread;
2173 }
2174
2175
version(Windows)2176 version (Windows)
2177 {
2178 // NOTE: These calls are not safe on Posix systems that use signals to
2179 // perform garbage collection. The suspendHandler uses getThis()
2180 // to get the thread handle so getThis() must be a simple call.
2181 // Mutexes can't safely be acquired inside signal handlers, and
2182 // even if they could, the mutex needed (Thread.slock) is held by
2183 // thread_suspendAll(). So in short, these routines will remain
2184 // Windows-specific. If they are truly needed elsewhere, the
2185 // suspendHandler will need a way to call a version of getThis()
2186 // that only does the TLS lookup without the fancy fallback stuff.
2187
2188 /// ditto
2189 extern (C) Thread thread_attachByAddr( ThreadID addr )
2190 {
2191 return thread_attachByAddrB( addr, getThreadStackBottom( addr ) );
2192 }
2193
2194
2195 /// ditto
2196 extern (C) Thread thread_attachByAddrB( ThreadID addr, void* bstack )
2197 {
2198 GC.disable(); scope(exit) GC.enable();
2199
2200 if (auto t = thread_findByAddr(addr))
2201 return t;
2202
2203 Thread thisThread = new Thread();
2204 Thread.Context* thisContext = &thisThread.m_main;
2205 assert( thisContext == thisThread.m_curr );
2206
2207 thisThread.m_addr = addr;
2208 thisContext.bstack = bstack;
2209 thisContext.tstack = thisContext.bstack;
2210
2211 thisThread.m_isDaemon = true;
2212
2213 if ( addr == GetCurrentThreadId() )
2214 {
2215 thisThread.m_hndl = GetCurrentThreadHandle();
2216 thisThread.m_tlsgcdata = rt_tlsgc_init();
2217 Thread.setThis( thisThread );
2218 }
2219 else
2220 {
2221 thisThread.m_hndl = OpenThreadHandle( addr );
2222 impersonate_thread(addr,
2223 {
2224 thisThread.m_tlsgcdata = rt_tlsgc_init();
2225 Thread.setThis( thisThread );
2226 });
2227 }
2228
2229 Thread.add( thisThread, false );
2230 Thread.add( thisContext );
2231 if ( Thread.sm_main !is null )
2232 multiThreadedFlag = true;
2233 return thisThread;
2234 }
2235 }
2236
2237
2238 /**
2239 * Deregisters the calling thread from use with the runtime. If this routine
2240 * is called for a thread which is not registered, the result is undefined.
2241 *
2242 * NOTE: This routine does not run thread-local static destructors when called.
2243 * If full functionality as a D thread is desired, the following function
2244 * must be called after thread_detachThis, particularly if the thread is
2245 * being detached at some indeterminate time before program termination:
2246 *
2247 * $(D extern(C) void rt_moduleTlsDtor();)
2248 */
thread_detachThis()2249 extern (C) void thread_detachThis() nothrow @nogc
2250 {
2251 if (auto t = Thread.getThis())
2252 Thread.remove(t);
2253 }
2254
2255
2256 /**
2257 * Deregisters the given thread from use with the runtime. If this routine
2258 * is called for a thread which is not registered, the result is undefined.
2259 *
2260 * NOTE: This routine does not run thread-local static destructors when called.
2261 * If full functionality as a D thread is desired, the following function
2262 * must be called by the detached thread, particularly if the thread is
2263 * being detached at some indeterminate time before program termination:
2264 *
2265 * $(D extern(C) void rt_moduleTlsDtor();)
2266 */
thread_detachByAddr(ThreadID addr)2267 extern (C) void thread_detachByAddr( ThreadID addr )
2268 {
2269 if ( auto t = thread_findByAddr( addr ) )
2270 Thread.remove( t );
2271 }
2272
2273
2274 /// ditto
thread_detachInstance(Thread t)2275 extern (C) void thread_detachInstance( Thread t ) nothrow @nogc
2276 {
2277 Thread.remove( t );
2278 }
2279
2280
2281 unittest
2282 {
2283 import core.sync.semaphore;
2284 auto sem = new Semaphore();
2285
2286 auto t = new Thread(
2287 {
2288 sem.notify();
2289 Thread.sleep(100.msecs);
2290 }).start();
2291
2292 sem.wait(); // thread cannot be detached while being started
2293 thread_detachInstance(t);
2294 foreach (t2; Thread)
2295 assert(t !is t2);
2296 t.join();
2297 }
2298
2299
2300 /**
2301 * Search the list of all threads for a thread with the given thread identifier.
2302 *
2303 * Params:
2304 * addr = The thread identifier to search for.
2305 * Returns:
2306 * The thread object associated with the thread identifier, null if not found.
2307 */
thread_findByAddr(ThreadID addr)2308 static Thread thread_findByAddr( ThreadID addr )
2309 {
2310 Thread.slock.lock_nothrow();
2311 scope(exit) Thread.slock.unlock_nothrow();
2312
2313 // also return just spawned thread so that
2314 // DLL_THREAD_ATTACH knows it's a D thread
2315 foreach (t; Thread.pAboutToStart[0 .. Thread.nAboutToStart])
2316 if (t.m_addr == addr)
2317 return t;
2318
2319 foreach (t; Thread)
2320 if (t.m_addr == addr)
2321 return t;
2322
2323 return null;
2324 }
2325
2326
2327 /**
2328 * Sets the current thread to a specific reference. Only to be used
2329 * when dealing with externally-created threads (in e.g. C code).
2330 * The primary use of this function is when Thread.getThis() must
2331 * return a sensible value in, for example, TLS destructors. In
2332 * other words, don't touch this unless you know what you're doing.
2333 *
2334 * Params:
2335 * t = A reference to the current thread. May be null.
2336 */
thread_setThis(Thread t)2337 extern (C) void thread_setThis(Thread t) nothrow @nogc
2338 {
2339 Thread.setThis(t);
2340 }
2341
2342
2343 /**
2344 * Joins all non-daemon threads that are currently running. This is done by
2345 * performing successive scans through the thread list until a scan consists
2346 * of only daemon threads.
2347 */
thread_joinAll()2348 extern (C) void thread_joinAll()
2349 {
2350 Lagain:
2351 Thread.slock.lock_nothrow();
2352 // wait for just spawned threads
2353 if (Thread.nAboutToStart)
2354 {
2355 Thread.slock.unlock_nothrow();
2356 Thread.yield();
2357 goto Lagain;
2358 }
2359
2360 // join all non-daemon threads, the main thread is also a daemon
2361 auto t = Thread.sm_tbeg;
2362 while (t)
2363 {
2364 if (!t.isRunning)
2365 {
2366 auto tn = t.next;
2367 Thread.remove(t);
2368 t = tn;
2369 }
2370 else if (t.isDaemon)
2371 {
2372 t = t.next;
2373 }
2374 else
2375 {
2376 Thread.slock.unlock_nothrow();
2377 t.join(); // might rethrow
2378 goto Lagain; // must restart iteration b/c of unlock
2379 }
2380 }
2381 Thread.slock.unlock_nothrow();
2382 }
2383
2384
2385 /**
2386 * Performs intermediate shutdown of the thread module.
2387 */
~this()2388 shared static ~this()
2389 {
2390 // NOTE: The functionality related to garbage collection must be minimally
2391 // operable after this dtor completes. Therefore, only minimal
2392 // cleanup may occur.
2393 auto t = Thread.sm_tbeg;
2394 while (t)
2395 {
2396 auto tn = t.next;
2397 if (!t.isRunning)
2398 Thread.remove(t);
2399 t = tn;
2400 }
2401 }
2402
2403
2404 // Used for needLock below.
2405 private __gshared bool multiThreadedFlag = false;
2406
2407 version (PPC64) version = ExternStackShell;
2408
version(ExternStackShell)2409 version (ExternStackShell)
2410 {
2411 extern(D) public void callWithStackShell(scope void delegate(void* sp) nothrow fn) nothrow;
2412 }
2413 else
2414 {
2415 // Calls the given delegate, passing the current thread's stack pointer to it.
callWithStackShell(scope void delegate (void * sp)nothrow fn)2416 private void callWithStackShell(scope void delegate(void* sp) nothrow fn) nothrow
2417 in
2418 {
2419 assert(fn);
2420 }
2421 body
2422 {
2423 // The purpose of the 'shell' is to ensure all the registers get
2424 // put on the stack so they'll be scanned. We only need to push
2425 // the callee-save registers.
2426 void *sp = void;
2427
version(GNU)2428 version (GNU)
2429 {
2430 __builtin_unwind_init();
2431 sp = &sp;
2432 }
version(AsmX86_Posix)2433 else version (AsmX86_Posix)
2434 {
2435 size_t[3] regs = void;
2436 asm pure nothrow @nogc
2437 {
2438 mov [regs + 0 * 4], EBX;
2439 mov [regs + 1 * 4], ESI;
2440 mov [regs + 2 * 4], EDI;
2441
2442 mov sp[EBP], ESP;
2443 }
2444 }
version(AsmX86_Windows)2445 else version (AsmX86_Windows)
2446 {
2447 size_t[3] regs = void;
2448 asm pure nothrow @nogc
2449 {
2450 mov [regs + 0 * 4], EBX;
2451 mov [regs + 1 * 4], ESI;
2452 mov [regs + 2 * 4], EDI;
2453
2454 mov sp[EBP], ESP;
2455 }
2456 }
version(AsmX86_64_Posix)2457 else version (AsmX86_64_Posix)
2458 {
2459 size_t[5] regs = void;
2460 asm pure nothrow @nogc
2461 {
2462 mov [regs + 0 * 8], RBX;
2463 mov [regs + 1 * 8], R12;
2464 mov [regs + 2 * 8], R13;
2465 mov [regs + 3 * 8], R14;
2466 mov [regs + 4 * 8], R15;
2467
2468 mov sp[RBP], RSP;
2469 }
2470 }
version(AsmX86_64_Windows)2471 else version (AsmX86_64_Windows)
2472 {
2473 size_t[7] regs = void;
2474 asm pure nothrow @nogc
2475 {
2476 mov [regs + 0 * 8], RBX;
2477 mov [regs + 1 * 8], RSI;
2478 mov [regs + 2 * 8], RDI;
2479 mov [regs + 3 * 8], R12;
2480 mov [regs + 4 * 8], R13;
2481 mov [regs + 5 * 8], R14;
2482 mov [regs + 6 * 8], R15;
2483
2484 mov sp[RBP], RSP;
2485 }
2486 }
2487 else
2488 {
2489 static assert(false, "Architecture not supported.");
2490 }
2491
2492 fn(sp);
2493 }
2494 }
2495
2496 // Used for suspendAll/resumeAll below.
2497 private __gshared uint suspendDepth = 0;
2498
2499 /**
2500 * Suspend the specified thread and load stack and register information for
2501 * use by thread_scanAll. If the supplied thread is the calling thread,
2502 * stack and register information will be loaded but the thread will not
2503 * be suspended. If the suspend operation fails and the thread is not
2504 * running then it will be removed from the global thread list, otherwise
2505 * an exception will be thrown.
2506 *
2507 * Params:
2508 * t = The thread to suspend.
2509 *
2510 * Throws:
2511 * ThreadError if the suspend operation fails for a running thread.
2512 * Returns:
2513 * Whether the thread is now suspended (true) or terminated (false).
2514 */
suspend(Thread t)2515 private bool suspend( Thread t ) nothrow
2516 {
2517 Duration waittime = dur!"usecs"(10);
2518 Lagain:
2519 if (!t.isRunning)
2520 {
2521 Thread.remove(t);
2522 return false;
2523 }
2524 else if (t.m_isInCriticalRegion)
2525 {
2526 Thread.criticalRegionLock.unlock_nothrow();
2527 Thread.sleep(waittime);
2528 if (waittime < dur!"msecs"(10)) waittime *= 2;
2529 Thread.criticalRegionLock.lock_nothrow();
2530 goto Lagain;
2531 }
2532
2533 version (Windows)
2534 {
2535 if ( t.m_addr != GetCurrentThreadId() && SuspendThread( t.m_hndl ) == 0xFFFFFFFF )
2536 {
2537 if ( !t.isRunning )
2538 {
2539 Thread.remove( t );
2540 return false;
2541 }
2542 onThreadError( "Unable to suspend thread" );
2543 }
2544
2545 CONTEXT context = void;
2546 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
2547
2548 if ( !GetThreadContext( t.m_hndl, &context ) )
2549 onThreadError( "Unable to load thread context" );
2550 version (X86)
2551 {
2552 if ( !t.m_lock )
2553 t.m_curr.tstack = cast(void*) context.Esp;
2554 // eax,ebx,ecx,edx,edi,esi,ebp,esp
2555 t.m_reg[0] = context.Eax;
2556 t.m_reg[1] = context.Ebx;
2557 t.m_reg[2] = context.Ecx;
2558 t.m_reg[3] = context.Edx;
2559 t.m_reg[4] = context.Edi;
2560 t.m_reg[5] = context.Esi;
2561 t.m_reg[6] = context.Ebp;
2562 t.m_reg[7] = context.Esp;
2563 }
2564 else version (X86_64)
2565 {
2566 if ( !t.m_lock )
2567 t.m_curr.tstack = cast(void*) context.Rsp;
2568 // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp
2569 t.m_reg[0] = context.Rax;
2570 t.m_reg[1] = context.Rbx;
2571 t.m_reg[2] = context.Rcx;
2572 t.m_reg[3] = context.Rdx;
2573 t.m_reg[4] = context.Rdi;
2574 t.m_reg[5] = context.Rsi;
2575 t.m_reg[6] = context.Rbp;
2576 t.m_reg[7] = context.Rsp;
2577 // r8,r9,r10,r11,r12,r13,r14,r15
2578 t.m_reg[8] = context.R8;
2579 t.m_reg[9] = context.R9;
2580 t.m_reg[10] = context.R10;
2581 t.m_reg[11] = context.R11;
2582 t.m_reg[12] = context.R12;
2583 t.m_reg[13] = context.R13;
2584 t.m_reg[14] = context.R14;
2585 t.m_reg[15] = context.R15;
2586 }
2587 else
2588 {
2589 static assert(false, "Architecture not supported." );
2590 }
2591 }
2592 else version (Darwin)
2593 {
2594 if ( t.m_addr != pthread_self() && thread_suspend( t.m_tmach ) != KERN_SUCCESS )
2595 {
2596 if ( !t.isRunning )
2597 {
2598 Thread.remove( t );
2599 return false;
2600 }
2601 onThreadError( "Unable to suspend thread" );
2602 }
2603
2604 version (X86)
2605 {
2606 x86_thread_state32_t state = void;
2607 mach_msg_type_number_t count = x86_THREAD_STATE32_COUNT;
2608
2609 if ( thread_get_state( t.m_tmach, x86_THREAD_STATE32, &state, &count ) != KERN_SUCCESS )
2610 onThreadError( "Unable to load thread state" );
2611 if ( !t.m_lock )
2612 t.m_curr.tstack = cast(void*) state.esp;
2613 // eax,ebx,ecx,edx,edi,esi,ebp,esp
2614 t.m_reg[0] = state.eax;
2615 t.m_reg[1] = state.ebx;
2616 t.m_reg[2] = state.ecx;
2617 t.m_reg[3] = state.edx;
2618 t.m_reg[4] = state.edi;
2619 t.m_reg[5] = state.esi;
2620 t.m_reg[6] = state.ebp;
2621 t.m_reg[7] = state.esp;
2622 }
2623 else version (X86_64)
2624 {
2625 x86_thread_state64_t state = void;
2626 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
2627
2628 if ( thread_get_state( t.m_tmach, x86_THREAD_STATE64, &state, &count ) != KERN_SUCCESS )
2629 onThreadError( "Unable to load thread state" );
2630 if ( !t.m_lock )
2631 t.m_curr.tstack = cast(void*) state.rsp;
2632 // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp
2633 t.m_reg[0] = state.rax;
2634 t.m_reg[1] = state.rbx;
2635 t.m_reg[2] = state.rcx;
2636 t.m_reg[3] = state.rdx;
2637 t.m_reg[4] = state.rdi;
2638 t.m_reg[5] = state.rsi;
2639 t.m_reg[6] = state.rbp;
2640 t.m_reg[7] = state.rsp;
2641 // r8,r9,r10,r11,r12,r13,r14,r15
2642 t.m_reg[8] = state.r8;
2643 t.m_reg[9] = state.r9;
2644 t.m_reg[10] = state.r10;
2645 t.m_reg[11] = state.r11;
2646 t.m_reg[12] = state.r12;
2647 t.m_reg[13] = state.r13;
2648 t.m_reg[14] = state.r14;
2649 t.m_reg[15] = state.r15;
2650 }
2651 else
2652 {
2653 static assert(false, "Architecture not supported." );
2654 }
2655 }
2656 else version (Posix)
2657 {
2658 if ( t.m_addr != pthread_self() )
2659 {
2660 if ( pthread_kill( t.m_addr, suspendSignalNumber ) != 0 )
2661 {
2662 if ( !t.isRunning )
2663 {
2664 Thread.remove( t );
2665 return false;
2666 }
2667 onThreadError( "Unable to suspend thread" );
2668 }
2669 }
2670 else if ( !t.m_lock )
2671 {
2672 t.m_curr.tstack = getStackTop();
2673 }
2674 }
2675 return true;
2676 }
2677
2678 /**
2679 * Suspend all threads but the calling thread for "stop the world" garbage
2680 * collection runs. This function may be called multiple times, and must
2681 * be followed by a matching number of calls to thread_resumeAll before
2682 * processing is resumed.
2683 *
2684 * Throws:
2685 * ThreadError if the suspend operation fails for a running thread.
2686 */
thread_suspendAll()2687 extern (C) void thread_suspendAll() nothrow
2688 {
2689 // NOTE: We've got an odd chicken & egg problem here, because while the GC
2690 // is required to call thread_init before calling any other thread
2691 // routines, thread_init may allocate memory which could in turn
2692 // trigger a collection. Thus, thread_suspendAll, thread_scanAll,
2693 // and thread_resumeAll must be callable before thread_init
2694 // completes, with the assumption that no other GC memory has yet
2695 // been allocated by the system, and thus there is no risk of losing
2696 // data if the global thread list is empty. The check of
2697 // Thread.sm_tbeg below is done to ensure thread_init has completed,
2698 // and therefore that calling Thread.getThis will not result in an
2699 // error. For the short time when Thread.sm_tbeg is null, there is
2700 // no reason not to simply call the multithreaded code below, with
2701 // the expectation that the foreach loop will never be entered.
2702 if ( !multiThreadedFlag && Thread.sm_tbeg )
2703 {
2704 if ( ++suspendDepth == 1 )
2705 suspend( Thread.getThis() );
2706
2707 return;
2708 }
2709
2710 Thread.slock.lock_nothrow();
2711 {
2712 if ( ++suspendDepth > 1 )
2713 return;
2714
2715 Thread.criticalRegionLock.lock_nothrow();
2716 scope (exit) Thread.criticalRegionLock.unlock_nothrow();
2717 size_t cnt;
2718 auto t = Thread.sm_tbeg;
2719 while (t)
2720 {
2721 auto tn = t.next;
2722 if (suspend(t))
2723 ++cnt;
2724 t = tn;
2725 }
2726
2727 version (Darwin)
2728 {}
2729 else version (Posix)
2730 {
2731 // subtract own thread
2732 assert(cnt >= 1);
2733 --cnt;
2734 Lagain:
2735 // wait for semaphore notifications
2736 for (; cnt; --cnt)
2737 {
2738 while (sem_wait(&suspendCount) != 0)
2739 {
2740 if (errno != EINTR)
2741 onThreadError("Unable to wait for semaphore");
2742 errno = 0;
2743 }
2744 }
2745 version (FreeBSD)
2746 {
2747 // avoid deadlocks, see Issue 13416
2748 t = Thread.sm_tbeg;
2749 while (t)
2750 {
2751 auto tn = t.next;
2752 if (t.m_suspendagain && suspend(t))
2753 ++cnt;
2754 t = tn;
2755 }
2756 if (cnt)
2757 goto Lagain;
2758 }
2759 }
2760 }
2761 }
2762
2763 /**
2764 * Resume the specified thread and unload stack and register information.
2765 * If the supplied thread is the calling thread, stack and register
2766 * information will be unloaded but the thread will not be resumed. If
2767 * the resume operation fails and the thread is not running then it will
2768 * be removed from the global thread list, otherwise an exception will be
2769 * thrown.
2770 *
2771 * Params:
2772 * t = The thread to resume.
2773 *
2774 * Throws:
2775 * ThreadError if the resume fails for a running thread.
2776 */
resume(Thread t)2777 private void resume( Thread t ) nothrow
2778 {
2779 version (Windows)
2780 {
2781 if ( t.m_addr != GetCurrentThreadId() && ResumeThread( t.m_hndl ) == 0xFFFFFFFF )
2782 {
2783 if ( !t.isRunning )
2784 {
2785 Thread.remove( t );
2786 return;
2787 }
2788 onThreadError( "Unable to resume thread" );
2789 }
2790
2791 if ( !t.m_lock )
2792 t.m_curr.tstack = t.m_curr.bstack;
2793 t.m_reg[0 .. $] = 0;
2794 }
2795 else version (Darwin)
2796 {
2797 if ( t.m_addr != pthread_self() && thread_resume( t.m_tmach ) != KERN_SUCCESS )
2798 {
2799 if ( !t.isRunning )
2800 {
2801 Thread.remove( t );
2802 return;
2803 }
2804 onThreadError( "Unable to resume thread" );
2805 }
2806
2807 if ( !t.m_lock )
2808 t.m_curr.tstack = t.m_curr.bstack;
2809 t.m_reg[0 .. $] = 0;
2810 }
2811 else version (Posix)
2812 {
2813 if ( t.m_addr != pthread_self() )
2814 {
2815 if ( pthread_kill( t.m_addr, resumeSignalNumber ) != 0 )
2816 {
2817 if ( !t.isRunning )
2818 {
2819 Thread.remove( t );
2820 return;
2821 }
2822 onThreadError( "Unable to resume thread" );
2823 }
2824 }
2825 else if ( !t.m_lock )
2826 {
2827 t.m_curr.tstack = t.m_curr.bstack;
2828 }
2829 }
2830 }
2831
2832 /**
2833 * Resume all threads but the calling thread for "stop the world" garbage
2834 * collection runs. This function must be called once for each preceding
2835 * call to thread_suspendAll before the threads are actually resumed.
2836 *
2837 * In:
2838 * This routine must be preceded by a call to thread_suspendAll.
2839 *
2840 * Throws:
2841 * ThreadError if the resume operation fails for a running thread.
2842 */
thread_resumeAll()2843 extern (C) void thread_resumeAll() nothrow
2844 in
2845 {
2846 assert( suspendDepth > 0 );
2847 }
2848 body
2849 {
2850 // NOTE: See thread_suspendAll for the logic behind this.
2851 if ( !multiThreadedFlag && Thread.sm_tbeg )
2852 {
2853 if ( --suspendDepth == 0 )
2854 resume( Thread.getThis() );
2855 return;
2856 }
2857
2858 scope(exit) Thread.slock.unlock_nothrow();
2859 {
2860 if ( --suspendDepth > 0 )
2861 return;
2862
2863 for ( Thread t = Thread.sm_tbeg; t; t = t.next )
2864 {
2865 // NOTE: We do not need to care about critical regions at all
2866 // here. thread_suspendAll takes care of everything.
2867 resume( t );
2868 }
2869 }
2870 }
2871
2872 /**
2873 * Indicates the kind of scan being performed by $(D thread_scanAllType).
2874 */
2875 enum ScanType
2876 {
2877 stack, /// The stack and/or registers are being scanned.
2878 tls, /// TLS data is being scanned.
2879 }
2880
2881 alias ScanAllThreadsFn = void delegate(void*, void*) nothrow; /// The scanning function.
2882 alias ScanAllThreadsTypeFn = void delegate(ScanType, void*, void*) nothrow; /// ditto
2883
2884 /**
2885 * The main entry point for garbage collection. The supplied delegate
2886 * will be passed ranges representing both stack and register values.
2887 *
2888 * Params:
2889 * scan = The scanner function. It should scan from p1 through p2 - 1.
2890 *
2891 * In:
2892 * This routine must be preceded by a call to thread_suspendAll.
2893 */
thread_scanAllType(scope ScanAllThreadsTypeFn scan)2894 extern (C) void thread_scanAllType( scope ScanAllThreadsTypeFn scan ) nothrow
2895 in
2896 {
2897 assert( suspendDepth > 0 );
2898 }
2899 body
2900 {
2901 callWithStackShell(sp => scanAllTypeImpl(scan, sp));
2902 }
2903
2904
scanAllTypeImpl(scope ScanAllThreadsTypeFn scan,void * curStackTop)2905 private void scanAllTypeImpl( scope ScanAllThreadsTypeFn scan, void* curStackTop ) nothrow
2906 {
2907 Thread thisThread = null;
2908 void* oldStackTop = null;
2909
2910 if ( Thread.sm_tbeg )
2911 {
2912 thisThread = Thread.getThis();
2913 if ( !thisThread.m_lock )
2914 {
2915 oldStackTop = thisThread.m_curr.tstack;
2916 thisThread.m_curr.tstack = curStackTop;
2917 }
2918 }
2919
2920 scope( exit )
2921 {
2922 if ( Thread.sm_tbeg )
2923 {
2924 if ( !thisThread.m_lock )
2925 {
2926 thisThread.m_curr.tstack = oldStackTop;
2927 }
2928 }
2929 }
2930
2931 // NOTE: Synchronizing on Thread.slock is not needed because this
2932 // function may only be called after all other threads have
2933 // been suspended from within the same lock.
2934 if (Thread.nAboutToStart)
2935 scan(ScanType.stack, Thread.pAboutToStart, Thread.pAboutToStart + Thread.nAboutToStart);
2936
2937 for ( Thread.Context* c = Thread.sm_cbeg; c; c = c.next )
2938 {
2939 version (StackGrowsDown)
2940 {
2941 // NOTE: We can't index past the bottom of the stack
2942 // so don't do the "+1" for StackGrowsDown.
2943 if ( c.tstack && c.tstack < c.bstack )
2944 scan( ScanType.stack, c.tstack, c.bstack );
2945 }
2946 else
2947 {
2948 if ( c.bstack && c.bstack < c.tstack )
2949 scan( ScanType.stack, c.bstack, c.tstack + 1 );
2950 }
2951 }
2952
2953 for ( Thread t = Thread.sm_tbeg; t; t = t.next )
2954 {
2955 version (Windows)
2956 {
2957 // Ideally, we'd pass ScanType.regs or something like that, but this
2958 // would make portability annoying because it only makes sense on Windows.
2959 scan( ScanType.stack, t.m_reg.ptr, t.m_reg.ptr + t.m_reg.length );
2960 }
2961
2962 if (t.m_tlsgcdata !is null)
2963 rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2));
2964 }
2965 }
2966
2967 /**
2968 * The main entry point for garbage collection. The supplied delegate
2969 * will be passed ranges representing both stack and register values.
2970 *
2971 * Params:
2972 * scan = The scanner function. It should scan from p1 through p2 - 1.
2973 *
2974 * In:
2975 * This routine must be preceded by a call to thread_suspendAll.
2976 */
thread_scanAll(scope ScanAllThreadsFn scan)2977 extern (C) void thread_scanAll( scope ScanAllThreadsFn scan ) nothrow
2978 {
2979 thread_scanAllType((type, p1, p2) => scan(p1, p2));
2980 }
2981
2982
2983 /**
2984 * Signals that the code following this call is a critical region. Any code in
2985 * this region must finish running before the calling thread can be suspended
2986 * by a call to thread_suspendAll.
2987 *
2988 * This function is, in particular, meant to help maintain garbage collector
2989 * invariants when a lock is not used.
2990 *
2991 * A critical region is exited with thread_exitCriticalRegion.
2992 *
2993 * $(RED Warning):
2994 * Using critical regions is extremely error-prone. For instance, using locks
2995 * inside a critical region can easily result in a deadlock when another thread
2996 * holding the lock already got suspended.
2997 *
2998 * The term and concept of a 'critical region' comes from
2999 * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925 Mono's SGen garbage collector).
3000 *
3001 * In:
3002 * The calling thread must be attached to the runtime.
3003 */
thread_enterCriticalRegion()3004 extern (C) void thread_enterCriticalRegion() @nogc
3005 in
3006 {
3007 assert(Thread.getThis());
3008 }
3009 body
3010 {
3011 synchronized (Thread.criticalRegionLock)
3012 Thread.getThis().m_isInCriticalRegion = true;
3013 }
3014
3015
3016 /**
3017 * Signals that the calling thread is no longer in a critical region. Following
3018 * a call to this function, the thread can once again be suspended.
3019 *
3020 * In:
3021 * The calling thread must be attached to the runtime.
3022 */
thread_exitCriticalRegion()3023 extern (C) void thread_exitCriticalRegion() @nogc
3024 in
3025 {
3026 assert(Thread.getThis());
3027 }
3028 body
3029 {
3030 synchronized (Thread.criticalRegionLock)
3031 Thread.getThis().m_isInCriticalRegion = false;
3032 }
3033
3034
3035 /**
3036 * Returns true if the current thread is in a critical region; otherwise, false.
3037 *
3038 * In:
3039 * The calling thread must be attached to the runtime.
3040 */
thread_inCriticalRegion()3041 extern (C) bool thread_inCriticalRegion() @nogc
3042 in
3043 {
3044 assert(Thread.getThis());
3045 }
3046 body
3047 {
3048 synchronized (Thread.criticalRegionLock)
3049 return Thread.getThis().m_isInCriticalRegion;
3050 }
3051
3052
3053 /**
3054 * A callback for thread errors in D during collections. Since an allocation is not possible
3055 * a preallocated ThreadError will be used as the Error instance
3056 *
3057 * Throws:
3058 * ThreadError.
3059 */
3060 private void onThreadError(string msg = null, Throwable next = null) nothrow
3061 {
3062 __gshared ThreadError error = new ThreadError(null);
3063 error.msg = msg;
3064 error.next = next;
3065 import core.exception : SuppressTraceInfo;
3066 error.info = SuppressTraceInfo.instance;
3067 throw error;
3068 }
3069
3070
3071 unittest
3072 {
3073 assert(!thread_inCriticalRegion());
3074
3075 {
3076 thread_enterCriticalRegion();
3077
3078 scope (exit)
3079 thread_exitCriticalRegion();
3080
3081 assert(thread_inCriticalRegion());
3082 }
3083
3084 assert(!thread_inCriticalRegion());
3085 }
3086
3087 unittest
3088 {
3089 // NOTE: This entire test is based on the assumption that no
3090 // memory is allocated after the child thread is
3091 // started. If an allocation happens, a collection could
3092 // trigger, which would cause the synchronization below
3093 // to cause a deadlock.
3094 // NOTE: DO NOT USE LOCKS IN CRITICAL REGIONS IN NORMAL CODE.
3095
3096 import core.sync.semaphore;
3097
3098 auto sema = new Semaphore(),
3099 semb = new Semaphore();
3100
3101 auto thr = new Thread(
3102 {
3103 thread_enterCriticalRegion();
3104 assert(thread_inCriticalRegion());
3105 sema.notify();
3106
3107 semb.wait();
3108 assert(thread_inCriticalRegion());
3109
3110 thread_exitCriticalRegion();
3111 assert(!thread_inCriticalRegion());
3112 sema.notify();
3113
3114 semb.wait();
3115 assert(!thread_inCriticalRegion());
3116 });
3117
3118 thr.start();
3119
3120 sema.wait();
3121 synchronized (Thread.criticalRegionLock)
3122 assert(thr.m_isInCriticalRegion);
3123 semb.notify();
3124
3125 sema.wait();
3126 synchronized (Thread.criticalRegionLock)
3127 assert(!thr.m_isInCriticalRegion);
3128 semb.notify();
3129
3130 thr.join();
3131 }
3132
3133 unittest
3134 {
3135 import core.sync.semaphore;
3136
3137 shared bool inCriticalRegion;
3138 auto sema = new Semaphore(),
3139 semb = new Semaphore();
3140
3141 auto thr = new Thread(
3142 {
3143 thread_enterCriticalRegion();
3144 inCriticalRegion = true;
3145 sema.notify();
3146 semb.wait();
3147
3148 Thread.sleep(dur!"msecs"(1));
3149 inCriticalRegion = false;
3150 thread_exitCriticalRegion();
3151 });
3152 thr.start();
3153
3154 sema.wait();
3155 assert(inCriticalRegion);
3156 semb.notify();
3157
3158 thread_suspendAll();
3159 assert(!inCriticalRegion);
3160 thread_resumeAll();
3161 }
3162
3163 /**
3164 * Indicates whether an address has been marked by the GC.
3165 */
3166 enum IsMarked : int
3167 {
3168 no, /// Address is not marked.
3169 yes, /// Address is marked.
3170 unknown, /// Address is not managed by the GC.
3171 }
3172
3173 alias IsMarkedDg = int delegate( void* addr ) nothrow; /// The isMarked callback function.
3174
3175 /**
3176 * This routine allows the runtime to process any special per-thread handling
3177 * for the GC. This is needed for taking into account any memory that is
3178 * referenced by non-scanned pointers but is about to be freed. That currently
3179 * means the array append cache.
3180 *
3181 * Params:
3182 * isMarked = The function used to check if $(D addr) is marked.
3183 *
3184 * In:
3185 * This routine must be called just prior to resuming all threads.
3186 */
thread_processGCMarks(scope IsMarkedDg isMarked)3187 extern(C) void thread_processGCMarks( scope IsMarkedDg isMarked ) nothrow
3188 {
3189 for ( Thread t = Thread.sm_tbeg; t; t = t.next )
3190 {
3191 /* Can be null if collection was triggered between adding a
3192 * thread and calling rt_tlsgc_init.
3193 */
3194 if (t.m_tlsgcdata !is null)
3195 rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked);
3196 }
3197 }
3198
3199
3200 extern (C) @nogc nothrow
3201 {
3202 version (CRuntime_Glibc) int pthread_getattr_np(pthread_t thread, pthread_attr_t* attr);
3203 version (FreeBSD) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr);
3204 version (NetBSD) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr);
3205 version (DragonFlyBSD) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr);
3206 version (Solaris) int thr_stksegment(stack_t* stk);
3207 version (CRuntime_Bionic) int pthread_getattr_np(pthread_t thid, pthread_attr_t* attr);
3208 version (CRuntime_Musl) int pthread_getattr_np(pthread_t, pthread_attr_t*);
3209 version (CRuntime_UClibc) int pthread_getattr_np(pthread_t thread, pthread_attr_t* attr);
3210 }
3211
3212
getStackTop()3213 private void* getStackTop() nothrow @nogc
3214 {
3215 version (D_InlineAsm_X86)
3216 asm pure nothrow @nogc { naked; mov EAX, ESP; ret; }
3217 else version (D_InlineAsm_X86_64)
3218 asm pure nothrow @nogc { naked; mov RAX, RSP; ret; }
3219 else version (GNU)
3220 return __builtin_frame_address(0);
3221 else
3222 static assert(false, "Architecture not supported.");
3223 }
3224
3225
getStackBottom()3226 private void* getStackBottom() nothrow @nogc
3227 {
3228 version (Windows)
3229 {
3230 version (D_InlineAsm_X86)
3231 asm pure nothrow @nogc { naked; mov EAX, FS:4; ret; }
3232 else version (D_InlineAsm_X86_64)
3233 asm pure nothrow @nogc
3234 { naked;
3235 mov RAX, 8;
3236 mov RAX, GS:[RAX];
3237 ret;
3238 }
3239 else version (GNU_InlineAsm)
3240 {
3241 void *bottom;
3242
3243 version (X86)
3244 asm pure nothrow @nogc { "movl %%fs:4, %0;" : "=r" bottom; }
3245 else version (X86_64)
3246 asm pure nothrow @nogc { "movq %%gs:8, %0;" : "=r" bottom; }
3247 else
3248 static assert(false, "Platform not supported.");
3249
3250 return bottom;
3251 }
3252 else
3253 static assert(false, "Architecture not supported.");
3254 }
3255 else version (Darwin)
3256 {
3257 import core.sys.darwin.pthread;
3258 return pthread_get_stackaddr_np(pthread_self());
3259 }
3260 else version (CRuntime_Glibc)
3261 {
3262 pthread_attr_t attr;
3263 void* addr; size_t size;
3264
3265 pthread_getattr_np(pthread_self(), &attr);
3266 pthread_attr_getstack(&attr, &addr, &size);
3267 pthread_attr_destroy(&attr);
3268 version (StackGrowsDown)
3269 addr += size;
3270 return addr;
3271 }
3272 else version (FreeBSD)
3273 {
3274 pthread_attr_t attr;
3275 void* addr; size_t size;
3276
3277 pthread_attr_init(&attr);
3278 pthread_attr_get_np(pthread_self(), &attr);
3279 pthread_attr_getstack(&attr, &addr, &size);
3280 pthread_attr_destroy(&attr);
3281 version (StackGrowsDown)
3282 addr += size;
3283 return addr;
3284 }
3285 else version (NetBSD)
3286 {
3287 pthread_attr_t attr;
3288 void* addr; size_t size;
3289
3290 pthread_attr_init(&attr);
3291 pthread_attr_get_np(pthread_self(), &attr);
3292 pthread_attr_getstack(&attr, &addr, &size);
3293 pthread_attr_destroy(&attr);
3294 version (StackGrowsDown)
3295 addr += size;
3296 return addr;
3297 }
3298 else version (DragonFlyBSD)
3299 {
3300 pthread_attr_t attr;
3301 void* addr; size_t size;
3302
3303 pthread_attr_init(&attr);
3304 pthread_attr_get_np(pthread_self(), &attr);
3305 pthread_attr_getstack(&attr, &addr, &size);
3306 pthread_attr_destroy(&attr);
3307 version (StackGrowsDown)
3308 addr += size;
3309 return addr;
3310 }
3311 else version (Solaris)
3312 {
3313 stack_t stk;
3314
3315 thr_stksegment(&stk);
3316 return stk.ss_sp;
3317 }
3318 else version (CRuntime_Bionic)
3319 {
3320 pthread_attr_t attr;
3321 void* addr; size_t size;
3322
3323 pthread_getattr_np(pthread_self(), &attr);
3324 pthread_attr_getstack(&attr, &addr, &size);
3325 pthread_attr_destroy(&attr);
3326 version (StackGrowsDown)
3327 addr += size;
3328 return addr;
3329 }
3330 else version (CRuntime_Musl)
3331 {
3332 pthread_attr_t attr;
3333 void* addr; size_t size;
3334
3335 pthread_getattr_np(pthread_self(), &attr);
3336 pthread_attr_getstack(&attr, &addr, &size);
3337 pthread_attr_destroy(&attr);
3338 version (StackGrowsDown)
3339 addr += size;
3340 return addr;
3341 }
3342 else version (CRuntime_UClibc)
3343 {
3344 pthread_attr_t attr;
3345 void* addr; size_t size;
3346
3347 pthread_getattr_np(pthread_self(), &attr);
3348 pthread_attr_getstack(&attr, &addr, &size);
3349 pthread_attr_destroy(&attr);
3350 version (StackGrowsDown)
3351 addr += size;
3352 return addr;
3353 }
3354 else
3355 static assert(false, "Platform not supported.");
3356 }
3357
3358
3359 /**
3360 * Returns the stack top of the currently active stack within the calling
3361 * thread.
3362 *
3363 * In:
3364 * The calling thread must be attached to the runtime.
3365 *
3366 * Returns:
3367 * The address of the stack top.
3368 */
thread_stackTop()3369 extern (C) void* thread_stackTop() nothrow @nogc
3370 in
3371 {
3372 // Not strictly required, but it gives us more flexibility.
3373 assert(Thread.getThis());
3374 }
3375 body
3376 {
3377 return getStackTop();
3378 }
3379
3380
3381 /**
3382 * Returns the stack bottom of the currently active stack within the calling
3383 * thread.
3384 *
3385 * In:
3386 * The calling thread must be attached to the runtime.
3387 *
3388 * Returns:
3389 * The address of the stack bottom.
3390 */
thread_stackBottom()3391 extern (C) void* thread_stackBottom() nothrow @nogc
3392 in
3393 {
3394 assert(Thread.getThis());
3395 }
3396 body
3397 {
3398 return Thread.getThis().topContext().bstack;
3399 }
3400
3401
3402 ///////////////////////////////////////////////////////////////////////////////
3403 // Thread Group
3404 ///////////////////////////////////////////////////////////////////////////////
3405
3406
3407 /**
3408 * This class is intended to simplify certain common programming techniques.
3409 */
3410 class ThreadGroup
3411 {
3412 /**
3413 * Creates and starts a new Thread object that executes fn and adds it to
3414 * the list of tracked threads.
3415 *
3416 * Params:
3417 * fn = The thread function.
3418 *
3419 * Returns:
3420 * A reference to the newly created thread.
3421 */
create(void function ()fn)3422 final Thread create( void function() fn )
3423 {
3424 Thread t = new Thread( fn ).start();
3425
3426 synchronized( this )
3427 {
3428 m_all[t] = t;
3429 }
3430 return t;
3431 }
3432
3433
3434 /**
3435 * Creates and starts a new Thread object that executes dg and adds it to
3436 * the list of tracked threads.
3437 *
3438 * Params:
3439 * dg = The thread function.
3440 *
3441 * Returns:
3442 * A reference to the newly created thread.
3443 */
create(void delegate ()dg)3444 final Thread create( void delegate() dg )
3445 {
3446 Thread t = new Thread( dg ).start();
3447
3448 synchronized( this )
3449 {
3450 m_all[t] = t;
3451 }
3452 return t;
3453 }
3454
3455
3456 /**
3457 * Add t to the list of tracked threads if it is not already being tracked.
3458 *
3459 * Params:
3460 * t = The thread to add.
3461 *
3462 * In:
3463 * t must not be null.
3464 */
add(Thread t)3465 final void add( Thread t )
3466 in
3467 {
3468 assert( t );
3469 }
3470 body
3471 {
synchronized(this)3472 synchronized( this )
3473 {
3474 m_all[t] = t;
3475 }
3476 }
3477
3478
3479 /**
3480 * Removes t from the list of tracked threads. No operation will be
3481 * performed if t is not currently being tracked by this object.
3482 *
3483 * Params:
3484 * t = The thread to remove.
3485 *
3486 * In:
3487 * t must not be null.
3488 */
remove(Thread t)3489 final void remove( Thread t )
3490 in
3491 {
3492 assert( t );
3493 }
3494 body
3495 {
synchronized(this)3496 synchronized( this )
3497 {
3498 m_all.remove( t );
3499 }
3500 }
3501
3502
3503 /**
3504 * Operates on all threads currently tracked by this object.
3505 */
opApply(scope int delegate (ref Thread)dg)3506 final int opApply( scope int delegate( ref Thread ) dg )
3507 {
3508 synchronized( this )
3509 {
3510 int ret = 0;
3511
3512 // NOTE: This loop relies on the knowledge that m_all uses the
3513 // Thread object for both the key and the mapped value.
3514 foreach ( Thread t; m_all.keys )
3515 {
3516 ret = dg( t );
3517 if ( ret )
3518 break;
3519 }
3520 return ret;
3521 }
3522 }
3523
3524
3525 /**
3526 * Iteratively joins all tracked threads. This function will block add,
3527 * remove, and opApply until it completes.
3528 *
3529 * Params:
3530 * rethrow = Rethrow any unhandled exception which may have caused the
3531 * current thread to terminate.
3532 *
3533 * Throws:
3534 * Any exception not handled by the joined threads.
3535 */
3536 final void joinAll( bool rethrow = true )
3537 {
synchronized(this)3538 synchronized( this )
3539 {
3540 // NOTE: This loop relies on the knowledge that m_all uses the
3541 // Thread object for both the key and the mapped value.
3542 foreach ( Thread t; m_all.keys )
3543 {
3544 t.join( rethrow );
3545 }
3546 }
3547 }
3548
3549
3550 private:
3551 Thread[Thread] m_all;
3552 }
3553
3554
3555 ///////////////////////////////////////////////////////////////////////////////
3556 // Fiber Platform Detection and Memory Allocation
3557 ///////////////////////////////////////////////////////////////////////////////
3558
3559
3560 private
3561 {
version(D_InlineAsm_X86)3562 version (D_InlineAsm_X86)
3563 {
3564 version (Windows)
3565 version = AsmX86_Windows;
3566 else version (Posix)
3567 version = AsmX86_Posix;
3568
3569 version (Darwin)
3570 version = AlignFiberStackTo16Byte;
3571 }
version(D_InlineAsm_X86_64)3572 else version (D_InlineAsm_X86_64)
3573 {
3574 version (Windows)
3575 {
3576 version = AsmX86_64_Windows;
3577 version = AlignFiberStackTo16Byte;
3578 }
3579 else version (Posix)
3580 {
3581 version = AsmX86_64_Posix;
3582 version = AlignFiberStackTo16Byte;
3583 }
3584 }
version(X86)3585 else version (X86)
3586 {
3587 version = AsmExternal;
3588
3589 version (MinGW)
3590 {
3591 version = GNU_AsmX86_Windows;
3592 version = AlignFiberStackTo16Byte;
3593 }
3594 else version (Posix)
3595 {
3596 version = AsmX86_Posix;
3597 version (OSX)
3598 version = AlignFiberStackTo16Byte;
3599 }
3600 }
version(X86_64)3601 else version (X86_64)
3602 {
3603 version (D_X32)
3604 {
3605 // let X32 be handled by ucontext swapcontext
3606 }
3607 else
3608 {
3609 version = AsmExternal;
3610 version = AlignFiberStackTo16Byte;
3611
3612 version (MinGW)
3613 version = GNU_AsmX86_64_Windows;
3614 else version (Posix)
3615 version = AsmX86_64_Posix;
3616 }
3617 }
version(PPC)3618 else version (PPC)
3619 {
3620 version (Posix)
3621 {
3622 version = AsmPPC_Posix;
3623 version = AsmExternal;
3624 }
3625 }
version(PPC64)3626 else version (PPC64)
3627 {
3628 version (Posix)
3629 {
3630 version = AlignFiberStackTo16Byte;
3631 }
3632 }
version(MIPS_O32)3633 else version (MIPS_O32)
3634 {
3635 version (Posix)
3636 {
3637 version = AsmMIPS_O32_Posix;
3638 version = AsmExternal;
3639 }
3640 }
version(AArch64)3641 else version (AArch64)
3642 {
3643 version (Posix)
3644 {
3645 version = AsmAArch64_Posix;
3646 version = AsmExternal;
3647 version = AlignFiberStackTo16Byte;
3648 }
3649 }
version(ARM)3650 else version (ARM)
3651 {
3652 version (Posix)
3653 {
3654 version = AsmARM_Posix;
3655 version = AsmExternal;
3656 }
3657 }
version(SPARC)3658 else version (SPARC)
3659 {
3660 // NOTE: The SPARC ABI specifies only doubleword alignment.
3661 version = AlignFiberStackTo16Byte;
3662 }
version(SPARC64)3663 else version (SPARC64)
3664 {
3665 version = AlignFiberStackTo16Byte;
3666 }
3667
version(Posix)3668 version (Posix)
3669 {
3670 import core.sys.posix.unistd; // for sysconf
3671
3672 version (AsmX86_Windows) {} else
3673 version (AsmX86_Posix) {} else
3674 version (AsmX86_64_Windows) {} else
3675 version (AsmX86_64_Posix) {} else
3676 version (AsmExternal) {} else
3677 {
3678 // NOTE: The ucontext implementation requires architecture specific
3679 // data definitions to operate so testing for it must be done
3680 // by checking for the existence of ucontext_t rather than by
3681 // a version identifier. Please note that this is considered
3682 // an obsolescent feature according to the POSIX spec, so a
3683 // custom solution is still preferred.
3684 import core.sys.posix.ucontext;
3685 }
3686 }
3687
3688 static immutable size_t PAGESIZE;
3689 version (Posix) static immutable size_t PTHREAD_STACK_MIN;
3690 }
3691
3692
3693 shared static this()
3694 {
3695 version (Windows)
3696 {
3697 SYSTEM_INFO info;
3698 GetSystemInfo(&info);
3699
3700 PAGESIZE = info.dwPageSize;
3701 assert(PAGESIZE < int.max);
3702 }
3703 else version (Posix)
3704 {
3705 PAGESIZE = cast(size_t)sysconf(_SC_PAGESIZE);
3706 PTHREAD_STACK_MIN = cast(size_t)sysconf(_SC_THREAD_STACK_MIN);
3707 }
3708 else
3709 {
3710 static assert(0, "unimplemented");
3711 }
3712 }
3713
3714
3715 ///////////////////////////////////////////////////////////////////////////////
3716 // Fiber Entry Point and Context Switch
3717 ///////////////////////////////////////////////////////////////////////////////
3718
3719
3720 private
3721 {
3722 extern (C) void fiber_entryPoint() nothrow
3723 {
3724 Fiber obj = Fiber.getThis();
3725 assert( obj );
3726
3727 assert( Thread.getThis().m_curr is obj.m_ctxt );
3728 atomicStore!(MemoryOrder.raw)(*cast(shared)&Thread.getThis().m_lock, false);
3729 obj.m_ctxt.tstack = obj.m_ctxt.bstack;
3730 obj.m_state = Fiber.State.EXEC;
3731
3732 try
3733 {
3734 obj.run();
3735 }
3736 catch ( Throwable t )
3737 {
3738 obj.m_unhandled = t;
3739 }
3740
3741 static if ( __traits( compiles, ucontext_t ) )
3742 obj.m_ucur = &obj.m_utxt;
3743
3744 obj.m_state = Fiber.State.TERM;
3745 obj.switchOut();
3746 }
3747
3748 // Look above the definition of 'class Fiber' for some information about the implementation of this routine
3749 version (AsmExternal)
3750 {
3751 extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;
3752 version (AArch64)
3753 extern (C) void fiber_trampoline() nothrow;
3754 }
3755 else
3756 extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc
3757 {
3758 // NOTE: The data pushed and popped in this routine must match the
3759 // default stack created by Fiber.initStack or the initial
3760 // switch into a new context will fail.
3761
3762 version (AsmX86_Windows)
3763 {
3764 asm pure nothrow @nogc
3765 {
3766 naked;
3767
3768 // save current stack state
3769 push EBP;
3770 mov EBP, ESP;
3771 push EDI;
3772 push ESI;
3773 push EBX;
3774 push dword ptr FS:[0];
3775 push dword ptr FS:[4];
3776 push dword ptr FS:[8];
3777 push EAX;
3778
3779 // store oldp again with more accurate address
3780 mov EAX, dword ptr 8[EBP];
3781 mov [EAX], ESP;
3782 // load newp to begin context switch
3783 mov ESP, dword ptr 12[EBP];
3784
3785 // load saved state from new stack
3786 pop EAX;
3787 pop dword ptr FS:[8];
3788 pop dword ptr FS:[4];
3789 pop dword ptr FS:[0];
3790 pop EBX;
3791 pop ESI;
3792 pop EDI;
3793 pop EBP;
3794
3795 // 'return' to complete switch
3796 pop ECX;
3797 jmp ECX;
3798 }
3799 }
3800 else version (AsmX86_64_Windows)
3801 {
3802 asm pure nothrow @nogc
3803 {
3804 naked;
3805
3806 // save current stack state
3807 // NOTE: When changing the layout of registers on the stack,
3808 // make sure that the XMM registers are still aligned.
3809 // On function entry, the stack is guaranteed to not
3810 // be aligned to 16 bytes because of the return address
3811 // on the stack.
3812 push RBP;
3813 mov RBP, RSP;
3814 push R12;
3815 push R13;
3816 push R14;
3817 push R15;
3818 push RDI;
3819 push RSI;
3820 // 7 registers = 56 bytes; stack is now aligned to 16 bytes
3821 sub RSP, 160;
3822 movdqa [RSP + 144], XMM6;
3823 movdqa [RSP + 128], XMM7;
3824 movdqa [RSP + 112], XMM8;
3825 movdqa [RSP + 96], XMM9;
3826 movdqa [RSP + 80], XMM10;
3827 movdqa [RSP + 64], XMM11;
3828 movdqa [RSP + 48], XMM12;
3829 movdqa [RSP + 32], XMM13;
3830 movdqa [RSP + 16], XMM14;
3831 movdqa [RSP], XMM15;
3832 push RBX;
3833 xor RAX,RAX;
3834 push qword ptr GS:[RAX];
3835 push qword ptr GS:8[RAX];
3836 push qword ptr GS:16[RAX];
3837
3838 // store oldp
3839 mov [RCX], RSP;
3840 // load newp to begin context switch
3841 mov RSP, RDX;
3842
3843 // load saved state from new stack
3844 pop qword ptr GS:16[RAX];
3845 pop qword ptr GS:8[RAX];
3846 pop qword ptr GS:[RAX];
3847 pop RBX;
3848 movdqa XMM15, [RSP];
3849 movdqa XMM14, [RSP + 16];
3850 movdqa XMM13, [RSP + 32];
3851 movdqa XMM12, [RSP + 48];
3852 movdqa XMM11, [RSP + 64];
3853 movdqa XMM10, [RSP + 80];
3854 movdqa XMM9, [RSP + 96];
3855 movdqa XMM8, [RSP + 112];
3856 movdqa XMM7, [RSP + 128];
3857 movdqa XMM6, [RSP + 144];
3858 add RSP, 160;
3859 pop RSI;
3860 pop RDI;
3861 pop R15;
3862 pop R14;
3863 pop R13;
3864 pop R12;
3865 pop RBP;
3866
3867 // 'return' to complete switch
3868 pop RCX;
3869 jmp RCX;
3870 }
3871 }
3872 else version (AsmX86_Posix)
3873 {
3874 asm pure nothrow @nogc
3875 {
3876 naked;
3877
3878 // save current stack state
3879 push EBP;
3880 mov EBP, ESP;
3881 push EDI;
3882 push ESI;
3883 push EBX;
3884 push EAX;
3885
3886 // store oldp again with more accurate address
3887 mov EAX, dword ptr 8[EBP];
3888 mov [EAX], ESP;
3889 // load newp to begin context switch
3890 mov ESP, dword ptr 12[EBP];
3891
3892 // load saved state from new stack
3893 pop EAX;
3894 pop EBX;
3895 pop ESI;
3896 pop EDI;
3897 pop EBP;
3898
3899 // 'return' to complete switch
3900 pop ECX;
3901 jmp ECX;
3902 }
3903 }
3904 else version (AsmX86_64_Posix)
3905 {
3906 asm pure nothrow @nogc
3907 {
3908 naked;
3909
3910 // save current stack state
3911 push RBP;
3912 mov RBP, RSP;
3913 push RBX;
3914 push R12;
3915 push R13;
3916 push R14;
3917 push R15;
3918
3919 // store oldp
3920 mov [RDI], RSP;
3921 // load newp to begin context switch
3922 mov RSP, RSI;
3923
3924 // load saved state from new stack
3925 pop R15;
3926 pop R14;
3927 pop R13;
3928 pop R12;
3929 pop RBX;
3930 pop RBP;
3931
3932 // 'return' to complete switch
3933 pop RCX;
3934 jmp RCX;
3935 }
3936 }
3937 else static if ( __traits( compiles, ucontext_t ) )
3938 {
3939 Fiber cfib = Fiber.getThis();
3940 void* ucur = cfib.m_ucur;
3941
3942 *oldp = &ucur;
3943 swapcontext( **(cast(ucontext_t***) oldp),
3944 *(cast(ucontext_t**) newp) );
3945 }
3946 else
3947 static assert(0, "Not implemented");
3948 }
3949 }
3950
3951
3952 ///////////////////////////////////////////////////////////////////////////////
3953 // Fiber
3954 ///////////////////////////////////////////////////////////////////////////////
3955 /*
3956 * Documentation of Fiber internals:
3957 *
3958 * The main routines to implement when porting Fibers to new architectures are
3959 * fiber_switchContext and initStack. Some version constants have to be defined
3960 * for the new platform as well, search for "Fiber Platform Detection and Memory Allocation".
3961 *
3962 * Fibers are based on a concept called 'Context'. A Context describes the execution
3963 * state of a Fiber or main thread which is fully described by the stack, some
3964 * registers and a return address at which the Fiber/Thread should continue executing.
3965 * Please note that not only each Fiber has a Context, but each thread also has got a
3966 * Context which describes the threads stack and state. If you call Fiber fib; fib.call
3967 * the first time in a thread you switch from Threads Context into the Fibers Context.
3968 * If you call fib.yield in that Fiber you switch out of the Fibers context and back
3969 * into the Thread Context. (However, this is not always the case. You can call a Fiber
3970 * from within another Fiber, then you switch Contexts between the Fibers and the Thread
3971 * Context is not involved)
3972 *
3973 * In all current implementations the registers and the return address are actually
3974 * saved on a Contexts stack.
3975 *
3976 * The fiber_switchContext routine has got two parameters:
3977 * void** a: This is the _location_ where we have to store the current stack pointer,
3978 * the stack pointer of the currently executing Context (Fiber or Thread).
3979 * void* b: This is the pointer to the stack of the Context which we want to switch into.
3980 * Note that we get the same pointer here as the one we stored into the void** a
3981 * in a previous call to fiber_switchContext.
3982 *
3983 * In the simplest case, a fiber_switchContext rountine looks like this:
3984 * fiber_switchContext:
3985 * push {return Address}
3986 * push {registers}
3987 * copy {stack pointer} into {location pointed to by a}
3988 * //We have now switch to the stack of a different Context!
3989 * copy {b} into {stack pointer}
3990 * pop {registers}
3991 * pop {return Address}
3992 * jump to {return Address}
3993 *
3994 * The GC uses the value returned in parameter a to scan the Fibers stack. It scans from
3995 * the stack base to that value. As the GC dislikes false pointers we can actually optimize
3996 * this a little: By storing registers which can not contain references to memory managed
3997 * by the GC outside of the region marked by the stack base pointer and the stack pointer
3998 * saved in fiber_switchContext we can prevent the GC from scanning them.
3999 * Such registers are usually floating point registers and the return address. In order to
4000 * implement this, we return a modified stack pointer from fiber_switchContext. However,
4001 * we have to remember that when we restore the registers from the stack!
4002 *
4003 * --------------------------- <= Stack Base
4004 * | Frame | <= Many other stack frames
4005 * | Frame |
4006 * |-------------------------| <= The last stack frame. This one is created by fiber_switchContext
4007 * | registers with pointers |
4008 * | | <= Stack pointer. GC stops scanning here
4009 * | return address |
4010 * |floating point registers |
4011 * --------------------------- <= Real Stack End
4012 *
4013 * fiber_switchContext:
4014 * push {registers with pointers}
4015 * copy {stack pointer} into {location pointed to by a}
4016 * push {return Address}
4017 * push {Floating point registers}
4018 * //We have now switch to the stack of a different Context!
4019 * copy {b} into {stack pointer}
4020 * //We now have to adjust the stack pointer to point to 'Real Stack End' so we can pop
4021 * //the FP registers
4022 * //+ or - depends on if your stack grows downwards or upwards
4023 * {stack pointer} = {stack pointer} +- ({FPRegisters}.sizeof + {return address}.sizeof}
4024 * pop {Floating point registers}
4025 * pop {return Address}
4026 * pop {registers with pointers}
4027 * jump to {return Address}
4028 *
4029 * So the question now is which registers need to be saved? This depends on the specific
4030 * architecture ABI of course, but here are some general guidelines:
4031 * - If a register is callee-save (if the callee modifies the register it must saved and
4032 * restored by the callee) it needs to be saved/restored in switchContext
4033 * - If a register is caller-save it needn't be saved/restored. (Calling fiber_switchContext
4034 * is a function call and the compiler therefore already must save these registers before
4035 * calling fiber_switchContext)
4036 * - Argument registers used for passing parameters to functions needn't be saved/restored
4037 * - The return register needn't be saved/restored (fiber_switchContext hasn't got a return type)
4038 * - All scratch registers needn't be saved/restored
4039 * - The link register usually needn't be saved/restored (but sometimes it must be cleared -
4040 * see below for details)
4041 * - The frame pointer register - if it exists - is usually callee-save
4042 * - All current implementations do not save control registers
4043 *
4044 * What happens on the first switch into a Fiber? We never saved a state for this fiber before,
4045 * but the initial state is prepared in the initStack routine. (This routine will also be called
4046 * when a Fiber is being resetted). initStack must produce exactly the same stack layout as the
4047 * part of fiber_switchContext which saves the registers. Pay special attention to set the stack
4048 * pointer correctly if you use the GC optimization mentioned before. the return Address saved in
4049 * initStack must be the address of fiber_entrypoint.
4050 *
4051 * There's now a small but important difference between the first context switch into a fiber and
4052 * further context switches. On the first switch, Fiber.call is used and the returnAddress in
4053 * fiber_switchContext will point to fiber_entrypoint. The important thing here is that this jump
4054 * is a _function call_, we call fiber_entrypoint by jumping before it's function prologue. On later
4055 * calls, the user used yield() in a function, and therefore the return address points into a user
4056 * function, after the yield call. So here the jump in fiber_switchContext is a _function return_,
4057 * not a function call!
4058 *
4059 * The most important result of this is that on entering a function, i.e. fiber_entrypoint, we
4060 * would have to provide a return address / set the link register once fiber_entrypoint
4061 * returns. Now fiber_entrypoint does never return and therefore the actual value of the return
4062 * address / link register is never read/used and therefore doesn't matter. When fiber_switchContext
4063 * performs a _function return_ the value in the link register doesn't matter either.
4064 * However, the link register will still be saved to the stack in fiber_entrypoint and some
4065 * exception handling / stack unwinding code might read it from this stack location and crash.
4066 * The exact solution depends on your architecture, but see the ARM implementation for a way
4067 * to deal with this issue.
4068 *
4069 * The ARM implementation is meant to be used as a kind of documented example implementation.
4070 * Look there for a concrete example.
4071 *
4072 * FIXME: fiber_entrypoint might benefit from a @noreturn attribute, but D doesn't have one.
4073 */
4074
4075 /**
4076 * This class provides a cooperative concurrency mechanism integrated with the
4077 * threading and garbage collection functionality. Calling a fiber may be
4078 * considered a blocking operation that returns when the fiber yields (via
4079 * Fiber.yield()). Execution occurs within the context of the calling thread
4080 * so synchronization is not necessary to guarantee memory visibility so long
4081 * as the same thread calls the fiber each time. Please note that there is no
4082 * requirement that a fiber be bound to one specific thread. Rather, fibers
4083 * may be freely passed between threads so long as they are not currently
4084 * executing. Like threads, a new fiber thread may be created using either
4085 * derivation or composition, as in the following example.
4086 *
4087 * Warning:
4088 * Status registers are not saved by the current implementations. This means
4089 * floating point exception status bits (overflow, divide by 0), rounding mode
4090 * and similar stuff is set per-thread, not per Fiber!
4091 *
4092 * Warning:
4093 * On ARM FPU registers are not saved if druntime was compiled as ARM_SoftFloat.
4094 * If such a build is used on a ARM_SoftFP system which actually has got a FPU
4095 * and other libraries are using the FPU registers (other code is compiled
4096 * as ARM_SoftFP) this can cause problems. Druntime must be compiled as
4097 * ARM_SoftFP in this case.
4098 *
4099 * Example:
4100 * ----------------------------------------------------------------------
4101 *
4102 * class DerivedFiber : Fiber
4103 * {
4104 * this()
4105 * {
4106 * super( &run );
4107 * }
4108 *
4109 * private :
4110 * void run()
4111 * {
4112 * printf( "Derived fiber running.\n" );
4113 * }
4114 * }
4115 *
4116 * void fiberFunc()
4117 * {
4118 * printf( "Composed fiber running.\n" );
4119 * Fiber.yield();
4120 * printf( "Composed fiber running.\n" );
4121 * }
4122 *
4123 * // create instances of each type
4124 * Fiber derived = new DerivedFiber();
4125 * Fiber composed = new Fiber( &fiberFunc );
4126 *
4127 * // call both fibers once
4128 * derived.call();
4129 * composed.call();
4130 * printf( "Execution returned to calling context.\n" );
4131 * composed.call();
4132 *
4133 * // since each fiber has run to completion, each should have state TERM
4134 * assert( derived.state == Fiber.State.TERM );
4135 * assert( composed.state == Fiber.State.TERM );
4136 *
4137 * ----------------------------------------------------------------------
4138 *
4139 * Authors: Based on a design by Mikola Lysenko.
4140 */
4141 class Fiber
4142 {
4143 ///////////////////////////////////////////////////////////////////////////
4144 // Initialization
4145 ///////////////////////////////////////////////////////////////////////////
4146
4147
4148 /**
4149 * Initializes a fiber object which is associated with a static
4150 * D function.
4151 *
4152 * Params:
4153 * fn = The fiber function.
4154 * sz = The stack size for this fiber.
4155 * guardPageSize = size of the guard page to trap fiber's stack
4156 * overflows
4157 *
4158 * In:
4159 * fn must not be null.
4160 */
4161 this( void function() fn, size_t sz = PAGESIZE*4,
4162 size_t guardPageSize = PAGESIZE ) nothrow
4163 in
4164 {
4165 assert( fn );
4166 }
4167 body
4168 {
4169 allocStack( sz, guardPageSize );
4170 reset( fn );
4171 }
4172
4173
4174 /**
4175 * Initializes a fiber object which is associated with a dynamic
4176 * D function.
4177 *
4178 * Params:
4179 * dg = The fiber function.
4180 * sz = The stack size for this fiber.
4181 * guardPageSize = size of the guard page to trap fiber's stack
4182 * overflows
4183 *
4184 * In:
4185 * dg must not be null.
4186 */
4187 this( void delegate() dg, size_t sz = PAGESIZE*4,
4188 size_t guardPageSize = PAGESIZE ) nothrow
4189 in
4190 {
4191 assert( dg );
4192 }
4193 body
4194 {
4195 allocStack( sz, guardPageSize);
4196 reset( dg );
4197 }
4198
4199
4200 /**
4201 * Cleans up any remaining resources used by this object.
4202 */
4203 ~this() nothrow @nogc
4204 {
4205 // NOTE: A live reference to this object will exist on its associated
4206 // stack from the first time its call() method has been called
4207 // until its execution completes with State.TERM. Thus, the only
4208 // times this dtor should be called are either if the fiber has
4209 // terminated (and therefore has no active stack) or if the user
4210 // explicitly deletes this object. The latter case is an error
4211 // but is not easily tested for, since State.HOLD may imply that
4212 // the fiber was just created but has never been run. There is
4213 // not a compelling case to create a State.INIT just to offer a
4214 // means of ensuring the user isn't violating this object's
4215 // contract, so for now this requirement will be enforced by
4216 // documentation only.
4217 freeStack();
4218 }
4219
4220
4221 ///////////////////////////////////////////////////////////////////////////
4222 // General Actions
4223 ///////////////////////////////////////////////////////////////////////////
4224
4225
4226 /**
4227 * Transfers execution to this fiber object. The calling context will be
4228 * suspended until the fiber calls Fiber.yield() or until it terminates
4229 * via an unhandled exception.
4230 *
4231 * Params:
4232 * rethrow = Rethrow any unhandled exception which may have caused this
4233 * fiber to terminate.
4234 *
4235 * In:
4236 * This fiber must be in state HOLD.
4237 *
4238 * Throws:
4239 * Any exception not handled by the joined thread.
4240 *
4241 * Returns:
4242 * Any exception not handled by this fiber if rethrow = false, null
4243 * otherwise.
4244 */
4245 // Not marked with any attributes, even though `nothrow @nogc` works
4246 // because it calls arbitrary user code. Most of the implementation
4247 // is already `@nogc nothrow`, but in order for `Fiber.call` to
4248 // propagate the attributes of the user's function, the Fiber
4249 // class needs to be templated.
4250 final Throwable call( Rethrow rethrow = Rethrow.yes )
4251 {
4252 return rethrow ? call!(Rethrow.yes)() : call!(Rethrow.no);
4253 }
4254
4255 /// ditto
4256 final Throwable call( Rethrow rethrow )()
4257 {
4258 callImpl();
4259 if ( m_unhandled )
4260 {
4261 Throwable t = m_unhandled;
4262 m_unhandled = null;
4263 static if ( rethrow )
4264 throw t;
4265 else
4266 return t;
4267 }
4268 return null;
4269 }
4270
4271 /// ditto
4272 deprecated("Please pass Fiber.Rethrow.yes or .no instead of a boolean.")
4273 final Throwable call( bool rethrow )
4274 {
4275 return rethrow ? call!(Rethrow.yes)() : call!(Rethrow.no);
4276 }
4277
4278 private void callImpl() nothrow @nogc
4279 in
4280 {
4281 assert( m_state == State.HOLD );
4282 }
4283 body
4284 {
4285 Fiber cur = getThis();
4286
4287 static if ( __traits( compiles, ucontext_t ) )
4288 m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt;
4289
4290 setThis( this );
4291 this.switchIn();
4292 setThis( cur );
4293
4294 static if ( __traits( compiles, ucontext_t ) )
4295 m_ucur = null;
4296
4297 // NOTE: If the fiber has terminated then the stack pointers must be
4298 // reset. This ensures that the stack for this fiber is not
4299 // scanned if the fiber has terminated. This is necessary to
4300 // prevent any references lingering on the stack from delaying
4301 // the collection of otherwise dead objects. The most notable
4302 // being the current object, which is referenced at the top of
4303 // fiber_entryPoint.
4304 if ( m_state == State.TERM )
4305 {
4306 m_ctxt.tstack = m_ctxt.bstack;
4307 }
4308 }
4309
4310 /// Flag to control rethrow behavior of $(D $(LREF call))
4311 enum Rethrow : bool { no, yes }
4312
4313 /**
4314 * Resets this fiber so that it may be re-used, optionally with a
4315 * new function/delegate. This routine should only be called for
4316 * fibers that have terminated, as doing otherwise could result in
4317 * scope-dependent functionality that is not executed.
4318 * Stack-based classes, for example, may not be cleaned up
4319 * properly if a fiber is reset before it has terminated.
4320 *
4321 * In:
4322 * This fiber must be in state TERM or HOLD.
4323 */
4324 final void reset() nothrow @nogc
4325 in
4326 {
4327 assert( m_state == State.TERM || m_state == State.HOLD );
4328 }
4329 body
4330 {
4331 m_ctxt.tstack = m_ctxt.bstack;
4332 m_state = State.HOLD;
4333 initStack();
4334 m_unhandled = null;
4335 }
4336
4337 /// ditto
4338 final void reset( void function() fn ) nothrow @nogc
4339 {
4340 reset();
4341 m_fn = fn;
4342 m_call = Call.FN;
4343 }
4344
4345 /// ditto
4346 final void reset( void delegate() dg ) nothrow @nogc
4347 {
4348 reset();
4349 m_dg = dg;
4350 m_call = Call.DG;
4351 }
4352
4353 ///////////////////////////////////////////////////////////////////////////
4354 // General Properties
4355 ///////////////////////////////////////////////////////////////////////////
4356
4357
4358 /**
4359 * A fiber may occupy one of three states: HOLD, EXEC, and TERM. The HOLD
4360 * state applies to any fiber that is suspended and ready to be called.
4361 * The EXEC state will be set for any fiber that is currently executing.
4362 * And the TERM state is set when a fiber terminates. Once a fiber
4363 * terminates, it must be reset before it may be called again.
4364 */
4365 enum State
4366 {
4367 HOLD, ///
4368 EXEC, ///
4369 TERM ///
4370 }
4371
4372
4373 /**
4374 * Gets the current state of this fiber.
4375 *
4376 * Returns:
4377 * The state of this fiber as an enumerated value.
4378 */
4379 final @property State state() const @safe pure nothrow @nogc
4380 {
4381 return m_state;
4382 }
4383
4384
4385 ///////////////////////////////////////////////////////////////////////////
4386 // Actions on Calling Fiber
4387 ///////////////////////////////////////////////////////////////////////////
4388
4389
4390 /**
4391 * Forces a context switch to occur away from the calling fiber.
4392 */
4393 static void yield() nothrow @nogc
4394 {
4395 Fiber cur = getThis();
4396 assert( cur, "Fiber.yield() called with no active fiber" );
4397 assert( cur.m_state == State.EXEC );
4398
4399 static if ( __traits( compiles, ucontext_t ) )
4400 cur.m_ucur = &cur.m_utxt;
4401
4402 cur.m_state = State.HOLD;
4403 cur.switchOut();
4404 cur.m_state = State.EXEC;
4405 }
4406
4407
4408 /**
4409 * Forces a context switch to occur away from the calling fiber and then
4410 * throws obj in the calling fiber.
4411 *
4412 * Params:
4413 * t = The object to throw.
4414 *
4415 * In:
4416 * t must not be null.
4417 */
4418 static void yieldAndThrow( Throwable t ) nothrow @nogc
4419 in
4420 {
4421 assert( t );
4422 }
4423 body
4424 {
4425 Fiber cur = getThis();
4426 assert( cur, "Fiber.yield() called with no active fiber" );
4427 assert( cur.m_state == State.EXEC );
4428
4429 static if ( __traits( compiles, ucontext_t ) )
4430 cur.m_ucur = &cur.m_utxt;
4431
4432 cur.m_unhandled = t;
4433 cur.m_state = State.HOLD;
4434 cur.switchOut();
4435 cur.m_state = State.EXEC;
4436 }
4437
4438
4439 ///////////////////////////////////////////////////////////////////////////
4440 // Fiber Accessors
4441 ///////////////////////////////////////////////////////////////////////////
4442
4443
4444 /**
4445 * Provides a reference to the calling fiber or null if no fiber is
4446 * currently active.
4447 *
4448 * Returns:
4449 * The fiber object representing the calling fiber or null if no fiber
4450 * is currently active within this thread. The result of deleting this object is undefined.
4451 */
4452 static Fiber getThis() @safe nothrow @nogc
4453 {
4454 return sm_this;
4455 }
4456
4457
4458 ///////////////////////////////////////////////////////////////////////////
4459 // Static Initialization
4460 ///////////////////////////////////////////////////////////////////////////
4461
4462
4463 version (Posix)
4464 {
4465 static this()
4466 {
4467 static if ( __traits( compiles, ucontext_t ) )
4468 {
4469 int status = getcontext( &sm_utxt );
4470 assert( status == 0 );
4471 }
4472 }
4473 }
4474
4475 private:
4476 //
4477 // Initializes a fiber object which has no associated executable function.
4478 //
4479 this() @safe pure nothrow @nogc
4480 {
4481 m_call = Call.NO;
4482 }
4483
4484
4485 //
4486 // Fiber entry point. Invokes the function or delegate passed on
4487 // construction (if any).
4488 //
4489 final void run()
4490 {
4491 switch ( m_call )
4492 {
4493 case Call.FN:
4494 m_fn();
4495 break;
4496 case Call.DG:
4497 m_dg();
4498 break;
4499 default:
4500 break;
4501 }
4502 }
4503
4504
4505 private:
4506 //
4507 // The type of routine passed on fiber construction.
4508 //
4509 enum Call
4510 {
4511 NO,
4512 FN,
4513 DG
4514 }
4515
4516
4517 //
4518 // Standard fiber data
4519 //
4520 Call m_call;
4521 union
4522 {
4523 void function() m_fn;
4524 void delegate() m_dg;
4525 }
4526 bool m_isRunning;
4527 Throwable m_unhandled;
4528 State m_state;
4529
4530
4531 private:
4532 ///////////////////////////////////////////////////////////////////////////
4533 // Stack Management
4534 ///////////////////////////////////////////////////////////////////////////
4535
4536
4537 //
4538 // Allocate a new stack for this fiber.
4539 //
4540 final void allocStack( size_t sz, size_t guardPageSize ) nothrow
4541 in
4542 {
4543 assert( !m_pmem && !m_ctxt );
4544 }
4545 body
4546 {
4547 // adjust alloc size to a multiple of PAGESIZE
4548 sz += PAGESIZE - 1;
4549 sz -= sz % PAGESIZE;
4550
4551 // NOTE: This instance of Thread.Context is dynamic so Fiber objects
4552 // can be collected by the GC so long as no user level references
4553 // to the object exist. If m_ctxt were not dynamic then its
4554 // presence in the global context list would be enough to keep
4555 // this object alive indefinitely. An alternative to allocating
4556 // room for this struct explicitly would be to mash it into the
4557 // base of the stack being allocated below. However, doing so
4558 // requires too much special logic to be worthwhile.
4559 m_ctxt = new Thread.Context;
4560
4561 static if ( __traits( compiles, VirtualAlloc ) )
4562 {
4563 // reserve memory for stack
4564 m_pmem = VirtualAlloc( null,
4565 sz + guardPageSize,
4566 MEM_RESERVE,
4567 PAGE_NOACCESS );
4568 if ( !m_pmem )
4569 onOutOfMemoryError();
4570
4571 version (StackGrowsDown)
4572 {
4573 void* stack = m_pmem + guardPageSize;
4574 void* guard = m_pmem;
4575 void* pbase = stack + sz;
4576 }
4577 else
4578 {
4579 void* stack = m_pmem;
4580 void* guard = m_pmem + sz;
4581 void* pbase = stack;
4582 }
4583
4584 // allocate reserved stack segment
4585 stack = VirtualAlloc( stack,
4586 sz,
4587 MEM_COMMIT,
4588 PAGE_READWRITE );
4589 if ( !stack )
4590 onOutOfMemoryError();
4591
4592 if (guardPageSize)
4593 {
4594 // allocate reserved guard page
4595 guard = VirtualAlloc( guard,
4596 guardPageSize,
4597 MEM_COMMIT,
4598 PAGE_READWRITE | PAGE_GUARD );
4599 if ( !guard )
4600 onOutOfMemoryError();
4601 }
4602
4603 m_ctxt.bstack = pbase;
4604 m_ctxt.tstack = pbase;
4605 m_size = sz;
4606 }
4607 else
4608 {
4609 version (Posix) import core.sys.posix.sys.mman; // mmap
4610 version (FreeBSD) import core.sys.freebsd.sys.mman : MAP_ANON;
4611 version (NetBSD) import core.sys.netbsd.sys.mman : MAP_ANON;
4612 version (DragonFlyBSD) import core.sys.dragonflybsd.sys.mman : MAP_ANON;
4613 version (CRuntime_Glibc) import core.sys.linux.sys.mman : MAP_ANON;
4614 version (Darwin) import core.sys.darwin.sys.mman : MAP_ANON;
4615 version (CRuntime_UClibc) import core.sys.linux.sys.mman : MAP_ANON;
4616
4617 static if ( __traits( compiles, mmap ) )
4618 {
4619 // Allocate more for the memory guard
4620 sz += guardPageSize;
4621
4622 m_pmem = mmap( null,
4623 sz,
4624 PROT_READ | PROT_WRITE,
4625 MAP_PRIVATE | MAP_ANON,
4626 -1,
4627 0 );
4628 if ( m_pmem == MAP_FAILED )
4629 m_pmem = null;
4630 }
4631 else static if ( __traits( compiles, valloc ) )
4632 {
4633 m_pmem = valloc( sz );
4634 }
4635 else static if ( __traits( compiles, malloc ) )
4636 {
4637 m_pmem = malloc( sz );
4638 }
4639 else
4640 {
4641 m_pmem = null;
4642 }
4643
4644 if ( !m_pmem )
4645 onOutOfMemoryError();
4646
4647 version (StackGrowsDown)
4648 {
4649 m_ctxt.bstack = m_pmem + sz;
4650 m_ctxt.tstack = m_pmem + sz;
4651 void* guard = m_pmem;
4652 }
4653 else
4654 {
4655 m_ctxt.bstack = m_pmem;
4656 m_ctxt.tstack = m_pmem;
4657 void* guard = m_pmem + sz - guardPageSize;
4658 }
4659 m_size = sz;
4660
4661 static if ( __traits( compiles, mmap ) )
4662 {
4663 if (guardPageSize)
4664 {
4665 // protect end of stack
4666 if ( mprotect(guard, guardPageSize, PROT_NONE) == -1 )
4667 abort();
4668 }
4669 }
4670 else
4671 {
4672 // Supported only for mmap allocated memory - results are
4673 // undefined if applied to memory not obtained by mmap
4674 }
4675 }
4676
4677 Thread.add( m_ctxt );
4678 }
4679
4680
4681 //
4682 // Free this fiber's stack.
4683 //
4684 final void freeStack() nothrow @nogc
4685 in
4686 {
4687 assert( m_pmem && m_ctxt );
4688 }
4689 body
4690 {
4691 // NOTE: m_ctxt is guaranteed to be alive because it is held in the
4692 // global context list.
4693 Thread.slock.lock_nothrow();
4694 scope(exit) Thread.slock.unlock_nothrow();
4695 Thread.remove( m_ctxt );
4696
4697 static if ( __traits( compiles, VirtualAlloc ) )
4698 {
4699 VirtualFree( m_pmem, 0, MEM_RELEASE );
4700 }
4701 else
4702 {
4703 import core.sys.posix.sys.mman; // munmap
4704
4705 static if ( __traits( compiles, mmap ) )
4706 {
4707 munmap( m_pmem, m_size );
4708 }
4709 else static if ( __traits( compiles, valloc ) )
4710 {
4711 free( m_pmem );
4712 }
4713 else static if ( __traits( compiles, malloc ) )
4714 {
4715 free( m_pmem );
4716 }
4717 }
4718 m_pmem = null;
4719 m_ctxt = null;
4720 }
4721
4722
4723 //
4724 // Initialize the allocated stack.
4725 // Look above the definition of 'class Fiber' for some information about the implementation of this routine
4726 //
4727 final void initStack() nothrow @nogc
4728 in
4729 {
4730 assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack );
4731 assert( cast(size_t) m_ctxt.bstack % (void*).sizeof == 0 );
4732 }
4733 body
4734 {
4735 void* pstack = m_ctxt.tstack;
4736 scope( exit ) m_ctxt.tstack = pstack;
4737
4738 void push( size_t val ) nothrow
4739 {
4740 version (StackGrowsDown)
4741 {
4742 pstack -= size_t.sizeof;
4743 *(cast(size_t*) pstack) = val;
4744 }
4745 else
4746 {
4747 pstack += size_t.sizeof;
4748 *(cast(size_t*) pstack) = val;
4749 }
4750 }
4751
4752 // NOTE: On OS X the stack must be 16-byte aligned according
4753 // to the IA-32 call spec. For x86_64 the stack also needs to
4754 // be aligned to 16-byte according to SysV AMD64 ABI.
4755 version (AlignFiberStackTo16Byte)
4756 {
4757 version (StackGrowsDown)
4758 {
4759 pstack = cast(void*)(cast(size_t)(pstack) - (cast(size_t)(pstack) & 0x0F));
4760 }
4761 else
4762 {
4763 pstack = cast(void*)(cast(size_t)(pstack) + (cast(size_t)(pstack) & 0x0F));
4764 }
4765 }
4766
4767 version (AsmX86_Windows)
4768 {
4769 version (StackGrowsDown) {} else static assert( false );
4770
4771 // On Windows Server 2008 and 2008 R2, an exploit mitigation
4772 // technique known as SEHOP is activated by default. To avoid
4773 // hijacking of the exception handler chain, the presence of a
4774 // Windows-internal handler (ntdll.dll!FinalExceptionHandler) at
4775 // its end is tested by RaiseException. If it is not present, all
4776 // handlers are disregarded, and the program is thus aborted
4777 // (see http://blogs.technet.com/b/srd/archive/2009/02/02/
4778 // preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx).
4779 // For new threads, this handler is installed by Windows immediately
4780 // after creation. To make exception handling work in fibers, we
4781 // have to insert it for our new stacks manually as well.
4782 //
4783 // To do this, we first determine the handler by traversing the SEH
4784 // chain of the current thread until its end, and then construct a
4785 // registration block for the last handler on the newly created
4786 // thread. We then continue to push all the initial register values
4787 // for the first context switch as for the other implementations.
4788 //
4789 // Note that this handler is never actually invoked, as we install
4790 // our own one on top of it in the fiber entry point function.
4791 // Thus, it should not have any effects on OSes not implementing
4792 // exception chain verification.
4793
4794 alias fp_t = void function(); // Actual signature not relevant.
4795 static struct EXCEPTION_REGISTRATION
4796 {
4797 EXCEPTION_REGISTRATION* next; // sehChainEnd if last one.
4798 fp_t handler;
4799 }
4800 enum sehChainEnd = cast(EXCEPTION_REGISTRATION*) 0xFFFFFFFF;
4801
4802 __gshared static fp_t finalHandler = null;
4803 if ( finalHandler is null )
4804 {
4805 static EXCEPTION_REGISTRATION* fs0() nothrow
4806 {
4807 asm pure nothrow @nogc
4808 {
4809 naked;
4810 mov EAX, FS:[0];
4811 ret;
4812 }
4813 }
4814 auto reg = fs0();
4815 while ( reg.next != sehChainEnd ) reg = reg.next;
4816
4817 // Benign races are okay here, just to avoid re-lookup on every
4818 // fiber creation.
4819 finalHandler = reg.handler;
4820 }
4821
4822 // When linking with /safeseh (supported by LDC, but not DMD)
4823 // the exception chain must not extend to the very top
4824 // of the stack, otherwise the exception chain is also considered
4825 // invalid. Reserving additional 4 bytes at the top of the stack will
4826 // keep the EXCEPTION_REGISTRATION below that limit
4827 size_t reserve = EXCEPTION_REGISTRATION.sizeof + 4;
4828 pstack -= reserve;
4829 *(cast(EXCEPTION_REGISTRATION*)pstack) =
4830 EXCEPTION_REGISTRATION( sehChainEnd, finalHandler );
4831
4832 push( cast(size_t) &fiber_entryPoint ); // EIP
4833 push( cast(size_t) m_ctxt.bstack - reserve ); // EBP
4834 push( 0x00000000 ); // EDI
4835 push( 0x00000000 ); // ESI
4836 push( 0x00000000 ); // EBX
4837 push( cast(size_t) m_ctxt.bstack - reserve ); // FS:[0]
4838 push( cast(size_t) m_ctxt.bstack ); // FS:[4]
4839 push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8]
4840 push( 0x00000000 ); // EAX
4841 }
4842 else version (AsmX86_64_Windows)
4843 {
4844 // Using this trampoline instead of the raw fiber_entryPoint
4845 // ensures that during context switches, source and destination
4846 // stacks have the same alignment. Otherwise, the stack would need
4847 // to be shifted by 8 bytes for the first call, as fiber_entryPoint
4848 // is an actual function expecting a stack which is not aligned
4849 // to 16 bytes.
4850 static void trampoline()
4851 {
4852 asm pure nothrow @nogc
4853 {
4854 naked;
4855 sub RSP, 32; // Shadow space (Win64 calling convention)
4856 call fiber_entryPoint;
4857 xor RCX, RCX; // This should never be reached, as
4858 jmp RCX; // fiber_entryPoint must never return.
4859 }
4860 }
4861
4862 push( cast(size_t) &trampoline ); // RIP
4863 push( 0x00000000_00000000 ); // RBP
4864 push( 0x00000000_00000000 ); // R12
4865 push( 0x00000000_00000000 ); // R13
4866 push( 0x00000000_00000000 ); // R14
4867 push( 0x00000000_00000000 ); // R15
4868 push( 0x00000000_00000000 ); // RDI
4869 push( 0x00000000_00000000 ); // RSI
4870 push( 0x00000000_00000000 ); // XMM6 (high)
4871 push( 0x00000000_00000000 ); // XMM6 (low)
4872 push( 0x00000000_00000000 ); // XMM7 (high)
4873 push( 0x00000000_00000000 ); // XMM7 (low)
4874 push( 0x00000000_00000000 ); // XMM8 (high)
4875 push( 0x00000000_00000000 ); // XMM8 (low)
4876 push( 0x00000000_00000000 ); // XMM9 (high)
4877 push( 0x00000000_00000000 ); // XMM9 (low)
4878 push( 0x00000000_00000000 ); // XMM10 (high)
4879 push( 0x00000000_00000000 ); // XMM10 (low)
4880 push( 0x00000000_00000000 ); // XMM11 (high)
4881 push( 0x00000000_00000000 ); // XMM11 (low)
4882 push( 0x00000000_00000000 ); // XMM12 (high)
4883 push( 0x00000000_00000000 ); // XMM12 (low)
4884 push( 0x00000000_00000000 ); // XMM13 (high)
4885 push( 0x00000000_00000000 ); // XMM13 (low)
4886 push( 0x00000000_00000000 ); // XMM14 (high)
4887 push( 0x00000000_00000000 ); // XMM14 (low)
4888 push( 0x00000000_00000000 ); // XMM15 (high)
4889 push( 0x00000000_00000000 ); // XMM15 (low)
4890 push( 0x00000000_00000000 ); // RBX
4891 push( 0xFFFFFFFF_FFFFFFFF ); // GS:[0]
4892 version (StackGrowsDown)
4893 {
4894 push( cast(size_t) m_ctxt.bstack ); // GS:[8]
4895 push( cast(size_t) m_ctxt.bstack - m_size ); // GS:[16]
4896 }
4897 else
4898 {
4899 push( cast(size_t) m_ctxt.bstack ); // GS:[8]
4900 push( cast(size_t) m_ctxt.bstack + m_size ); // GS:[16]
4901 }
4902 }
4903 else version (AsmX86_Posix)
4904 {
4905 push( 0x00000000 ); // Return address of fiber_entryPoint call
4906 push( cast(size_t) &fiber_entryPoint ); // EIP
4907 push( cast(size_t) m_ctxt.bstack ); // EBP
4908 push( 0x00000000 ); // EDI
4909 push( 0x00000000 ); // ESI
4910 push( 0x00000000 ); // EBX
4911 push( 0x00000000 ); // EAX
4912 }
4913 else version (AsmX86_64_Posix)
4914 {
4915 push( 0x00000000_00000000 ); // Return address of fiber_entryPoint call
4916 push( cast(size_t) &fiber_entryPoint ); // RIP
4917 push( cast(size_t) m_ctxt.bstack ); // RBP
4918 push( 0x00000000_00000000 ); // RBX
4919 push( 0x00000000_00000000 ); // R12
4920 push( 0x00000000_00000000 ); // R13
4921 push( 0x00000000_00000000 ); // R14
4922 push( 0x00000000_00000000 ); // R15
4923 }
4924 else version (AsmPPC_Posix)
4925 {
4926 version (StackGrowsDown)
4927 {
4928 pstack -= int.sizeof * 5;
4929 }
4930 else
4931 {
4932 pstack += int.sizeof * 5;
4933 }
4934
4935 push( cast(size_t) &fiber_entryPoint ); // link register
4936 push( 0x00000000 ); // control register
4937 push( 0x00000000 ); // old stack pointer
4938
4939 // GPR values
4940 version (StackGrowsDown)
4941 {
4942 pstack -= int.sizeof * 20;
4943 }
4944 else
4945 {
4946 pstack += int.sizeof * 20;
4947 }
4948
4949 assert( (cast(size_t) pstack & 0x0f) == 0 );
4950 }
4951 else version (AsmMIPS_O32_Posix)
4952 {
4953 version (StackGrowsDown) {}
4954 else static assert(0);
4955
4956 /* We keep the FP registers and the return address below
4957 * the stack pointer, so they don't get scanned by the
4958 * GC. The last frame before swapping the stack pointer is
4959 * organized like the following.
4960 *
4961 * |-----------|<= frame pointer
4962 * | $gp |
4963 * | $s0-8 |
4964 * |-----------|<= stack pointer
4965 * | $ra |
4966 * | align(8) |
4967 * | $f20-30 |
4968 * |-----------|
4969 *
4970 */
4971 enum SZ_GP = 10 * size_t.sizeof; // $gp + $s0-8
4972 enum SZ_RA = size_t.sizeof; // $ra
4973 version (MIPS_HardFloat)
4974 {
4975 enum SZ_FP = 6 * 8; // $f20-30
4976 enum ALIGN = -(SZ_FP + SZ_RA) & (8 - 1);
4977 }
4978 else
4979 {
4980 enum SZ_FP = 0;
4981 enum ALIGN = 0;
4982 }
4983
4984 enum BELOW = SZ_FP + ALIGN + SZ_RA;
4985 enum ABOVE = SZ_GP;
4986 enum SZ = BELOW + ABOVE;
4987
4988 (cast(ubyte*)pstack - SZ)[0 .. SZ] = 0;
4989 pstack -= ABOVE;
4990 *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint;
4991 }
4992 else version (AsmAArch64_Posix)
4993 {
4994 // Like others, FP registers and return address (lr) are kept
4995 // below the saved stack top (tstack) to hide from GC scanning.
4996 // fiber_switchContext expects newp sp to look like this:
4997 // 19: x19
4998 // ...
4999 // 9: x29 (fp) <-- newp tstack
5000 // 8: x30 (lr) [&fiber_entryPoint]
5001 // 7: d8
5002 // ...
5003 // 0: d15
5004
5005 version (StackGrowsDown) {}
5006 else
5007 static assert(false, "Only full descending stacks supported on AArch64");
5008
5009 // Only need to set return address (lr). Everything else is fine
5010 // zero initialized.
5011 pstack -= size_t.sizeof * 11; // skip past x19-x29
5012 push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs
5013 pstack += size_t.sizeof; // adjust sp (newp) above lr
5014 }
5015 else version (AsmARM_Posix)
5016 {
5017 /* We keep the FP registers and the return address below
5018 * the stack pointer, so they don't get scanned by the
5019 * GC. The last frame before swapping the stack pointer is
5020 * organized like the following.
5021 *
5022 * | |-----------|<= 'frame starts here'
5023 * | | fp | (the actual frame pointer, r11 isn't
5024 * | | r10-r4 | updated and still points to the previous frame)
5025 * | |-----------|<= stack pointer
5026 * | | lr |
5027 * | | 4byte pad |
5028 * | | d15-d8 |(if FP supported)
5029 * | |-----------|
5030 * Y
5031 * stack grows down: The pointer value here is smaller than some lines above
5032 */
5033 // frame pointer can be zero, r10-r4 also zero initialized
5034 version (StackGrowsDown)
5035 pstack -= int.sizeof * 8;
5036 else
5037 static assert(false, "Only full descending stacks supported on ARM");
5038
5039 // link register
5040 push( cast(size_t) &fiber_entryPoint );
5041 /*
5042 * We do not push padding and d15-d8 as those are zero initialized anyway
5043 * Position the stack pointer above the lr register
5044 */
5045 pstack += int.sizeof * 1;
5046 }
5047 else version (GNU_AsmX86_Windows)
5048 {
5049 version (StackGrowsDown) {} else static assert( false );
5050
5051 // Currently, MinGW doesn't utilize SEH exceptions.
5052 // See DMD AsmX86_Windows If this code ever becomes fails and SEH is used.
5053
5054 push( 0x00000000 ); // Return address of fiber_entryPoint call
5055 push( cast(size_t) &fiber_entryPoint ); // EIP
5056 push( 0x00000000 ); // EBP
5057 push( 0x00000000 ); // EDI
5058 push( 0x00000000 ); // ESI
5059 push( 0x00000000 ); // EBX
5060 push( 0xFFFFFFFF ); // FS:[0] - Current SEH frame
5061 push( cast(size_t) m_ctxt.bstack ); // FS:[4] - Top of stack
5062 push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8] - Bottom of stack
5063 push( 0x00000000 ); // EAX
5064 }
5065 else version (GNU_AsmX86_64_Windows)
5066 {
5067 push( 0x00000000_00000000 ); // Return address of fiber_entryPoint call
5068 push( cast(size_t) &fiber_entryPoint ); // RIP
5069 push( 0x00000000_00000000 ); // RBP
5070 push( 0x00000000_00000000 ); // RBX
5071 push( 0x00000000_00000000 ); // R12
5072 push( 0x00000000_00000000 ); // R13
5073 push( 0x00000000_00000000 ); // R14
5074 push( 0x00000000_00000000 ); // R15
5075 push( 0xFFFFFFFF_FFFFFFFF ); // GS:[0] - Current SEH frame
5076 version (StackGrowsDown)
5077 {
5078 push( cast(size_t) m_ctxt.bstack ); // GS:[8] - Top of stack
5079 push( cast(size_t) m_ctxt.bstack - m_size ); // GS:[16] - Bottom of stack
5080 }
5081 else
5082 {
5083 push( cast(size_t) m_ctxt.bstack ); // GS:[8] - Top of stack
5084 push( cast(size_t) m_ctxt.bstack + m_size ); // GS:[16] - Bottom of stack
5085 }
5086 }
5087 else static if ( __traits( compiles, ucontext_t ) )
5088 {
5089 getcontext( &m_utxt );
5090 m_utxt.uc_stack.ss_sp = m_pmem;
5091 m_utxt.uc_stack.ss_size = m_size;
5092 makecontext( &m_utxt, &fiber_entryPoint, 0 );
5093 // NOTE: If ucontext is being used then the top of the stack will
5094 // be a pointer to the ucontext_t struct for that fiber.
5095 push( cast(size_t) &m_utxt );
5096 }
5097 else
5098 static assert(0, "Not implemented");
5099 }
5100
5101
5102 Thread.Context* m_ctxt;
5103 size_t m_size;
5104 void* m_pmem;
5105
5106 static if ( __traits( compiles, ucontext_t ) )
5107 {
5108 // NOTE: The static ucontext instance is used to represent the context
5109 // of the executing thread.
5110 static ucontext_t sm_utxt = void;
5111 ucontext_t m_utxt = void;
5112 ucontext_t* m_ucur = null;
5113 }
5114
5115
5116 private:
5117 ///////////////////////////////////////////////////////////////////////////
5118 // Storage of Active Fiber
5119 ///////////////////////////////////////////////////////////////////////////
5120
5121
5122 //
5123 // Sets a thread-local reference to the current fiber object.
5124 //
5125 static void setThis( Fiber f ) nothrow @nogc
5126 {
5127 sm_this = f;
5128 }
5129
5130 static Fiber sm_this;
5131
5132
5133 private:
5134 ///////////////////////////////////////////////////////////////////////////
5135 // Context Switching
5136 ///////////////////////////////////////////////////////////////////////////
5137
5138
5139 //
5140 // Switches into the stack held by this fiber.
5141 //
5142 final void switchIn() nothrow @nogc
5143 {
5144 Thread tobj = Thread.getThis();
5145 void** oldp = &tobj.m_curr.tstack;
5146 void* newp = m_ctxt.tstack;
5147
5148 // NOTE: The order of operations here is very important. The current
5149 // stack top must be stored before m_lock is set, and pushContext
5150 // must not be called until after m_lock is set. This process
5151 // is intended to prevent a race condition with the suspend
5152 // mechanism used for garbage collection. If it is not followed,
5153 // a badly timed collection could cause the GC to scan from the
5154 // bottom of one stack to the top of another, or to miss scanning
5155 // a stack that still contains valid data. The old stack pointer
5156 // oldp will be set again before the context switch to guarantee
5157 // that it points to exactly the correct stack location so the
5158 // successive pop operations will succeed.
5159 *oldp = getStackTop();
5160 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
5161 tobj.pushContext( m_ctxt );
5162
5163 fiber_switchContext( oldp, newp );
5164
5165 // NOTE: As above, these operations must be performed in a strict order
5166 // to prevent Bad Things from happening.
5167 tobj.popContext();
5168 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
5169 tobj.m_curr.tstack = tobj.m_curr.bstack;
5170 }
5171
5172
5173 //
5174 // Switches out of the current stack and into the enclosing stack.
5175 //
5176 final void switchOut() nothrow @nogc
5177 {
5178 Thread tobj = Thread.getThis();
5179 void** oldp = &m_ctxt.tstack;
5180 void* newp = tobj.m_curr.within.tstack;
5181
5182 // NOTE: The order of operations here is very important. The current
5183 // stack top must be stored before m_lock is set, and pushContext
5184 // must not be called until after m_lock is set. This process
5185 // is intended to prevent a race condition with the suspend
5186 // mechanism used for garbage collection. If it is not followed,
5187 // a badly timed collection could cause the GC to scan from the
5188 // bottom of one stack to the top of another, or to miss scanning
5189 // a stack that still contains valid data. The old stack pointer
5190 // oldp will be set again before the context switch to guarantee
5191 // that it points to exactly the correct stack location so the
5192 // successive pop operations will succeed.
5193 *oldp = getStackTop();
5194 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
5195
5196 fiber_switchContext( oldp, newp );
5197
5198 // NOTE: As above, these operations must be performed in a strict order
5199 // to prevent Bad Things from happening.
5200 // NOTE: If use of this fiber is multiplexed across threads, the thread
5201 // executing here may be different from the one above, so get the
5202 // current thread handle before unlocking, etc.
5203 tobj = Thread.getThis();
5204 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
5205 tobj.m_curr.tstack = tobj.m_curr.bstack;
5206 }
5207 }
5208
5209
5210 version (unittest)
5211 {
5212 class TestFiber : Fiber
5213 {
5214 this()
5215 {
5216 super(&run);
5217 }
5218
5219 void run()
5220 {
5221 foreach (i; 0 .. 1000)
5222 {
5223 sum += i;
5224 Fiber.yield();
5225 }
5226 }
5227
5228 enum expSum = 1000 * 999 / 2;
5229 size_t sum;
5230 }
5231
5232 void runTen()
5233 {
5234 TestFiber[10] fibs;
5235 foreach (ref fib; fibs)
5236 fib = new TestFiber();
5237
5238 bool cont;
5239 do {
5240 cont = false;
5241 foreach (fib; fibs) {
5242 if (fib.state == Fiber.State.HOLD)
5243 {
5244 fib.call();
5245 cont |= fib.state != Fiber.State.TERM;
5246 }
5247 }
5248 } while (cont);
5249
5250 foreach (fib; fibs)
5251 {
5252 assert(fib.sum == TestFiber.expSum);
5253 }
5254 }
5255 }
5256
5257
5258 // Single thread running separate fibers
5259 unittest
5260 {
5261 runTen();
5262 }
5263
5264
5265 // Multiple threads running separate fibers
5266 unittest
5267 {
5268 auto group = new ThreadGroup();
5269 foreach (_; 0 .. 4)
5270 {
5271 group.create(&runTen);
5272 }
5273 group.joinAll();
5274 }
5275
5276
5277 // Multiple threads running shared fibers
5278 unittest
5279 {
5280 shared bool[10] locks;
5281 TestFiber[10] fibs;
5282
5283 void runShared()
5284 {
5285 bool cont;
5286 do {
5287 cont = false;
5288 foreach (idx; 0 .. 10)
5289 {
5290 if (cas(&locks[idx], false, true))
5291 {
5292 if (fibs[idx].state == Fiber.State.HOLD)
5293 {
5294 fibs[idx].call();
5295 cont |= fibs[idx].state != Fiber.State.TERM;
5296 }
5297 locks[idx] = false;
5298 }
5299 else
5300 {
5301 cont = true;
5302 }
5303 }
5304 } while (cont);
5305 }
5306
5307 foreach (ref fib; fibs)
5308 {
5309 fib = new TestFiber();
5310 }
5311
5312 auto group = new ThreadGroup();
5313 foreach (_; 0 .. 4)
5314 {
5315 group.create(&runShared);
5316 }
5317 group.joinAll();
5318
5319 foreach (fib; fibs)
5320 {
5321 assert(fib.sum == TestFiber.expSum);
5322 }
5323 }
5324
5325
5326 // Test exception handling inside fibers.
5327 version (Win32) {
5328 // broken on win32 under windows server 2012: bug 13821
5329 } else unittest {
5330 enum MSG = "Test message.";
5331 string caughtMsg;
5332 (new Fiber({
5333 try
5334 {
5335 throw new Exception(MSG);
5336 }
5337 catch (Exception e)
5338 {
5339 caughtMsg = e.msg;
5340 }
5341 })).call();
5342 assert(caughtMsg == MSG);
5343 }
5344
5345
5346 unittest
5347 {
5348 int x = 0;
5349
5350 (new Fiber({
5351 x++;
5352 })).call();
5353 assert( x == 1 );
5354 }
5355
5356 nothrow unittest
5357 {
5358 new Fiber({}).call!(Fiber.Rethrow.no)();
5359 }
5360
5361 unittest
5362 {
5363 new Fiber({}).call(Fiber.Rethrow.yes);
5364 new Fiber({}).call(Fiber.Rethrow.no);
5365 }
5366
5367 deprecated unittest
5368 {
5369 new Fiber({}).call(true);
5370 new Fiber({}).call(false);
5371 }
5372
5373 version (Win32) {
5374 // broken on win32 under windows server 2012: bug 13821
5375 } else unittest {
5376 enum MSG = "Test message.";
5377
5378 try
5379 {
5380 (new Fiber({
5381 throw new Exception( MSG );
5382 })).call();
5383 assert( false, "Expected rethrown exception." );
5384 }
5385 catch ( Throwable t )
5386 {
5387 assert( t.msg == MSG );
5388 }
5389 }
5390
5391 // Test exception chaining when switching contexts in finally blocks.
5392 unittest
5393 {
5394 static void throwAndYield(string msg) {
5395 try {
5396 throw new Exception(msg);
5397 } finally {
5398 Fiber.yield();
5399 }
5400 }
5401
5402 static void fiber(string name) {
5403 try {
5404 try {
5405 throwAndYield(name ~ ".1");
5406 } finally {
5407 throwAndYield(name ~ ".2");
5408 }
5409 } catch (Exception e) {
5410 assert(e.msg == name ~ ".1");
5411 assert(e.next);
5412 assert(e.next.msg == name ~ ".2");
5413 assert(!e.next.next);
5414 }
5415 }
5416
5417 auto first = new Fiber(() => fiber("first"));
5418 auto second = new Fiber(() => fiber("second"));
5419 first.call();
5420 second.call();
5421 first.call();
5422 second.call();
5423 first.call();
5424 second.call();
5425 assert(first.state == Fiber.State.TERM);
5426 assert(second.state == Fiber.State.TERM);
5427 }
5428
5429 // Test Fiber resetting
5430 unittest
5431 {
5432 static string method;
5433
5434 static void foo()
5435 {
5436 method = "foo";
5437 }
5438
5439 void bar()
5440 {
5441 method = "bar";
5442 }
5443
5444 static void expect(Fiber fib, string s)
5445 {
5446 assert(fib.state == Fiber.State.HOLD);
5447 fib.call();
5448 assert(fib.state == Fiber.State.TERM);
5449 assert(method == s); method = null;
5450 }
5451 auto fib = new Fiber(&foo);
5452 expect(fib, "foo");
5453
5454 fib.reset();
5455 expect(fib, "foo");
5456
5457 fib.reset(&foo);
5458 expect(fib, "foo");
5459
5460 fib.reset(&bar);
5461 expect(fib, "bar");
5462
5463 fib.reset(function void(){method = "function";});
5464 expect(fib, "function");
5465
5466 fib.reset(delegate void(){method = "delegate";});
5467 expect(fib, "delegate");
5468 }
5469
5470 // Test unsafe reset in hold state
5471 unittest
5472 {
5473 auto fib = new Fiber(function {ubyte[2048] buf = void; Fiber.yield();}, 4096);
5474 foreach (_; 0 .. 10)
5475 {
5476 fib.call();
5477 assert(fib.state == Fiber.State.HOLD);
5478 fib.reset();
5479 }
5480 }
5481
5482 // stress testing GC stack scanning
5483 unittest
5484 {
5485 import core.memory;
5486
5487 static void unreferencedThreadObject()
5488 {
5489 static void sleep() { Thread.sleep(dur!"msecs"(100)); }
5490 auto thread = new Thread(&sleep).start();
5491 }
5492 unreferencedThreadObject();
5493 GC.collect();
5494
5495 static class Foo
5496 {
5497 this(int value)
5498 {
5499 _value = value;
5500 }
5501
5502 int bar()
5503 {
5504 return _value;
5505 }
5506
5507 int _value;
5508 }
5509
5510 static void collect()
5511 {
5512 auto foo = new Foo(2);
5513 assert(foo.bar() == 2);
5514 GC.collect();
5515 Fiber.yield();
5516 GC.collect();
5517 assert(foo.bar() == 2);
5518 }
5519
5520 auto fiber = new Fiber(&collect);
5521
5522 fiber.call();
5523 GC.collect();
5524 fiber.call();
5525
5526 // thread reference
5527 auto foo = new Foo(2);
5528
5529 void collect2()
5530 {
5531 assert(foo.bar() == 2);
5532 GC.collect();
5533 Fiber.yield();
5534 GC.collect();
5535 assert(foo.bar() == 2);
5536 }
5537
5538 fiber = new Fiber(&collect2);
5539
5540 fiber.call();
5541 GC.collect();
5542 fiber.call();
5543
5544 static void recurse(size_t cnt)
5545 {
5546 --cnt;
5547 Fiber.yield();
5548 if (cnt)
5549 {
5550 auto fib = new Fiber(() { recurse(cnt); });
5551 fib.call();
5552 GC.collect();
5553 fib.call();
5554 }
5555 }
5556 fiber = new Fiber(() { recurse(20); });
5557 fiber.call();
5558 }
5559
5560
5561 version (AsmX86_64_Windows)
5562 {
5563 // Test Windows x64 calling convention
5564 unittest
5565 {
5566 void testNonvolatileRegister(alias REG)()
5567 {
5568 auto zeroRegister = new Fiber(() {
5569 mixin("asm pure nothrow @nogc { naked; xor "~REG~", "~REG~"; ret; }");
5570 });
5571 long after;
5572
5573 mixin("asm pure nothrow @nogc { mov "~REG~", 0xFFFFFFFFFFFFFFFF; }");
5574 zeroRegister.call();
5575 mixin("asm pure nothrow @nogc { mov after, "~REG~"; }");
5576
5577 assert(after == -1);
5578 }
5579
5580 void testNonvolatileRegisterSSE(alias REG)()
5581 {
5582 auto zeroRegister = new Fiber(() {
5583 mixin("asm pure nothrow @nogc { naked; xorpd "~REG~", "~REG~"; ret; }");
5584 });
5585 long[2] before = [0xFFFFFFFF_FFFFFFFF, 0xFFFFFFFF_FFFFFFFF], after;
5586
5587 mixin("asm pure nothrow @nogc { movdqu "~REG~", before; }");
5588 zeroRegister.call();
5589 mixin("asm pure nothrow @nogc { movdqu after, "~REG~"; }");
5590
5591 assert(before == after);
5592 }
5593
5594 testNonvolatileRegister!("R12")();
5595 testNonvolatileRegister!("R13")();
5596 testNonvolatileRegister!("R14")();
5597 testNonvolatileRegister!("R15")();
5598 testNonvolatileRegister!("RDI")();
5599 testNonvolatileRegister!("RSI")();
5600 testNonvolatileRegister!("RBX")();
5601
5602 testNonvolatileRegisterSSE!("XMM6")();
5603 testNonvolatileRegisterSSE!("XMM7")();
5604 testNonvolatileRegisterSSE!("XMM8")();
5605 testNonvolatileRegisterSSE!("XMM9")();
5606 testNonvolatileRegisterSSE!("XMM10")();
5607 testNonvolatileRegisterSSE!("XMM11")();
5608 testNonvolatileRegisterSSE!("XMM12")();
5609 testNonvolatileRegisterSSE!("XMM13")();
5610 testNonvolatileRegisterSSE!("XMM14")();
5611 testNonvolatileRegisterSSE!("XMM15")();
5612 }
5613 }
5614
5615
5616 version (D_InlineAsm_X86_64)
5617 {
5618 unittest
5619 {
5620 void testStackAlignment()
5621 {
5622 void* pRSP;
5623 asm pure nothrow @nogc
5624 {
5625 mov pRSP, RSP;
5626 }
5627 assert((cast(size_t)pRSP & 0xF) == 0);
5628 }
5629
5630 auto fib = new Fiber(&testStackAlignment);
5631 fib.call();
5632 }
5633 }
5634
5635 // regression test for Issue 13416
5636 version (FreeBSD) unittest
5637 {
5638 static void loop()
5639 {
5640 pthread_attr_t attr;
5641 pthread_attr_init(&attr);
5642 auto thr = pthread_self();
5643 foreach (i; 0 .. 50)
5644 pthread_attr_get_np(thr, &attr);
5645 pthread_attr_destroy(&attr);
5646 }
5647
5648 auto thr = new Thread(&loop).start();
5649 foreach (i; 0 .. 50)
5650 {
5651 thread_suspendAll();
5652 thread_resumeAll();
5653 }
5654 thr.join();
5655 }
5656
5657 version (DragonFlyBSD) unittest
5658 {
5659 static void loop()
5660 {
5661 pthread_attr_t attr;
5662 pthread_attr_init(&attr);
5663 auto thr = pthread_self();
5664 foreach (i; 0 .. 50)
5665 pthread_attr_get_np(thr, &attr);
5666 pthread_attr_destroy(&attr);
5667 }
5668
5669 auto thr = new Thread(&loop).start();
5670 foreach (i; 0 .. 50)
5671 {
5672 thread_suspendAll();
5673 thread_resumeAll();
5674 }
5675 thr.join();
5676 }
5677
5678 unittest
5679 {
5680 // use >PAGESIZE to avoid stack overflow (e.g. in an syscall)
5681 auto thr = new Thread(function{}, 4096 + 1).start();
5682 thr.join();
5683 }
5684
5685 /**
5686 * Represents the ID of a thread, as returned by $(D Thread.)$(LREF id).
5687 * The exact type varies from platform to platform.
5688 */
5689 version (Windows)
5690 alias ThreadID = uint;
5691 else
5692 version (Posix)
5693 alias ThreadID = pthread_t;
5694