d: Merge upstream dmd, druntime 4c18eed967, phobos d945686a4.
[official-gcc.git] / libphobos / libdruntime / core / thread / fiber.d
blob65878bc1c66be995fbf0a866cbc5061911fdd695
1 /**
2 * The fiber module provides OS-indepedent lightweight threads aka fibers.
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/fiber.d)
12 /* NOTE: This file has been patched from the original DMD distribution to
13 * work with the GDC compiler.
15 module core.thread.fiber;
17 import core.thread.osthread;
18 import core.thread.threadgroup;
19 import core.thread.types;
20 import core.thread.context;
22 import core.memory : pageSize;
24 ///////////////////////////////////////////////////////////////////////////////
25 // Fiber Platform Detection
26 ///////////////////////////////////////////////////////////////////////////////
28 version (GNU)
30 import gcc.builtins;
31 import gcc.config;
32 version (GNU_StackGrowsDown)
33 version = StackGrowsDown;
35 else
37 // this should be true for most architectures
38 version = StackGrowsDown;
41 version (Windows)
43 import core.stdc.stdlib : malloc, free;
44 import core.sys.windows.winbase;
45 import core.sys.windows.winnt;
48 private
50 version (D_InlineAsm_X86)
52 version (Windows)
53 version = AsmX86_Windows;
54 else version (Posix)
55 version = AsmX86_Posix;
57 version = AlignFiberStackTo16Byte;
59 else version (D_InlineAsm_X86_64)
61 version (Windows)
63 version = AsmX86_64_Windows;
64 version = AlignFiberStackTo16Byte;
66 else version (Posix)
68 version = AsmX86_64_Posix;
69 version = AlignFiberStackTo16Byte;
72 else version (X86)
74 version = AlignFiberStackTo16Byte;
76 version (CET)
78 // fiber_switchContext does not support shadow stack from
79 // Intel CET. So use ucontext implementation.
81 else
83 version = AsmExternal;
85 version (MinGW)
86 version = GNU_AsmX86_Windows;
87 else version (OSX)
88 version = AsmX86_Posix;
89 else version (Posix)
90 version = AsmX86_Posix;
93 else version (X86_64)
95 version = AlignFiberStackTo16Byte;
97 version (CET)
99 // fiber_switchContext does not support shadow stack from
100 // Intel CET. So use ucontext implementation.
102 else version (D_X32)
104 // let X32 be handled by ucontext swapcontext
106 else
108 version = AsmExternal;
110 version (MinGW)
111 version = GNU_AsmX86_64_Windows;
112 else version (OSX)
113 version = AsmX86_64_Posix;
114 else version (Posix)
115 version = AsmX86_64_Posix;
118 else version (PPC)
120 version (OSX)
122 version = AsmPPC_Darwin;
123 version = AsmExternal;
124 version = AlignFiberStackTo16Byte;
126 else version (Posix)
128 version = AsmPPC_Posix;
129 version = AsmExternal;
132 else version (PPC64)
134 version (OSX)
136 version = AsmPPC_Darwin;
137 version = AsmExternal;
138 version = AlignFiberStackTo16Byte;
140 else version (Posix)
142 version = AlignFiberStackTo16Byte;
145 else version (MIPS_O32)
147 version (Posix)
149 version = AsmMIPS_O32_Posix;
150 version = AsmExternal;
153 else version (AArch64)
155 version (Posix)
157 version = AsmAArch64_Posix;
158 version = AsmExternal;
159 version = AlignFiberStackTo16Byte;
162 else version (ARM)
164 version (Posix)
166 version = AsmARM_Posix;
167 version = AsmExternal;
170 else version (SPARC)
172 // NOTE: The SPARC ABI specifies only doubleword alignment.
173 version = AlignFiberStackTo16Byte;
175 else version (SPARC64)
177 version = AlignFiberStackTo16Byte;
179 else version (LoongArch64)
181 version = AsmLoongArch64_Posix;
182 version = AsmExternal;
185 version (Posix)
187 version (AsmX86_Windows) {} else
188 version (AsmX86_Posix) {} else
189 version (AsmX86_64_Windows) {} else
190 version (AsmX86_64_Posix) {} else
191 version (AsmExternal) {} else
193 // NOTE: The ucontext implementation requires architecture specific
194 // data definitions to operate so testing for it must be done
195 // by checking for the existence of ucontext_t rather than by
196 // a version identifier. Please note that this is considered
197 // an obsolescent feature according to the POSIX spec, so a
198 // custom solution is still preferred.
199 import core.sys.posix.ucontext;
204 ///////////////////////////////////////////////////////////////////////////////
205 // Fiber Entry Point and Context Switch
206 ///////////////////////////////////////////////////////////////////////////////
208 private
210 import core.atomic : atomicStore, cas, MemoryOrder;
211 import core.exception : onOutOfMemoryError;
212 import core.stdc.stdlib : abort;
214 extern (C) void fiber_entryPoint() nothrow
216 Fiber obj = Fiber.getThis();
217 assert( obj );
219 assert( Thread.getThis().m_curr is obj.m_ctxt );
220 atomicStore!(MemoryOrder.raw)(*cast(shared)&Thread.getThis().m_lock, false);
221 obj.m_ctxt.tstack = obj.m_ctxt.bstack;
222 obj.m_state = Fiber.State.EXEC;
226 obj.run();
228 catch ( Throwable t )
230 obj.m_unhandled = t;
233 static if ( __traits( compiles, ucontext_t ) )
234 obj.m_ucur = &obj.m_utxt;
236 obj.m_state = Fiber.State.TERM;
237 obj.switchOut();
240 // Look above the definition of 'class Fiber' for some information about the implementation of this routine
241 version (AsmExternal)
243 extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;
244 version (AArch64)
245 extern (C) void fiber_trampoline() nothrow;
247 else
248 extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc
250 // NOTE: The data pushed and popped in this routine must match the
251 // default stack created by Fiber.initStack or the initial
252 // switch into a new context will fail.
254 version (AsmX86_Windows)
256 asm pure nothrow @nogc
258 naked;
260 // save current stack state
261 push EBP;
262 mov EBP, ESP;
263 push EDI;
264 push ESI;
265 push EBX;
266 push dword ptr FS:[0];
267 push dword ptr FS:[4];
268 push dword ptr FS:[8];
269 push EAX;
271 // store oldp again with more accurate address
272 mov EAX, dword ptr 8[EBP];
273 mov [EAX], ESP;
274 // load newp to begin context switch
275 mov ESP, dword ptr 12[EBP];
277 // load saved state from new stack
278 pop EAX;
279 pop dword ptr FS:[8];
280 pop dword ptr FS:[4];
281 pop dword ptr FS:[0];
282 pop EBX;
283 pop ESI;
284 pop EDI;
285 pop EBP;
287 // 'return' to complete switch
288 pop ECX;
289 jmp ECX;
292 else version (AsmX86_64_Windows)
294 asm pure nothrow @nogc
296 naked;
298 // save current stack state
299 // NOTE: When changing the layout of registers on the stack,
300 // make sure that the XMM registers are still aligned.
301 // On function entry, the stack is guaranteed to not
302 // be aligned to 16 bytes because of the return address
303 // on the stack.
304 push RBP;
305 mov RBP, RSP;
306 push R12;
307 push R13;
308 push R14;
309 push R15;
310 push RDI;
311 push RSI;
312 // 7 registers = 56 bytes; stack is now aligned to 16 bytes
313 sub RSP, 160;
314 movdqa [RSP + 144], XMM6;
315 movdqa [RSP + 128], XMM7;
316 movdqa [RSP + 112], XMM8;
317 movdqa [RSP + 96], XMM9;
318 movdqa [RSP + 80], XMM10;
319 movdqa [RSP + 64], XMM11;
320 movdqa [RSP + 48], XMM12;
321 movdqa [RSP + 32], XMM13;
322 movdqa [RSP + 16], XMM14;
323 movdqa [RSP], XMM15;
324 push RBX;
325 xor RAX,RAX;
326 push qword ptr GS:[RAX];
327 push qword ptr GS:8[RAX];
328 push qword ptr GS:16[RAX];
330 // store oldp
331 mov [RCX], RSP;
332 // load newp to begin context switch
333 mov RSP, RDX;
335 // load saved state from new stack
336 pop qword ptr GS:16[RAX];
337 pop qword ptr GS:8[RAX];
338 pop qword ptr GS:[RAX];
339 pop RBX;
340 movdqa XMM15, [RSP];
341 movdqa XMM14, [RSP + 16];
342 movdqa XMM13, [RSP + 32];
343 movdqa XMM12, [RSP + 48];
344 movdqa XMM11, [RSP + 64];
345 movdqa XMM10, [RSP + 80];
346 movdqa XMM9, [RSP + 96];
347 movdqa XMM8, [RSP + 112];
348 movdqa XMM7, [RSP + 128];
349 movdqa XMM6, [RSP + 144];
350 add RSP, 160;
351 pop RSI;
352 pop RDI;
353 pop R15;
354 pop R14;
355 pop R13;
356 pop R12;
357 pop RBP;
359 // 'return' to complete switch
360 pop RCX;
361 jmp RCX;
364 else version (AsmX86_Posix)
366 asm pure nothrow @nogc
368 naked;
370 // save current stack state
371 push EBP;
372 mov EBP, ESP;
373 push EDI;
374 push ESI;
375 push EBX;
376 push EAX;
378 // store oldp again with more accurate address
379 mov EAX, dword ptr 8[EBP];
380 mov [EAX], ESP;
381 // load newp to begin context switch
382 mov ESP, dword ptr 12[EBP];
384 // load saved state from new stack
385 pop EAX;
386 pop EBX;
387 pop ESI;
388 pop EDI;
389 pop EBP;
391 // 'return' to complete switch
392 pop ECX;
393 jmp ECX;
396 else version (AsmX86_64_Posix)
398 asm pure nothrow @nogc
400 naked;
402 // save current stack state
403 push RBP;
404 mov RBP, RSP;
405 push RBX;
406 push R12;
407 push R13;
408 push R14;
409 push R15;
411 // store oldp
412 mov [RDI], RSP;
413 // load newp to begin context switch
414 mov RSP, RSI;
416 // load saved state from new stack
417 pop R15;
418 pop R14;
419 pop R13;
420 pop R12;
421 pop RBX;
422 pop RBP;
424 // 'return' to complete switch
425 pop RCX;
426 jmp RCX;
429 else static if ( __traits( compiles, ucontext_t ) )
431 Fiber cfib = Fiber.getThis();
432 void* ucur = cfib.m_ucur;
434 *oldp = &ucur;
435 swapcontext( **(cast(ucontext_t***) oldp),
436 *(cast(ucontext_t**) newp) );
438 else
439 static assert(0, "Not implemented");
444 ///////////////////////////////////////////////////////////////////////////////
445 // Fiber
446 ///////////////////////////////////////////////////////////////////////////////
448 * Documentation of Fiber internals:
450 * The main routines to implement when porting Fibers to new architectures are
451 * fiber_switchContext and initStack. Some version constants have to be defined
452 * for the new platform as well, search for "Fiber Platform Detection and Memory Allocation".
454 * Fibers are based on a concept called 'Context'. A Context describes the execution
455 * state of a Fiber or main thread which is fully described by the stack, some
456 * registers and a return address at which the Fiber/Thread should continue executing.
457 * Please note that not only each Fiber has a Context, but each thread also has got a
458 * Context which describes the threads stack and state. If you call Fiber fib; fib.call
459 * the first time in a thread you switch from Threads Context into the Fibers Context.
460 * If you call fib.yield in that Fiber you switch out of the Fibers context and back
461 * into the Thread Context. (However, this is not always the case. You can call a Fiber
462 * from within another Fiber, then you switch Contexts between the Fibers and the Thread
463 * Context is not involved)
465 * In all current implementations the registers and the return address are actually
466 * saved on a Contexts stack.
468 * The fiber_switchContext routine has got two parameters:
469 * void** a: This is the _location_ where we have to store the current stack pointer,
470 * the stack pointer of the currently executing Context (Fiber or Thread).
471 * void* b: This is the pointer to the stack of the Context which we want to switch into.
472 * Note that we get the same pointer here as the one we stored into the void** a
473 * in a previous call to fiber_switchContext.
475 * In the simplest case, a fiber_switchContext rountine looks like this:
476 * fiber_switchContext:
477 * push {return Address}
478 * push {registers}
479 * copy {stack pointer} into {location pointed to by a}
480 * //We have now switch to the stack of a different Context!
481 * copy {b} into {stack pointer}
482 * pop {registers}
483 * pop {return Address}
484 * jump to {return Address}
486 * The GC uses the value returned in parameter a to scan the Fibers stack. It scans from
487 * the stack base to that value. As the GC dislikes false pointers we can actually optimize
488 * this a little: By storing registers which can not contain references to memory managed
489 * by the GC outside of the region marked by the stack base pointer and the stack pointer
490 * saved in fiber_switchContext we can prevent the GC from scanning them.
491 * Such registers are usually floating point registers and the return address. In order to
492 * implement this, we return a modified stack pointer from fiber_switchContext. However,
493 * we have to remember that when we restore the registers from the stack!
495 * --------------------------- <= Stack Base
496 * | Frame | <= Many other stack frames
497 * | Frame |
498 * |-------------------------| <= The last stack frame. This one is created by fiber_switchContext
499 * | registers with pointers |
500 * | | <= Stack pointer. GC stops scanning here
501 * | return address |
502 * |floating point registers |
503 * --------------------------- <= Real Stack End
505 * fiber_switchContext:
506 * push {registers with pointers}
507 * copy {stack pointer} into {location pointed to by a}
508 * push {return Address}
509 * push {Floating point registers}
510 * //We have now switch to the stack of a different Context!
511 * copy {b} into {stack pointer}
512 * //We now have to adjust the stack pointer to point to 'Real Stack End' so we can pop
513 * //the FP registers
514 * //+ or - depends on if your stack grows downwards or upwards
515 * {stack pointer} = {stack pointer} +- ({FPRegisters}.sizeof + {return address}.sizeof}
516 * pop {Floating point registers}
517 * pop {return Address}
518 * pop {registers with pointers}
519 * jump to {return Address}
521 * So the question now is which registers need to be saved? This depends on the specific
522 * architecture ABI of course, but here are some general guidelines:
523 * - If a register is callee-save (if the callee modifies the register it must saved and
524 * restored by the callee) it needs to be saved/restored in switchContext
525 * - If a register is caller-save it needn't be saved/restored. (Calling fiber_switchContext
526 * is a function call and the compiler therefore already must save these registers before
527 * calling fiber_switchContext)
528 * - Argument registers used for passing parameters to functions needn't be saved/restored
529 * - The return register needn't be saved/restored (fiber_switchContext hasn't got a return type)
530 * - All scratch registers needn't be saved/restored
531 * - The link register usually needn't be saved/restored (but sometimes it must be cleared -
532 * see below for details)
533 * - The frame pointer register - if it exists - is usually callee-save
534 * - All current implementations do not save control registers
536 * What happens on the first switch into a Fiber? We never saved a state for this fiber before,
537 * but the initial state is prepared in the initStack routine. (This routine will also be called
538 * when a Fiber is being resetted). initStack must produce exactly the same stack layout as the
539 * part of fiber_switchContext which saves the registers. Pay special attention to set the stack
540 * pointer correctly if you use the GC optimization mentioned before. the return Address saved in
541 * initStack must be the address of fiber_entrypoint.
543 * There's now a small but important difference between the first context switch into a fiber and
544 * further context switches. On the first switch, Fiber.call is used and the returnAddress in
545 * fiber_switchContext will point to fiber_entrypoint. The important thing here is that this jump
546 * is a _function call_, we call fiber_entrypoint by jumping before it's function prologue. On later
547 * calls, the user used yield() in a function, and therefore the return address points into a user
548 * function, after the yield call. So here the jump in fiber_switchContext is a _function return_,
549 * not a function call!
551 * The most important result of this is that on entering a function, i.e. fiber_entrypoint, we
552 * would have to provide a return address / set the link register once fiber_entrypoint
553 * returns. Now fiber_entrypoint does never return and therefore the actual value of the return
554 * address / link register is never read/used and therefore doesn't matter. When fiber_switchContext
555 * performs a _function return_ the value in the link register doesn't matter either.
556 * However, the link register will still be saved to the stack in fiber_entrypoint and some
557 * exception handling / stack unwinding code might read it from this stack location and crash.
558 * The exact solution depends on your architecture, but see the ARM implementation for a way
559 * to deal with this issue.
561 * The ARM implementation is meant to be used as a kind of documented example implementation.
562 * Look there for a concrete example.
564 * FIXME: fiber_entrypoint might benefit from a @noreturn attribute, but D doesn't have one.
568 * This class provides a cooperative concurrency mechanism integrated with the
569 * threading and garbage collection functionality. Calling a fiber may be
570 * considered a blocking operation that returns when the fiber yields (via
571 * Fiber.yield()). Execution occurs within the context of the calling thread
572 * so synchronization is not necessary to guarantee memory visibility so long
573 * as the same thread calls the fiber each time. Please note that there is no
574 * requirement that a fiber be bound to one specific thread. Rather, fibers
575 * may be freely passed between threads so long as they are not currently
576 * executing. Like threads, a new fiber thread may be created using either
577 * derivation or composition, as in the following example.
579 * Warning:
580 * Status registers are not saved by the current implementations. This means
581 * floating point exception status bits (overflow, divide by 0), rounding mode
582 * and similar stuff is set per-thread, not per Fiber!
584 * Warning:
585 * On ARM FPU registers are not saved if druntime was compiled as ARM_SoftFloat.
586 * If such a build is used on a ARM_SoftFP system which actually has got a FPU
587 * and other libraries are using the FPU registers (other code is compiled
588 * as ARM_SoftFP) this can cause problems. Druntime must be compiled as
589 * ARM_SoftFP in this case.
591 * Authors: Based on a design by Mikola Lysenko.
593 class Fiber
595 ///////////////////////////////////////////////////////////////////////////
596 // Initialization
597 ///////////////////////////////////////////////////////////////////////////
599 version (Windows)
600 // exception handling walks the stack, invoking DbgHelp.dll which
601 // needs up to 16k of stack space depending on the version of DbgHelp.dll,
602 // the existence of debug symbols and other conditions. Avoid causing
603 // stack overflows by defaulting to a larger stack size
604 enum defaultStackPages = 8;
605 else version (OSX)
607 version (X86_64)
608 // libunwind on macOS 11 now requires more stack space than 16k, so
609 // default to a larger stack size. This is only applied to X86 as
610 // the pageSize is still 4k, however on AArch64 it is 16k.
611 enum defaultStackPages = 8;
612 else
613 enum defaultStackPages = 4;
615 else
616 enum defaultStackPages = 4;
619 * Initializes a fiber object which is associated with a static
620 * D function.
622 * Params:
623 * fn = The fiber function.
624 * sz = The stack size for this fiber.
625 * guardPageSize = size of the guard page to trap fiber's stack
626 * overflows. Beware that using this will increase
627 * the number of mmaped regions on platforms using mmap
628 * so an OS-imposed limit may be hit.
630 * In:
631 * fn must not be null.
633 this( void function() fn, size_t sz = pageSize * defaultStackPages,
634 size_t guardPageSize = pageSize ) nothrow
637 assert( fn );
641 allocStack( sz, guardPageSize );
642 reset( fn );
647 * Initializes a fiber object which is associated with a dynamic
648 * D function.
650 * Params:
651 * dg = The fiber function.
652 * sz = The stack size for this fiber.
653 * guardPageSize = size of the guard page to trap fiber's stack
654 * overflows. Beware that using this will increase
655 * the number of mmaped regions on platforms using mmap
656 * so an OS-imposed limit may be hit.
658 * In:
659 * dg must not be null.
661 this( void delegate() dg, size_t sz = pageSize * defaultStackPages,
662 size_t guardPageSize = pageSize ) nothrow
664 allocStack( sz, guardPageSize );
665 reset( cast(void delegate() const) dg );
670 * Cleans up any remaining resources used by this object.
672 ~this() nothrow @nogc
674 // NOTE: A live reference to this object will exist on its associated
675 // stack from the first time its call() method has been called
676 // until its execution completes with State.TERM. Thus, the only
677 // times this dtor should be called are either if the fiber has
678 // terminated (and therefore has no active stack) or if the user
679 // explicitly deletes this object. The latter case is an error
680 // but is not easily tested for, since State.HOLD may imply that
681 // the fiber was just created but has never been run. There is
682 // not a compelling case to create a State.INIT just to offer a
683 // means of ensuring the user isn't violating this object's
684 // contract, so for now this requirement will be enforced by
685 // documentation only.
686 freeStack();
690 ///////////////////////////////////////////////////////////////////////////
691 // General Actions
692 ///////////////////////////////////////////////////////////////////////////
696 * Transfers execution to this fiber object. The calling context will be
697 * suspended until the fiber calls Fiber.yield() or until it terminates
698 * via an unhandled exception.
700 * Params:
701 * rethrow = Rethrow any unhandled exception which may have caused this
702 * fiber to terminate.
704 * In:
705 * This fiber must be in state HOLD.
707 * Throws:
708 * Any exception not handled by the joined thread.
710 * Returns:
711 * Any exception not handled by this fiber if rethrow = false, null
712 * otherwise.
714 // Not marked with any attributes, even though `nothrow @nogc` works
715 // because it calls arbitrary user code. Most of the implementation
716 // is already `@nogc nothrow`, but in order for `Fiber.call` to
717 // propagate the attributes of the user's function, the Fiber
718 // class needs to be templated.
719 final Throwable call( Rethrow rethrow = Rethrow.yes )
721 return rethrow ? call!(Rethrow.yes)() : call!(Rethrow.no);
724 /// ditto
725 final Throwable call( Rethrow rethrow )()
727 callImpl();
728 if ( m_unhandled )
730 Throwable t = m_unhandled;
731 m_unhandled = null;
732 static if ( rethrow )
733 throw t;
734 else
735 return t;
737 return null;
740 private void callImpl() nothrow @nogc
743 assert( m_state == State.HOLD );
747 Fiber cur = getThis();
749 static if ( __traits( compiles, ucontext_t ) )
750 m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt;
752 setThis( this );
753 this.switchIn();
754 setThis( cur );
756 static if ( __traits( compiles, ucontext_t ) )
757 m_ucur = null;
759 // NOTE: If the fiber has terminated then the stack pointers must be
760 // reset. This ensures that the stack for this fiber is not
761 // scanned if the fiber has terminated. This is necessary to
762 // prevent any references lingering on the stack from delaying
763 // the collection of otherwise dead objects. The most notable
764 // being the current object, which is referenced at the top of
765 // fiber_entryPoint.
766 if ( m_state == State.TERM )
768 m_ctxt.tstack = m_ctxt.bstack;
772 /// Flag to control rethrow behavior of $(D $(LREF call))
773 enum Rethrow : bool { no, yes }
776 * Resets this fiber so that it may be re-used, optionally with a
777 * new function/delegate. This routine should only be called for
778 * fibers that have terminated, as doing otherwise could result in
779 * scope-dependent functionality that is not executed.
780 * Stack-based classes, for example, may not be cleaned up
781 * properly if a fiber is reset before it has terminated.
783 * In:
784 * This fiber must be in state TERM or HOLD.
786 final void reset() nothrow @nogc
789 assert( m_state == State.TERM || m_state == State.HOLD );
793 m_ctxt.tstack = m_ctxt.bstack;
794 m_state = State.HOLD;
795 initStack();
796 m_unhandled = null;
799 /// ditto
800 final void reset( void function() fn ) nothrow @nogc
802 reset();
803 m_call = fn;
806 /// ditto
807 final void reset( void delegate() dg ) nothrow @nogc
809 reset();
810 m_call = dg;
813 ///////////////////////////////////////////////////////////////////////////
814 // General Properties
815 ///////////////////////////////////////////////////////////////////////////
818 /// A fiber may occupy one of three states: HOLD, EXEC, and TERM.
819 enum State
821 /** The HOLD state applies to any fiber that is suspended and ready to
822 be called. */
823 HOLD,
824 /** The EXEC state will be set for any fiber that is currently
825 executing. */
826 EXEC,
827 /** The TERM state is set when a fiber terminates. Once a fiber
828 terminates, it must be reset before it may be called again. */
829 TERM
834 * Gets the current state of this fiber.
836 * Returns:
837 * The state of this fiber as an enumerated value.
839 final @property State state() const @safe pure nothrow @nogc
841 return m_state;
845 ///////////////////////////////////////////////////////////////////////////
846 // Actions on Calling Fiber
847 ///////////////////////////////////////////////////////////////////////////
851 * Forces a context switch to occur away from the calling fiber.
853 static void yield() nothrow @nogc
855 Fiber cur = getThis();
856 assert( cur, "Fiber.yield() called with no active fiber" );
857 assert( cur.m_state == State.EXEC );
859 static if ( __traits( compiles, ucontext_t ) )
860 cur.m_ucur = &cur.m_utxt;
862 cur.m_state = State.HOLD;
863 cur.switchOut();
864 cur.m_state = State.EXEC;
869 * Forces a context switch to occur away from the calling fiber and then
870 * throws obj in the calling fiber.
872 * Params:
873 * t = The object to throw.
875 * In:
876 * t must not be null.
878 static void yieldAndThrow( Throwable t ) nothrow @nogc
881 assert( t );
885 Fiber cur = getThis();
886 assert( cur, "Fiber.yield() called with no active fiber" );
887 assert( cur.m_state == State.EXEC );
889 static if ( __traits( compiles, ucontext_t ) )
890 cur.m_ucur = &cur.m_utxt;
892 cur.m_unhandled = t;
893 cur.m_state = State.HOLD;
894 cur.switchOut();
895 cur.m_state = State.EXEC;
899 ///////////////////////////////////////////////////////////////////////////
900 // Fiber Accessors
901 ///////////////////////////////////////////////////////////////////////////
905 * Provides a reference to the calling fiber or null if no fiber is
906 * currently active.
908 * Returns:
909 * The fiber object representing the calling fiber or null if no fiber
910 * is currently active within this thread. The result of deleting this object is undefined.
912 static Fiber getThis() @safe nothrow @nogc
914 version (GNU) pragma(inline, false);
915 return sm_this;
919 ///////////////////////////////////////////////////////////////////////////
920 // Static Initialization
921 ///////////////////////////////////////////////////////////////////////////
924 version (Posix)
926 static this()
928 static if ( __traits( compiles, ucontext_t ) )
930 int status = getcontext( &sm_utxt );
931 assert( status == 0 );
936 private:
939 // Fiber entry point. Invokes the function or delegate passed on
940 // construction (if any).
942 final void run()
944 m_call();
948 // Standard fiber data
950 Callable m_call;
951 bool m_isRunning;
952 Throwable m_unhandled;
953 State m_state;
956 private:
957 ///////////////////////////////////////////////////////////////////////////
958 // Stack Management
959 ///////////////////////////////////////////////////////////////////////////
963 // Allocate a new stack for this fiber.
965 final void allocStack( size_t sz, size_t guardPageSize ) nothrow
968 assert( !m_pmem && !m_ctxt );
972 // adjust alloc size to a multiple of pageSize
973 sz += pageSize - 1;
974 sz -= sz % pageSize;
976 // NOTE: This instance of Thread.Context is dynamic so Fiber objects
977 // can be collected by the GC so long as no user level references
978 // to the object exist. If m_ctxt were not dynamic then its
979 // presence in the global context list would be enough to keep
980 // this object alive indefinitely. An alternative to allocating
981 // room for this struct explicitly would be to mash it into the
982 // base of the stack being allocated below. However, doing so
983 // requires too much special logic to be worthwhile.
984 m_ctxt = new StackContext;
986 version (Windows)
988 // reserve memory for stack
989 m_pmem = VirtualAlloc( null,
990 sz + guardPageSize,
991 MEM_RESERVE,
992 PAGE_NOACCESS );
993 if ( !m_pmem )
994 onOutOfMemoryError();
996 version (StackGrowsDown)
998 void* stack = m_pmem + guardPageSize;
999 void* guard = m_pmem;
1000 void* pbase = stack + sz;
1002 else
1004 void* stack = m_pmem;
1005 void* guard = m_pmem + sz;
1006 void* pbase = stack;
1009 // allocate reserved stack segment
1010 stack = VirtualAlloc( stack,
1012 MEM_COMMIT,
1013 PAGE_READWRITE );
1014 if ( !stack )
1015 onOutOfMemoryError();
1017 if (guardPageSize)
1019 // allocate reserved guard page
1020 guard = VirtualAlloc( guard,
1021 guardPageSize,
1022 MEM_COMMIT,
1023 PAGE_READWRITE | PAGE_GUARD );
1024 if ( !guard )
1025 onOutOfMemoryError();
1028 m_ctxt.bstack = pbase;
1029 m_ctxt.tstack = pbase;
1030 m_size = sz;
1032 else
1034 version (Posix) import core.sys.posix.sys.mman; // mmap, MAP_ANON
1036 static if ( __traits( compiles, ucontext_t ) )
1038 // Stack size must be at least the minimum allowable by the OS.
1039 if (sz < MINSIGSTKSZ)
1040 sz = MINSIGSTKSZ;
1043 static if ( __traits( compiles, mmap ) )
1045 // Allocate more for the memory guard
1046 sz += guardPageSize;
1048 int mmap_flags = MAP_PRIVATE | MAP_ANON;
1049 version (OpenBSD)
1050 mmap_flags |= MAP_STACK;
1052 m_pmem = mmap( null,
1054 PROT_READ | PROT_WRITE,
1055 mmap_flags,
1057 0 );
1058 if ( m_pmem == MAP_FAILED )
1059 m_pmem = null;
1061 else static if ( __traits( compiles, valloc ) )
1063 m_pmem = valloc( sz );
1065 else static if ( __traits( compiles, malloc ) )
1067 m_pmem = malloc( sz );
1069 else
1071 m_pmem = null;
1074 if ( !m_pmem )
1075 onOutOfMemoryError();
1077 version (StackGrowsDown)
1079 m_ctxt.bstack = m_pmem + sz;
1080 m_ctxt.tstack = m_pmem + sz;
1081 void* guard = m_pmem;
1083 else
1085 m_ctxt.bstack = m_pmem;
1086 m_ctxt.tstack = m_pmem;
1087 void* guard = m_pmem + sz - guardPageSize;
1089 m_size = sz;
1091 static if ( __traits( compiles, mmap ) )
1093 if (guardPageSize)
1095 // protect end of stack
1096 if ( mprotect(guard, guardPageSize, PROT_NONE) == -1 )
1097 abort();
1100 else
1102 // Supported only for mmap allocated memory - results are
1103 // undefined if applied to memory not obtained by mmap
1107 Thread.add( m_ctxt );
1112 // Free this fiber's stack.
1114 final void freeStack() nothrow @nogc
1117 assert( m_pmem && m_ctxt );
1121 // NOTE: m_ctxt is guaranteed to be alive because it is held in the
1122 // global context list.
1123 Thread.slock.lock_nothrow();
1124 scope(exit) Thread.slock.unlock_nothrow();
1125 Thread.remove( m_ctxt );
1127 version (Windows)
1129 VirtualFree( m_pmem, 0, MEM_RELEASE );
1131 else
1133 import core.sys.posix.sys.mman; // munmap
1135 static if ( __traits( compiles, mmap ) )
1137 munmap( m_pmem, m_size );
1139 else static if ( __traits( compiles, valloc ) )
1141 free( m_pmem );
1143 else static if ( __traits( compiles, malloc ) )
1145 free( m_pmem );
1148 m_pmem = null;
1149 m_ctxt = null;
1154 // Initialize the allocated stack.
1155 // Look above the definition of 'class Fiber' for some information about the implementation of this routine
1157 final void initStack() nothrow @nogc
1160 assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack );
1161 assert( cast(size_t) m_ctxt.bstack % (void*).sizeof == 0 );
1165 void* pstack = m_ctxt.tstack;
1166 scope( exit ) m_ctxt.tstack = pstack;
1168 void push( size_t val ) nothrow
1170 version (StackGrowsDown)
1172 pstack -= size_t.sizeof;
1173 *(cast(size_t*) pstack) = val;
1175 else
1177 pstack += size_t.sizeof;
1178 *(cast(size_t*) pstack) = val;
1182 // NOTE: On OS X the stack must be 16-byte aligned according
1183 // to the IA-32 call spec. For x86_64 the stack also needs to
1184 // be aligned to 16-byte according to SysV AMD64 ABI.
1185 version (AlignFiberStackTo16Byte)
1187 version (StackGrowsDown)
1189 pstack = cast(void*)(cast(size_t)(pstack) - (cast(size_t)(pstack) & 0x0F));
1191 else
1193 pstack = cast(void*)(cast(size_t)(pstack) + (cast(size_t)(pstack) & 0x0F));
1197 version (AsmX86_Windows)
1199 version (StackGrowsDown) {} else static assert( false );
1201 // On Windows Server 2008 and 2008 R2, an exploit mitigation
1202 // technique known as SEHOP is activated by default. To avoid
1203 // hijacking of the exception handler chain, the presence of a
1204 // Windows-internal handler (ntdll.dll!FinalExceptionHandler) at
1205 // its end is tested by RaiseException. If it is not present, all
1206 // handlers are disregarded, and the program is thus aborted
1207 // (see http://blogs.technet.com/b/srd/archive/2009/02/02/
1208 // preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx).
1209 // For new threads, this handler is installed by Windows immediately
1210 // after creation. To make exception handling work in fibers, we
1211 // have to insert it for our new stacks manually as well.
1213 // To do this, we first determine the handler by traversing the SEH
1214 // chain of the current thread until its end, and then construct a
1215 // registration block for the last handler on the newly created
1216 // thread. We then continue to push all the initial register values
1217 // for the first context switch as for the other implementations.
1219 // Note that this handler is never actually invoked, as we install
1220 // our own one on top of it in the fiber entry point function.
1221 // Thus, it should not have any effects on OSes not implementing
1222 // exception chain verification.
1224 alias fp_t = void function(); // Actual signature not relevant.
1225 static struct EXCEPTION_REGISTRATION
1227 EXCEPTION_REGISTRATION* next; // sehChainEnd if last one.
1228 fp_t handler;
1230 enum sehChainEnd = cast(EXCEPTION_REGISTRATION*) 0xFFFFFFFF;
1232 __gshared static fp_t finalHandler = null;
1233 if ( finalHandler is null )
1235 static EXCEPTION_REGISTRATION* fs0() nothrow
1237 asm pure nothrow @nogc
1239 naked;
1240 mov EAX, FS:[0];
1241 ret;
1244 auto reg = fs0();
1245 while ( reg.next != sehChainEnd ) reg = reg.next;
1247 // Benign races are okay here, just to avoid re-lookup on every
1248 // fiber creation.
1249 finalHandler = reg.handler;
1252 // When linking with /safeseh (supported by LDC, but not DMD)
1253 // the exception chain must not extend to the very top
1254 // of the stack, otherwise the exception chain is also considered
1255 // invalid. Reserving additional 4 bytes at the top of the stack will
1256 // keep the EXCEPTION_REGISTRATION below that limit
1257 size_t reserve = EXCEPTION_REGISTRATION.sizeof + 4;
1258 pstack -= reserve;
1259 *(cast(EXCEPTION_REGISTRATION*)pstack) =
1260 EXCEPTION_REGISTRATION( sehChainEnd, finalHandler );
1261 auto pChainEnd = pstack;
1263 push( cast(size_t) &fiber_entryPoint ); // EIP
1264 push( cast(size_t) m_ctxt.bstack - reserve ); // EBP
1265 push( 0x00000000 ); // EDI
1266 push( 0x00000000 ); // ESI
1267 push( 0x00000000 ); // EBX
1268 push( cast(size_t) pChainEnd ); // FS:[0]
1269 push( cast(size_t) m_ctxt.bstack ); // FS:[4]
1270 push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8]
1271 push( 0x00000000 ); // EAX
1273 else version (AsmX86_64_Windows)
1275 // Using this trampoline instead of the raw fiber_entryPoint
1276 // ensures that during context switches, source and destination
1277 // stacks have the same alignment. Otherwise, the stack would need
1278 // to be shifted by 8 bytes for the first call, as fiber_entryPoint
1279 // is an actual function expecting a stack which is not aligned
1280 // to 16 bytes.
1281 static void trampoline()
1283 asm pure nothrow @nogc
1285 naked;
1286 sub RSP, 32; // Shadow space (Win64 calling convention)
1287 call fiber_entryPoint;
1288 xor RCX, RCX; // This should never be reached, as
1289 jmp RCX; // fiber_entryPoint must never return.
1293 push( cast(size_t) &trampoline ); // RIP
1294 push( 0x00000000_00000000 ); // RBP
1295 push( 0x00000000_00000000 ); // R12
1296 push( 0x00000000_00000000 ); // R13
1297 push( 0x00000000_00000000 ); // R14
1298 push( 0x00000000_00000000 ); // R15
1299 push( 0x00000000_00000000 ); // RDI
1300 push( 0x00000000_00000000 ); // RSI
1301 push( 0x00000000_00000000 ); // XMM6 (high)
1302 push( 0x00000000_00000000 ); // XMM6 (low)
1303 push( 0x00000000_00000000 ); // XMM7 (high)
1304 push( 0x00000000_00000000 ); // XMM7 (low)
1305 push( 0x00000000_00000000 ); // XMM8 (high)
1306 push( 0x00000000_00000000 ); // XMM8 (low)
1307 push( 0x00000000_00000000 ); // XMM9 (high)
1308 push( 0x00000000_00000000 ); // XMM9 (low)
1309 push( 0x00000000_00000000 ); // XMM10 (high)
1310 push( 0x00000000_00000000 ); // XMM10 (low)
1311 push( 0x00000000_00000000 ); // XMM11 (high)
1312 push( 0x00000000_00000000 ); // XMM11 (low)
1313 push( 0x00000000_00000000 ); // XMM12 (high)
1314 push( 0x00000000_00000000 ); // XMM12 (low)
1315 push( 0x00000000_00000000 ); // XMM13 (high)
1316 push( 0x00000000_00000000 ); // XMM13 (low)
1317 push( 0x00000000_00000000 ); // XMM14 (high)
1318 push( 0x00000000_00000000 ); // XMM14 (low)
1319 push( 0x00000000_00000000 ); // XMM15 (high)
1320 push( 0x00000000_00000000 ); // XMM15 (low)
1321 push( 0x00000000_00000000 ); // RBX
1322 push( 0xFFFFFFFF_FFFFFFFF ); // GS:[0]
1323 version (StackGrowsDown)
1325 push( cast(size_t) m_ctxt.bstack ); // GS:[8]
1326 push( cast(size_t) m_ctxt.bstack - m_size ); // GS:[16]
1328 else
1330 push( cast(size_t) m_ctxt.bstack ); // GS:[8]
1331 push( cast(size_t) m_ctxt.bstack + m_size ); // GS:[16]
1334 else version (AsmX86_Posix)
1336 push( 0x00000000 ); // Return address of fiber_entryPoint call
1337 push( cast(size_t) &fiber_entryPoint ); // EIP
1338 push( cast(size_t) m_ctxt.bstack ); // EBP
1339 push( 0x00000000 ); // EDI
1340 push( 0x00000000 ); // ESI
1341 push( 0x00000000 ); // EBX
1342 push( 0x00000000 ); // EAX
1344 else version (AsmX86_64_Posix)
1346 push( 0x00000000_00000000 ); // Return address of fiber_entryPoint call
1347 push( cast(size_t) &fiber_entryPoint ); // RIP
1348 push( cast(size_t) m_ctxt.bstack ); // RBP
1349 push( 0x00000000_00000000 ); // RBX
1350 push( 0x00000000_00000000 ); // R12
1351 push( 0x00000000_00000000 ); // R13
1352 push( 0x00000000_00000000 ); // R14
1353 push( 0x00000000_00000000 ); // R15
1355 else version (AsmPPC_Posix)
1357 version (StackGrowsDown)
1359 pstack -= int.sizeof * 5;
1361 else
1363 pstack += int.sizeof * 5;
1366 push( cast(size_t) &fiber_entryPoint ); // link register
1367 push( 0x00000000 ); // control register
1368 push( 0x00000000 ); // old stack pointer
1370 // GPR values
1371 version (StackGrowsDown)
1373 pstack -= int.sizeof * 20;
1375 else
1377 pstack += int.sizeof * 20;
1380 assert( (cast(size_t) pstack & 0x0f) == 0 );
1382 else version (AsmPPC_Darwin)
1384 version (StackGrowsDown) {}
1385 else static assert(false, "PowerPC Darwin only supports decrementing stacks");
1387 uint wsize = size_t.sizeof;
1389 // linkage + regs + FPRs + VRs
1390 uint space = 8 * wsize + 20 * wsize + 18 * 8 + 12 * 16;
1391 (cast(ubyte*)pstack - space)[0 .. space] = 0;
1393 pstack -= wsize * 6;
1394 *cast(size_t*)pstack = cast(size_t) &fiber_entryPoint; // LR
1395 pstack -= wsize * 22;
1397 // On Darwin PPC64 pthread self is in R13 (which is reserved).
1398 // At present, it is not safe to migrate fibers between threads, but if that
1399 // changes, then updating the value of R13 will also need to be handled.
1400 version (PPC64)
1401 *cast(size_t*)(pstack + wsize) = cast(size_t) Thread.getThis().m_addr;
1402 assert( (cast(size_t) pstack & 0x0f) == 0 );
1404 else version (AsmMIPS_O32_Posix)
1406 version (StackGrowsDown) {}
1407 else static assert(0);
1409 /* We keep the FP registers and the return address below
1410 * the stack pointer, so they don't get scanned by the
1411 * GC. The last frame before swapping the stack pointer is
1412 * organized like the following.
1414 * |-----------|<= frame pointer
1415 * | $gp |
1416 * | $s0-8 |
1417 * |-----------|<= stack pointer
1418 * | $ra |
1419 * | align(8) |
1420 * | $f20-30 |
1421 * |-----------|
1424 enum SZ_GP = 10 * size_t.sizeof; // $gp + $s0-8
1425 enum SZ_RA = size_t.sizeof; // $ra
1426 version (MIPS_HardFloat)
1428 enum SZ_FP = 6 * 8; // $f20-30
1429 enum ALIGN = -(SZ_FP + SZ_RA) & (8 - 1);
1431 else
1433 enum SZ_FP = 0;
1434 enum ALIGN = 0;
1437 enum BELOW = SZ_FP + ALIGN + SZ_RA;
1438 enum ABOVE = SZ_GP;
1439 enum SZ = BELOW + ABOVE;
1441 (cast(ubyte*)pstack - SZ)[0 .. SZ] = 0;
1442 pstack -= ABOVE;
1443 *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint;
1445 else version (AsmLoongArch64_Posix)
1447 version (StackGrowsDown) {}
1448 else static assert(0);
1450 // Like others, FP registers and return address (ra) are kept
1451 // below the saved stack top (tstack) to hide from GC scanning.
1452 // The newp stack should look like this on LoongArch64:
1453 // 18: fp <- pstack
1454 // ...
1455 // 9: s0 <- newp tstack
1456 // 8: ra [&fiber_entryPoint]
1457 // 7: fs7
1458 // ...
1459 // 1: fs1
1460 // 0: fs0
1461 pstack -= 10 * size_t.sizeof; // skip s0-s8 and fp
1462 // set $ra
1463 push( cast(size_t) &fiber_entryPoint );
1464 pstack += size_t.sizeof;
1466 else version (AsmAArch64_Posix)
1468 // Like others, FP registers and return address (lr) are kept
1469 // below the saved stack top (tstack) to hide from GC scanning.
1470 // fiber_switchContext expects newp sp to look like this:
1471 // 19: x19
1472 // ...
1473 // 9: x29 (fp) <-- newp tstack
1474 // 8: x30 (lr) [&fiber_entryPoint]
1475 // 7: d8
1476 // ...
1477 // 0: d15
1479 version (StackGrowsDown) {}
1480 else
1481 static assert(false, "Only full descending stacks supported on AArch64");
1483 // Only need to set return address (lr). Everything else is fine
1484 // zero initialized.
1485 pstack -= size_t.sizeof * 11; // skip past x19-x29
1486 push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs
1487 pstack += size_t.sizeof; // adjust sp (newp) above lr
1489 else version (AsmARM_Posix)
1491 /* We keep the FP registers and the return address below
1492 * the stack pointer, so they don't get scanned by the
1493 * GC. The last frame before swapping the stack pointer is
1494 * organized like the following.
1496 * | |-----------|<= 'frame starts here'
1497 * | | fp | (the actual frame pointer, r11 isn't
1498 * | | r10-r4 | updated and still points to the previous frame)
1499 * | |-----------|<= stack pointer
1500 * | | lr |
1501 * | | 4byte pad |
1502 * | | d15-d8 |(if FP supported)
1503 * | |-----------|
1505 * stack grows down: The pointer value here is smaller than some lines above
1507 // frame pointer can be zero, r10-r4 also zero initialized
1508 version (StackGrowsDown)
1509 pstack -= int.sizeof * 8;
1510 else
1511 static assert(false, "Only full descending stacks supported on ARM");
1513 // link register
1514 push( cast(size_t) &fiber_entryPoint );
1516 * We do not push padding and d15-d8 as those are zero initialized anyway
1517 * Position the stack pointer above the lr register
1519 pstack += int.sizeof * 1;
1521 else version (GNU_AsmX86_Windows)
1523 version (StackGrowsDown) {} else static assert( false );
1525 // Currently, MinGW doesn't utilize SEH exceptions.
1526 // See DMD AsmX86_Windows If this code ever becomes fails and SEH is used.
1528 push( 0x00000000 ); // Return address of fiber_entryPoint call
1529 push( cast(size_t) &fiber_entryPoint ); // EIP
1530 push( 0x00000000 ); // EBP
1531 push( 0x00000000 ); // EDI
1532 push( 0x00000000 ); // ESI
1533 push( 0x00000000 ); // EBX
1534 push( 0xFFFFFFFF ); // FS:[0] - Current SEH frame
1535 push( cast(size_t) m_ctxt.bstack ); // FS:[4] - Top of stack
1536 push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8] - Bottom of stack
1537 push( 0x00000000 ); // EAX
1539 else version (GNU_AsmX86_64_Windows)
1541 push( 0x00000000_00000000 ); // Return address of fiber_entryPoint call
1542 push( cast(size_t) &fiber_entryPoint ); // RIP
1543 push( 0x00000000_00000000 ); // RBP
1544 push( 0x00000000_00000000 ); // RBX
1545 push( 0x00000000_00000000 ); // R12
1546 push( 0x00000000_00000000 ); // R13
1547 push( 0x00000000_00000000 ); // R14
1548 push( 0x00000000_00000000 ); // R15
1549 push( 0xFFFFFFFF_FFFFFFFF ); // GS:[0] - Current SEH frame
1550 version (StackGrowsDown)
1552 push( cast(size_t) m_ctxt.bstack ); // GS:[8] - Top of stack
1553 push( cast(size_t) m_ctxt.bstack - m_size ); // GS:[16] - Bottom of stack
1555 else
1557 push( cast(size_t) m_ctxt.bstack ); // GS:[8] - Top of stack
1558 push( cast(size_t) m_ctxt.bstack + m_size ); // GS:[16] - Bottom of stack
1561 else static if ( __traits( compiles, ucontext_t ) )
1563 getcontext( &m_utxt );
1564 m_utxt.uc_stack.ss_sp = m_pmem;
1565 m_utxt.uc_stack.ss_size = m_size;
1566 makecontext( &m_utxt, &fiber_entryPoint, 0 );
1567 // NOTE: If ucontext is being used then the top of the stack will
1568 // be a pointer to the ucontext_t struct for that fiber.
1569 push( cast(size_t) &m_utxt );
1571 else
1572 static assert(0, "Not implemented");
1576 StackContext* m_ctxt;
1577 size_t m_size;
1578 void* m_pmem;
1580 static if ( __traits( compiles, ucontext_t ) )
1582 // NOTE: The static ucontext instance is used to represent the context
1583 // of the executing thread.
1584 static ucontext_t sm_utxt = void;
1585 ucontext_t m_utxt = void;
1586 ucontext_t* m_ucur = null;
1588 else static if (GNU_Enable_CET)
1590 // When libphobos was built with --enable-cet, these fields need to
1591 // always be present in the Fiber class layout.
1592 import core.sys.posix.ucontext;
1593 static ucontext_t sm_utxt = void;
1594 ucontext_t m_utxt = void;
1595 ucontext_t* m_ucur = null;
1599 private:
1600 ///////////////////////////////////////////////////////////////////////////
1601 // Storage of Active Fiber
1602 ///////////////////////////////////////////////////////////////////////////
1606 // Sets a thread-local reference to the current fiber object.
1608 static void setThis( Fiber f ) nothrow @nogc
1610 sm_this = f;
1613 static Fiber sm_this;
1616 private:
1617 ///////////////////////////////////////////////////////////////////////////
1618 // Context Switching
1619 ///////////////////////////////////////////////////////////////////////////
1623 // Switches into the stack held by this fiber.
1625 final void switchIn() nothrow @nogc
1627 Thread tobj = Thread.getThis();
1628 void** oldp = &tobj.m_curr.tstack;
1629 void* newp = m_ctxt.tstack;
1631 // NOTE: The order of operations here is very important. The current
1632 // stack top must be stored before m_lock is set, and pushContext
1633 // must not be called until after m_lock is set. This process
1634 // is intended to prevent a race condition with the suspend
1635 // mechanism used for garbage collection. If it is not followed,
1636 // a badly timed collection could cause the GC to scan from the
1637 // bottom of one stack to the top of another, or to miss scanning
1638 // a stack that still contains valid data. The old stack pointer
1639 // oldp will be set again before the context switch to guarantee
1640 // that it points to exactly the correct stack location so the
1641 // successive pop operations will succeed.
1642 *oldp = getStackTop();
1643 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
1644 tobj.pushContext( m_ctxt );
1646 fiber_switchContext( oldp, newp );
1648 // NOTE: As above, these operations must be performed in a strict order
1649 // to prevent Bad Things from happening.
1650 tobj.popContext();
1651 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
1652 tobj.m_curr.tstack = tobj.m_curr.bstack;
1657 // Switches out of the current stack and into the enclosing stack.
1659 final void switchOut() nothrow @nogc
1661 Thread tobj = Thread.getThis();
1662 void** oldp = &m_ctxt.tstack;
1663 void* newp = tobj.m_curr.within.tstack;
1665 // NOTE: The order of operations here is very important. The current
1666 // stack top must be stored before m_lock is set, and pushContext
1667 // must not be called until after m_lock is set. This process
1668 // is intended to prevent a race condition with the suspend
1669 // mechanism used for garbage collection. If it is not followed,
1670 // a badly timed collection could cause the GC to scan from the
1671 // bottom of one stack to the top of another, or to miss scanning
1672 // a stack that still contains valid data. The old stack pointer
1673 // oldp will be set again before the context switch to guarantee
1674 // that it points to exactly the correct stack location so the
1675 // successive pop operations will succeed.
1676 *oldp = getStackTop();
1677 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
1679 fiber_switchContext( oldp, newp );
1681 // NOTE: As above, these operations must be performed in a strict order
1682 // to prevent Bad Things from happening.
1683 // NOTE: If use of this fiber is multiplexed across threads, the thread
1684 // executing here may be different from the one above, so get the
1685 // current thread handle before unlocking, etc.
1686 tobj = Thread.getThis();
1687 atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
1688 tobj.m_curr.tstack = tobj.m_curr.bstack;
1693 unittest {
1694 int counter;
1696 class DerivedFiber : Fiber
1698 this()
1700 super( &run );
1703 private :
1704 void run()
1706 counter += 2;
1710 void fiberFunc()
1712 counter += 4;
1713 Fiber.yield();
1714 counter += 8;
1717 // create instances of each type
1718 Fiber derived = new DerivedFiber();
1719 Fiber composed = new Fiber( &fiberFunc );
1721 assert( counter == 0 );
1723 derived.call();
1724 assert( counter == 2, "Derived fiber increment." );
1726 composed.call();
1727 assert( counter == 6, "First composed fiber increment." );
1729 counter += 16;
1730 assert( counter == 22, "Calling context increment." );
1732 composed.call();
1733 assert( counter == 30, "Second composed fiber increment." );
1735 // since each fiber has run to completion, each should have state TERM
1736 assert( derived.state == Fiber.State.TERM );
1737 assert( composed.state == Fiber.State.TERM );
1740 version (CoreUnittest)
1742 class TestFiber : Fiber
1744 this()
1746 super(&run);
1749 void run()
1751 foreach (i; 0 .. 1000)
1753 sum += i;
1754 Fiber.yield();
1758 enum expSum = 1000 * 999 / 2;
1759 size_t sum;
1762 void runTen()
1764 TestFiber[10] fibs;
1765 foreach (ref fib; fibs)
1766 fib = new TestFiber();
1768 bool cont;
1769 do {
1770 cont = false;
1771 foreach (fib; fibs) {
1772 if (fib.state == Fiber.State.HOLD)
1774 fib.call();
1775 cont |= fib.state != Fiber.State.TERM;
1778 } while (cont);
1780 foreach (fib; fibs)
1782 assert(fib.sum == TestFiber.expSum);
1788 // Single thread running separate fibers
1789 unittest
1791 runTen();
1795 // Multiple threads running separate fibers
1796 unittest
1798 auto group = new ThreadGroup();
1799 foreach (_; 0 .. 4)
1801 group.create(&runTen);
1803 group.joinAll();
1807 // Multiple threads running shared fibers
1808 version (PPC) version = UnsafeFiberMigration;
1809 version (PPC64) version = UnsafeFiberMigration;
1810 version (OSX)
1812 version (X86) version = UnsafeFiberMigration;
1813 version (X86_64) version = UnsafeFiberMigration;
1814 version (AArch64) version = UnsafeFiberMigration;
1817 version (UnsafeFiberMigration)
1819 // XBUG: core.thread fibers are supposed to be safe to migrate across
1820 // threads, however, there is a problem: GCC always assumes that the
1821 // address of thread-local variables don't change while on a given stack.
1822 // In consequence, migrating fibers between threads currently is an unsafe
1823 // thing to do, and will break on some targets (possibly PR26461).
1825 else
1827 version = FiberMigrationUnittest;
1830 version (FiberMigrationUnittest)
1831 unittest
1833 shared bool[10] locks;
1834 TestFiber[10] fibs;
1836 void runShared()
1838 bool cont;
1839 do {
1840 cont = false;
1841 foreach (idx; 0 .. 10)
1843 if (cas(&locks[idx], false, true))
1845 if (fibs[idx].state == Fiber.State.HOLD)
1847 fibs[idx].call();
1848 cont |= fibs[idx].state != Fiber.State.TERM;
1850 locks[idx] = false;
1852 else
1854 cont = true;
1857 } while (cont);
1860 foreach (ref fib; fibs)
1862 fib = new TestFiber();
1865 auto group = new ThreadGroup();
1866 foreach (_; 0 .. 4)
1868 group.create(&runShared);
1870 group.joinAll();
1872 foreach (fib; fibs)
1874 assert(fib.sum == TestFiber.expSum);
1879 // Test exception handling inside fibers.
1880 unittest
1882 enum MSG = "Test message.";
1883 string caughtMsg;
1884 (new Fiber({
1887 throw new Exception(MSG);
1889 catch (Exception e)
1891 caughtMsg = e.msg;
1893 })).call();
1894 assert(caughtMsg == MSG);
1898 unittest
1900 int x = 0;
1902 (new Fiber({
1903 x++;
1904 })).call();
1905 assert( x == 1 );
1908 nothrow unittest
1910 new Fiber({}).call!(Fiber.Rethrow.no)();
1913 unittest
1915 new Fiber({}).call(Fiber.Rethrow.yes);
1916 new Fiber({}).call(Fiber.Rethrow.no);
1919 unittest
1921 enum MSG = "Test message.";
1925 (new Fiber(function() {
1926 throw new Exception( MSG );
1927 })).call();
1928 assert( false, "Expected rethrown exception." );
1930 catch ( Throwable t )
1932 assert( t.msg == MSG );
1936 // Test exception chaining when switching contexts in finally blocks.
1937 unittest
1939 static void throwAndYield(string msg) {
1940 try {
1941 throw new Exception(msg);
1942 } finally {
1943 Fiber.yield();
1947 static void fiber(string name) {
1948 try {
1949 try {
1950 throwAndYield(name ~ ".1");
1951 } finally {
1952 throwAndYield(name ~ ".2");
1954 } catch (Exception e) {
1955 assert(e.msg == name ~ ".1");
1956 assert(e.next);
1957 assert(e.next.msg == name ~ ".2");
1958 assert(!e.next.next);
1962 auto first = new Fiber(() => fiber("first"));
1963 auto second = new Fiber(() => fiber("second"));
1964 first.call();
1965 second.call();
1966 first.call();
1967 second.call();
1968 first.call();
1969 second.call();
1970 assert(first.state == Fiber.State.TERM);
1971 assert(second.state == Fiber.State.TERM);
1974 // Test Fiber resetting
1975 unittest
1977 static string method;
1979 static void foo()
1981 method = "foo";
1984 void bar()
1986 method = "bar";
1989 static void expect(Fiber fib, string s)
1991 assert(fib.state == Fiber.State.HOLD);
1992 fib.call();
1993 assert(fib.state == Fiber.State.TERM);
1994 assert(method == s); method = null;
1996 auto fib = new Fiber(&foo);
1997 expect(fib, "foo");
1999 fib.reset();
2000 expect(fib, "foo");
2002 fib.reset(&foo);
2003 expect(fib, "foo");
2005 fib.reset(&bar);
2006 expect(fib, "bar");
2008 fib.reset(function void(){method = "function";});
2009 expect(fib, "function");
2011 fib.reset(delegate void(){method = "delegate";});
2012 expect(fib, "delegate");
2015 // Test unsafe reset in hold state
2016 unittest
2018 auto fib = new Fiber(function {ubyte[2048] buf = void; Fiber.yield();}, 4096);
2019 foreach (_; 0 .. 10)
2021 fib.call();
2022 assert(fib.state == Fiber.State.HOLD);
2023 fib.reset();
2027 // stress testing GC stack scanning
2028 unittest
2030 import core.memory;
2031 import core.time : dur;
2033 static void unreferencedThreadObject()
2035 static void sleep() { Thread.sleep(dur!"msecs"(100)); }
2036 auto thread = new Thread(&sleep).start();
2038 unreferencedThreadObject();
2039 GC.collect();
2041 static class Foo
2043 this(int value)
2045 _value = value;
2048 int bar()
2050 return _value;
2053 int _value;
2056 static void collect()
2058 auto foo = new Foo(2);
2059 assert(foo.bar() == 2);
2060 GC.collect();
2061 Fiber.yield();
2062 GC.collect();
2063 assert(foo.bar() == 2);
2066 auto fiber = new Fiber(&collect);
2068 fiber.call();
2069 GC.collect();
2070 fiber.call();
2072 // thread reference
2073 auto foo = new Foo(2);
2075 void collect2()
2077 assert(foo.bar() == 2);
2078 GC.collect();
2079 Fiber.yield();
2080 GC.collect();
2081 assert(foo.bar() == 2);
2084 fiber = new Fiber(&collect2);
2086 fiber.call();
2087 GC.collect();
2088 fiber.call();
2090 static void recurse(size_t cnt)
2092 --cnt;
2093 Fiber.yield();
2094 if (cnt)
2096 auto fib = new Fiber(() { recurse(cnt); });
2097 fib.call();
2098 GC.collect();
2099 fib.call();
2102 fiber = new Fiber(() { recurse(20); });
2103 fiber.call();
2107 version (AsmX86_64_Windows)
2109 // Test Windows x64 calling convention
2110 unittest
2112 void testNonvolatileRegister(alias REG)()
2114 auto zeroRegister = new Fiber(() {
2115 mixin("asm pure nothrow @nogc { naked; xor "~REG~", "~REG~"; ret; }");
2117 long after;
2119 mixin("asm pure nothrow @nogc { mov "~REG~", 0xFFFFFFFFFFFFFFFF; }");
2120 zeroRegister.call();
2121 mixin("asm pure nothrow @nogc { mov after, "~REG~"; }");
2123 assert(after == -1);
2126 void testNonvolatileRegisterSSE(alias REG)()
2128 auto zeroRegister = new Fiber(() {
2129 mixin("asm pure nothrow @nogc { naked; xorpd "~REG~", "~REG~"; ret; }");
2131 long[2] before = [0xFFFFFFFF_FFFFFFFF, 0xFFFFFFFF_FFFFFFFF], after;
2133 mixin("asm pure nothrow @nogc { movdqu "~REG~", before; }");
2134 zeroRegister.call();
2135 mixin("asm pure nothrow @nogc { movdqu after, "~REG~"; }");
2137 assert(before == after);
2140 testNonvolatileRegister!("R12")();
2141 testNonvolatileRegister!("R13")();
2142 testNonvolatileRegister!("R14")();
2143 testNonvolatileRegister!("R15")();
2144 testNonvolatileRegister!("RDI")();
2145 testNonvolatileRegister!("RSI")();
2146 testNonvolatileRegister!("RBX")();
2148 testNonvolatileRegisterSSE!("XMM6")();
2149 testNonvolatileRegisterSSE!("XMM7")();
2150 testNonvolatileRegisterSSE!("XMM8")();
2151 testNonvolatileRegisterSSE!("XMM9")();
2152 testNonvolatileRegisterSSE!("XMM10")();
2153 testNonvolatileRegisterSSE!("XMM11")();
2154 testNonvolatileRegisterSSE!("XMM12")();
2155 testNonvolatileRegisterSSE!("XMM13")();
2156 testNonvolatileRegisterSSE!("XMM14")();
2157 testNonvolatileRegisterSSE!("XMM15")();
2162 version (D_InlineAsm_X86_64)
2164 unittest
2166 void testStackAlignment()
2168 void* pRSP;
2169 asm pure nothrow @nogc
2171 mov pRSP, RSP;
2173 assert((cast(size_t)pRSP & 0xF) == 0);
2176 auto fib = new Fiber(&testStackAlignment);
2177 fib.call();