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