The OpenD Programming Language

1 /**
2  * The fiber module provides OS-indepedent lightweight threads aka fibers.
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/fiber.d)
10  */
11 
12 module core.thread.fiber;
13 
14 import core.thread.osthread;
15 import core.thread.threadgroup;
16 import core.thread.types;
17 import core.thread.context;
18 
19 import core.memory : pageSize;
20 
21 version(WebAssembly) {} else:
22 
23 ///////////////////////////////////////////////////////////////////////////////
24 // Fiber Platform Detection
25 ///////////////////////////////////////////////////////////////////////////////
26 
27 version (GNU)
28 {
29     import gcc.builtins;
30     version (GNU_StackGrowsDown)
31         version = StackGrowsDown;
32 }
33 else
34 {
35     // this should be true for most architectures
36     version = StackGrowsDown;
37 }
38 
39 version (Windows)
40 {
41     import core.stdc.stdlib : malloc, free;
42     import core.sys.windows.winbase;
43     import core.sys.windows.winnt;
44 }
45 
46 private
47 {
48     version (D_InlineAsm_X86)
49     {
50         version (Windows)
51             version = AsmX86_Windows;
52         else version (Posix)
53             version = AsmX86_Posix;
54 
55         version = AlignFiberStackTo16Byte;
56     }
57     else version (D_InlineAsm_X86_64)
58     {
59         version (Windows)
60         {
61             version = AsmX86_64_Windows;
62             version = AlignFiberStackTo16Byte;
63         }
64         else version (Posix)
65         {
66             version = AsmX86_64_Posix;
67             version = AlignFiberStackTo16Byte;
68         }
69     }
70     else version (PPC)
71     {
72         version (OSX)
73         {
74             version = AsmPPC_Darwin;
75             version = AsmExternal;
76             version = AlignFiberStackTo16Byte;
77         }
78         else version (Posix)
79         {
80             version = AsmPPC_Posix;
81             version = AsmExternal;
82         }
83     }
84     else version (PPC64)
85     {
86         version (OSX)
87         {
88             version = AsmPPC_Darwin;
89             version = AsmExternal;
90             version = AlignFiberStackTo16Byte;
91         }
92         else version (Posix)
93         {
94             version = AlignFiberStackTo16Byte;
95         }
96     }
97     else version (MIPS_O32)
98     {
99         version (Posix)
100         {
101             version = AsmMIPS_O32_Posix;
102             version = AsmExternal;
103         }
104     }
105     else version (AArch64)
106     {
107         version (Posix)
108         {
109             version = AsmAArch64_Posix;
110             version = AsmExternal;
111             version = AlignFiberStackTo16Byte;
112         }
113     }
114     else version (ARM)
115     {
116         version (Posix)
117         {
118             version = AsmARM_Posix;
119             version = AsmExternal;
120         }
121     }
122     else version (SPARC)
123     {
124         // NOTE: The SPARC ABI specifies only doubleword alignment.
125         version = AlignFiberStackTo16Byte;
126     }
127     else version (SPARC64)
128     {
129         version = AlignFiberStackTo16Byte;
130     }
131     else version (LoongArch64)
132     {
133         version (Posix)
134         {
135             version = AsmLoongArch64_Posix;
136             version = AsmExternal;
137             version = AlignFiberStackTo16Byte;
138         }
139     }
140 
141     version (Posix)
142     {
143         version (AsmX86_Windows)    {} else
144         version (AsmX86_Posix)      {} else
145         version (AsmX86_64_Windows) {} else
146         version (AsmX86_64_Posix)   {} else
147         version (AsmExternal)       {} else
148         {
149             // NOTE: The ucontext implementation requires architecture specific
150             //       data definitions to operate so testing for it must be done
151             //       by checking for the existence of ucontext_t rather than by
152             //       a version identifier.  Please note that this is considered
153             //       an obsolescent feature according to the POSIX spec, so a
154             //       custom solution is still preferred.
155             import core.sys.posix.ucontext;
156         }
157     }
158 }
159 
160 ///////////////////////////////////////////////////////////////////////////////
161 // Fiber Entry Point and Context Switch
162 ///////////////////////////////////////////////////////////////////////////////
163 
164 private
165 {
166     import core.atomic : atomicStore, cas, MemoryOrder;
167     import core.exception : onOutOfMemoryError;
168     import core.stdc.stdlib : abort;
169 
170     extern (C) void fiber_entryPoint() nothrow
171     {
172         Fiber   obj = Fiber.getThis();
173         assert( obj );
174 
175         assert( Thread.getThis().m_curr is obj.m_ctxt );
176         atomicStore!(MemoryOrder.raw)(*cast(shared)&Thread.getThis().m_lock, false);
177         obj.m_ctxt.tstack = obj.m_ctxt.bstack;
178         obj.m_state = Fiber.State.EXEC;
179 
180         try
181         {
182             obj.run();
183         }
184         catch ( Throwable t )
185         {
186             obj.m_unhandled = t;
187         }
188 
189         static if ( __traits( compiles, ucontext_t ) )
190           obj.m_ucur = &obj.m_utxt;
191 
192         obj.m_state = Fiber.State.TERM;
193         obj.switchOut();
194     }
195 
196   // Look above the definition of 'class Fiber' for some information about the implementation of this routine
197   version (AsmExternal)
198   {
199       extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;
200       version (AArch64)
201           extern (C) void fiber_trampoline() nothrow;
202   }
203   else
204     extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc
205     {
206         // NOTE: The data pushed and popped in this routine must match the
207         //       default stack created by Fiber.initStack or the initial
208         //       switch into a new context will fail.
209 
210         version (AsmX86_Windows)
211         {
212             asm pure nothrow @nogc
213             {
214                 naked;
215 
216                 // save current stack state
217                 push EBP;
218                 mov  EBP, ESP;
219                 push EDI;
220                 push ESI;
221                 push EBX;
222                 push dword ptr FS:[0];
223                 push dword ptr FS:[4];
224                 push dword ptr FS:[8];
225                 push EAX;
226 
227                 // store oldp again with more accurate address
228                 mov EAX, dword ptr 8[EBP];
229                 mov [EAX], ESP;
230                 // load newp to begin context switch
231                 mov ESP, dword ptr 12[EBP];
232 
233                 // load saved state from new stack
234                 pop EAX;
235                 pop dword ptr FS:[8];
236                 pop dword ptr FS:[4];
237                 pop dword ptr FS:[0];
238                 pop EBX;
239                 pop ESI;
240                 pop EDI;
241                 pop EBP;
242 
243                 // 'return' to complete switch
244                 pop ECX;
245                 jmp ECX;
246             }
247         }
248         else version (AsmX86_64_Windows)
249         {
250             asm pure nothrow @nogc
251             {
252                 naked;
253 
254                 // save current stack state
255                 // NOTE: When changing the layout of registers on the stack,
256                 //       make sure that the XMM registers are still aligned.
257                 //       On function entry, the stack is guaranteed to not
258                 //       be aligned to 16 bytes because of the return address
259                 //       on the stack.
260                 push RBP;
261                 mov  RBP, RSP;
262                 push R12;
263                 push R13;
264                 push R14;
265                 push R15;
266                 push RDI;
267                 push RSI;
268                 // 7 registers = 56 bytes; stack is now aligned to 16 bytes
269                 sub RSP, 160;
270                 movdqa [RSP + 144], XMM6;
271                 movdqa [RSP + 128], XMM7;
272                 movdqa [RSP + 112], XMM8;
273                 movdqa [RSP + 96], XMM9;
274                 movdqa [RSP + 80], XMM10;
275                 movdqa [RSP + 64], XMM11;
276                 movdqa [RSP + 48], XMM12;
277                 movdqa [RSP + 32], XMM13;
278                 movdqa [RSP + 16], XMM14;
279                 movdqa [RSP], XMM15;
280                 push RBX;
281                 xor  RAX,RAX;
282                 push qword ptr GS:[RAX];
283                 push qword ptr GS:8[RAX];
284                 push qword ptr GS:16[RAX];
285 
286                 // store oldp
287                 mov [RCX], RSP;
288                 // load newp to begin context switch
289                 mov RSP, RDX;
290 
291                 // load saved state from new stack
292                 pop qword ptr GS:16[RAX];
293                 pop qword ptr GS:8[RAX];
294                 pop qword ptr GS:[RAX];
295                 pop RBX;
296                 movdqa XMM15, [RSP];
297                 movdqa XMM14, [RSP + 16];
298                 movdqa XMM13, [RSP + 32];
299                 movdqa XMM12, [RSP + 48];
300                 movdqa XMM11, [RSP + 64];
301                 movdqa XMM10, [RSP + 80];
302                 movdqa XMM9, [RSP + 96];
303                 movdqa XMM8, [RSP + 112];
304                 movdqa XMM7, [RSP + 128];
305                 movdqa XMM6, [RSP + 144];
306                 add RSP, 160;
307                 pop RSI;
308                 pop RDI;
309                 pop R15;
310                 pop R14;
311                 pop R13;
312                 pop R12;
313                 pop RBP;
314 
315                 // 'return' to complete switch
316                 pop RCX;
317                 jmp RCX;
318             }
319         }
320         else version (AsmX86_Posix)
321         {
322             asm pure nothrow @nogc
323             {
324                 naked;
325 
326                 // save current stack state
327                 push EBP;
328                 mov  EBP, ESP;
329                 push EDI;
330                 push ESI;
331                 push EBX;
332                 push EAX;
333 
334                 // store oldp again with more accurate address
335                 mov EAX, dword ptr 8[EBP];
336                 mov [EAX], ESP;
337                 // load newp to begin context switch
338                 mov ESP, dword ptr 12[EBP];
339 
340                 // load saved state from new stack
341                 pop EAX;
342                 pop EBX;
343                 pop ESI;
344                 pop EDI;
345                 pop EBP;
346 
347                 // 'return' to complete switch
348                 pop ECX;
349                 jmp ECX;
350             }
351         }
352         else version (AsmX86_64_Posix)
353         {
354             asm pure nothrow @nogc
355             {
356                 naked;
357 
358                 // save current stack state
359                 push RBP;
360                 mov  RBP, RSP;
361                 push RBX;
362                 push R12;
363                 push R13;
364                 push R14;
365                 push R15;
366 
367                 // store oldp
368                 mov [RDI], RSP;
369                 // load newp to begin context switch
370                 mov RSP, RSI;
371 
372                 // load saved state from new stack
373                 pop R15;
374                 pop R14;
375                 pop R13;
376                 pop R12;
377                 pop RBX;
378                 pop RBP;
379 
380                 // 'return' to complete switch
381                 pop RCX;
382                 jmp RCX;
383             }
384         }
385         else static if ( __traits( compiles, ucontext_t ) )
386         {
387             Fiber   cfib = Fiber.getThis();
388             void*   ucur = cfib.m_ucur;
389 
390             *oldp = &ucur;
391             swapcontext( **(cast(ucontext_t***) oldp),
392                           *(cast(ucontext_t**)  newp) );
393         }
394 	else version (FreeStanding)
395 		assert(0); // FIXME this should be possible tbh
396         else
397             static assert(0, "Not implemented");
398     }
399 }
400 
401 
402 ///////////////////////////////////////////////////////////////////////////////
403 // Fiber
404 ///////////////////////////////////////////////////////////////////////////////
405 /*
406  * Documentation of Fiber internals:
407  *
408  * The main routines to implement when porting Fibers to new architectures are
409  * fiber_switchContext and initStack. Some version constants have to be defined
410  * for the new platform as well, search for "Fiber Platform Detection and Memory Allocation".
411  *
412  * Fibers are based on a concept called 'Context'. A Context describes the execution
413  * state of a Fiber or main thread which is fully described by the stack, some
414  * registers and a return address at which the Fiber/Thread should continue executing.
415  * Please note that not only each Fiber has a Context, but each thread also has got a
416  * Context which describes the threads stack and state. If you call Fiber fib; fib.call
417  * the first time in a thread you switch from Threads Context into the Fibers Context.
418  * If you call fib.yield in that Fiber you switch out of the Fibers context and back
419  * into the Thread Context. (However, this is not always the case. You can call a Fiber
420  * from within another Fiber, then you switch Contexts between the Fibers and the Thread
421  * Context is not involved)
422  *
423  * In all current implementations the registers and the return address are actually
424  * saved on a Contexts stack.
425  *
426  * The fiber_switchContext routine has got two parameters:
427  * void** a:  This is the _location_ where we have to store the current stack pointer,
428  *            the stack pointer of the currently executing Context (Fiber or Thread).
429  * void*  b:  This is the pointer to the stack of the Context which we want to switch into.
430  *            Note that we get the same pointer here as the one we stored into the void** a
431  *            in a previous call to fiber_switchContext.
432  *
433  * In the simplest case, a fiber_switchContext rountine looks like this:
434  * fiber_switchContext:
435  *     push {return Address}
436  *     push {registers}
437  *     copy {stack pointer} into {location pointed to by a}
438  *     //We have now switch to the stack of a different Context!
439  *     copy {b} into {stack pointer}
440  *     pop {registers}
441  *     pop {return Address}
442  *     jump to {return Address}
443  *
444  * The GC uses the value returned in parameter a to scan the Fibers stack. It scans from
445  * the stack base to that value. As the GC dislikes false pointers we can actually optimize
446  * this a little: By storing registers which can not contain references to memory managed
447  * by the GC outside of the region marked by the stack base pointer and the stack pointer
448  * saved in fiber_switchContext we can prevent the GC from scanning them.
449  * Such registers are usually floating point registers and the return address. In order to
450  * implement this, we return a modified stack pointer from fiber_switchContext. However,
451  * we have to remember that when we restore the registers from the stack!
452  *
453  * --------------------------- <= Stack Base
454  * |          Frame          | <= Many other stack frames
455  * |          Frame          |
456  * |-------------------------| <= The last stack frame. This one is created by fiber_switchContext
457  * | registers with pointers |
458  * |                         | <= Stack pointer. GC stops scanning here
459  * |   return address        |
460  * |floating point registers |
461  * --------------------------- <= Real Stack End
462  *
463  * fiber_switchContext:
464  *     push {registers with pointers}
465  *     copy {stack pointer} into {location pointed to by a}
466  *     push {return Address}
467  *     push {Floating point registers}
468  *     //We have now switch to the stack of a different Context!
469  *     copy {b} into {stack pointer}
470  *     //We now have to adjust the stack pointer to point to 'Real Stack End' so we can pop
471  *     //the FP registers
472  *     //+ or - depends on if your stack grows downwards or upwards
473  *     {stack pointer} = {stack pointer} +- ({FPRegisters}.sizeof + {return address}.sizeof}
474  *     pop {Floating point registers}
475  *     pop {return Address}
476  *     pop {registers with pointers}
477  *     jump to {return Address}
478  *
479  * So the question now is which registers need to be saved? This depends on the specific
480  * architecture ABI of course, but here are some general guidelines:
481  * - If a register is callee-save (if the callee modifies the register it must saved and
482  *   restored by the callee) it needs to be saved/restored in switchContext
483  * - If a register is caller-save it needn't be saved/restored. (Calling fiber_switchContext
484  *   is a function call and the compiler therefore already must save these registers before
485  *   calling fiber_switchContext)
486  * - Argument registers used for passing parameters to functions needn't be saved/restored
487  * - The return register needn't be saved/restored (fiber_switchContext hasn't got a return type)
488  * - All scratch registers needn't be saved/restored
489  * - The link register usually needn't be saved/restored (but sometimes it must be cleared -
490  *   see below for details)
491  * - The frame pointer register - if it exists - is usually callee-save
492  * - All current implementations do not save control registers
493  *
494  * What happens on the first switch into a Fiber? We never saved a state for this fiber before,
495  * but the initial state is prepared in the initStack routine. (This routine will also be called
496  * when a Fiber is being resetted). initStack must produce exactly the same stack layout as the
497  * part of fiber_switchContext which saves the registers. Pay special attention to set the stack
498  * pointer correctly if you use the GC optimization mentioned before. the return Address saved in
499  * initStack must be the address of fiber_entrypoint.
500  *
501  * There's now a small but important difference between the first context switch into a fiber and
502  * further context switches. On the first switch, Fiber.call is used and the returnAddress in
503  * fiber_switchContext will point to fiber_entrypoint. The important thing here is that this jump
504  * is a _function call_, we call fiber_entrypoint by jumping before it's function prologue. On later
505  * calls, the user used yield() in a function, and therefore the return address points into a user
506  * function, after the yield call. So here the jump in fiber_switchContext is a _function return_,
507  * not a function call!
508  *
509  * The most important result of this is that on entering a function, i.e. fiber_entrypoint, we
510  * would have to provide a return address / set the link register once fiber_entrypoint
511  * returns. Now fiber_entrypoint does never return and therefore the actual value of the return
512  * address / link register is never read/used and therefore doesn't matter. When fiber_switchContext
513  * performs a _function return_ the value in the link register doesn't matter either.
514  * However, the link register will still be saved to the stack in fiber_entrypoint and some
515  * exception handling / stack unwinding code might read it from this stack location and crash.
516  * The exact solution depends on your architecture, but see the ARM implementation for a way
517  * to deal with this issue.
518  *
519  * The ARM implementation is meant to be used as a kind of documented example implementation.
520  * Look there for a concrete example.
521  *
522  * FIXME: fiber_entrypoint might benefit from a @noreturn attribute, but D doesn't have one.
523  */
524 
525 /**
526  * This class provides a cooperative concurrency mechanism integrated with the
527  * threading and garbage collection functionality.  Calling a fiber may be
528  * considered a blocking operation that returns when the fiber yields (via
529  * Fiber.yield()).  Execution occurs within the context of the calling thread
530  * so synchronization is not necessary to guarantee memory visibility so long
531  * as the same thread calls the fiber each time.  Please note that there is no
532  * requirement that a fiber be bound to one specific thread.  Rather, fibers
533  * may be freely passed between threads so long as they are not currently
534  * executing.  Like threads, a new fiber thread may be created using either
535  * derivation or composition, as in the following example.
536  *
537  * Warning:
538  * Status registers are not saved by the current implementations. This means
539  * floating point exception status bits (overflow, divide by 0), rounding mode
540  * and similar stuff is set per-thread, not per Fiber!
541  *
542  * Warning:
543  * On ARM FPU registers are not saved if druntime was compiled as ARM_SoftFloat.
544  * If such a build is used on a ARM_SoftFP system which actually has got a FPU
545  * and other libraries are using the FPU registers (other code is compiled
546  * as ARM_SoftFP) this can cause problems. Druntime must be compiled as
547  * ARM_SoftFP in this case.
548  *
549  * Authors: Based on a design by Mikola Lysenko.
550  */
551 class Fiber
552 {
553     ///////////////////////////////////////////////////////////////////////////
554     // Initialization
555     ///////////////////////////////////////////////////////////////////////////
556 
557     version (Windows)
558         // exception handling walks the stack, invoking DbgHelp.dll which
559         // needs up to 16k of stack space depending on the version of DbgHelp.dll,
560         // the existence of debug symbols and other conditions. Avoid causing
561         // stack overflows by defaulting to a larger stack size
562         enum defaultStackPages = 8;
563     else version (OSX)
564     {
565         version (X86_64)
566             // libunwind on macOS 11 now requires more stack space than 16k, so
567             // default to a larger stack size. This is only applied to X86 as
568             // the pageSize is still 4k, however on AArch64 it is 16k.
569             enum defaultStackPages = 8;
570         else
571             enum defaultStackPages = 4;
572     }
573     else
574         enum defaultStackPages = 4;
575 
576     /**
577      * Initializes a fiber object which is associated with a static
578      * D function.
579      *
580      * Params:
581      *  fn = The fiber function.
582      *  sz = The stack size for this fiber.
583      *  guardPageSize = size of the guard page to trap fiber's stack
584      *                  overflows. Beware that using this will increase
585      *                  the number of mmaped regions on platforms using mmap
586      *                  so an OS-imposed limit may be hit.
587      *
588      * In:
589      *  fn must not be null.
590      */
591     this( void function() fn, size_t sz = pageSize * defaultStackPages,
592           size_t guardPageSize = pageSize ) nothrow
593     in
594     {
595         assert( fn );
596     }
597     do
598     {
599         allocStack( sz, guardPageSize );
600         reset( fn );
601     }
602 
603 
604     /**
605      * Initializes a fiber object which is associated with a dynamic
606      * D function.
607      *
608      * Params:
609      *  dg = The fiber function.
610      *  sz = The stack size for this fiber.
611      *  guardPageSize = size of the guard page to trap fiber's stack
612      *                  overflows. Beware that using this will increase
613      *                  the number of mmaped regions on platforms using mmap
614      *                  so an OS-imposed limit may be hit.
615      *
616      * In:
617      *  dg must not be null.
618      */
619     this( void delegate() dg, size_t sz = pageSize * defaultStackPages,
620           size_t guardPageSize = pageSize ) nothrow
621     {
622         allocStack( sz, guardPageSize );
623         reset( cast(void delegate() const) dg );
624     }
625 
626 
627     /**
628      * Cleans up any remaining resources used by this object.
629      */
630     ~this() nothrow @nogc @system
631     {
632         // NOTE: A live reference to this object will exist on its associated
633         //       stack from the first time its call() method has been called
634         //       until its execution completes with State.TERM.  Thus, the only
635         //       times this dtor should be called are either if the fiber has
636         //       terminated (and therefore has no active stack) or if the user
637         //       explicitly deletes this object.  The latter case is an error
638         //       but is not easily tested for, since State.HOLD may imply that
639         //       the fiber was just created but has never been run.  There is
640         //       not a compelling case to create a State.INIT just to offer a
641         //       means of ensuring the user isn't violating this object's
642         //       contract, so for now this requirement will be enforced by
643         //       documentation only.
644         freeStack();
645     }
646 
647 
648     ///////////////////////////////////////////////////////////////////////////
649     // General Actions
650     ///////////////////////////////////////////////////////////////////////////
651 
652 
653     /**
654      * Transfers execution to this fiber object.  The calling context will be
655      * suspended until the fiber calls Fiber.yield() or until it terminates
656      * via an unhandled exception.
657      *
658      * Params:
659      *  rethrow = Rethrow any unhandled exception which may have caused this
660      *            fiber to terminate.
661      *
662      * In:
663      *  This fiber must be in state HOLD.
664      *
665      * Throws:
666      *  Any exception not handled by the joined thread.
667      *
668      * Returns:
669      *  Any exception not handled by this fiber if rethrow = false, null
670      *  otherwise.
671      */
672     // Not marked with any attributes, even though `nothrow @nogc` works
673     // because it calls arbitrary user code. Most of the implementation
674     // is already `@nogc nothrow`, but in order for `Fiber.call` to
675     // propagate the attributes of the user's function, the Fiber
676     // class needs to be templated.
677     final Throwable call( Rethrow rethrow = Rethrow.yes )
678     {
679         return rethrow ? call!(Rethrow.yes)() : call!(Rethrow.no);
680     }
681 
682     /// ditto
683     final Throwable call( Rethrow rethrow )()
684     {
685         callImpl();
686         if ( m_unhandled )
687         {
688             Throwable t = m_unhandled;
689             m_unhandled = null;
690             static if ( rethrow )
691                 throw t;
692             else
693                 return t;
694         }
695         return null;
696     }
697 
698     private void callImpl() nothrow @nogc
699     in
700     {
701         assert( m_state == State.HOLD );
702     }
703     do
704     {
705         Fiber   cur = getThis();
706 
707         static if ( __traits( compiles, ucontext_t ) )
708             m_ucur = cur ? &cur.m_utxt : &Fiber.sm_utxt;
709 
710         setThis( this );
711         this.switchIn();
712         setThis( cur );
713 
714         static if ( __traits( compiles, ucontext_t ) )
715             m_ucur = null;
716 
717         // NOTE: If the fiber has terminated then the stack pointers must be
718         //       reset.  This ensures that the stack for this fiber is not
719         //       scanned if the fiber has terminated.  This is necessary to
720         //       prevent any references lingering on the stack from delaying
721         //       the collection of otherwise dead objects.  The most notable
722         //       being the current object, which is referenced at the top of
723         //       fiber_entryPoint.
724         if ( m_state == State.TERM )
725         {
726             m_ctxt.tstack = m_ctxt.bstack;
727         }
728     }
729 
730     /// Flag to control rethrow behavior of $(D $(LREF call))
731     enum Rethrow : bool { no, yes }
732 
733     /**
734      * Resets this fiber so that it may be re-used, optionally with a
735      * new function/delegate.  This routine should only be called for
736      * fibers that have terminated, as doing otherwise could result in
737      * scope-dependent functionality that is not executed.
738      * Stack-based classes, for example, may not be cleaned up
739      * properly if a fiber is reset before it has terminated.
740      *
741      * In:
742      *  This fiber must be in state TERM or HOLD.
743      */
744     final void reset() nothrow @nogc
745     in
746     {
747         assert( m_state == State.TERM || m_state == State.HOLD );
748     }
749     do
750     {
751         m_ctxt.tstack = m_ctxt.bstack;
752         m_state = State.HOLD;
753         initStack();
754         m_unhandled = null;
755     }
756 
757     /// ditto
758     final void reset( void function() fn ) nothrow @nogc
759     {
760         reset();
761         m_call  = fn;
762     }
763 
764     /// ditto
765     final void reset( void delegate() dg ) nothrow @nogc
766     {
767         reset();
768         m_call  = dg;
769     }
770 
771     ///////////////////////////////////////////////////////////////////////////
772     // General Properties
773     ///////////////////////////////////////////////////////////////////////////
774 
775 
776     /// A fiber may occupy one of three states: HOLD, EXEC, and TERM.
777     enum State
778     {
779         /** The HOLD state applies to any fiber that is suspended and ready to
780         be called. */
781         HOLD,
782         /** The EXEC state will be set for any fiber that is currently
783         executing. */
784         EXEC,
785         /** The TERM state is set when a fiber terminates. Once a fiber
786         terminates, it must be reset before it may be called again. */
787         TERM
788     }
789 
790 
791     /**
792      * Gets the current state of this fiber.
793      *
794      * Returns:
795      *  The state of this fiber as an enumerated value.
796      */
797     final @property State state() const @safe pure nothrow @nogc
798     {
799         return m_state;
800     }
801 
802 
803     ///////////////////////////////////////////////////////////////////////////
804     // Actions on Calling Fiber
805     ///////////////////////////////////////////////////////////////////////////
806 
807 
808     /**
809      * Forces a context switch to occur away from the calling fiber.
810      */
811     static void yield() nothrow @nogc
812     {
813         Fiber   cur = getThis();
814         assert( cur, "Fiber.yield() called with no active fiber" );
815         assert( cur.m_state == State.EXEC );
816 
817         static if ( __traits( compiles, ucontext_t ) )
818           cur.m_ucur = &cur.m_utxt;
819 
820         cur.m_state = State.HOLD;
821         cur.switchOut();
822         cur.m_state = State.EXEC;
823     }
824 
825 
826     /**
827      * Forces a context switch to occur away from the calling fiber and then
828      * throws obj in the calling fiber.
829      *
830      * Params:
831      *  t = The object to throw.
832      *
833      * In:
834      *  t must not be null.
835      */
836     static void yieldAndThrow( Throwable t ) nothrow @nogc
837     in
838     {
839         assert( t );
840     }
841     do
842     {
843         Fiber   cur = getThis();
844         assert( cur, "Fiber.yield() called with no active fiber" );
845         assert( cur.m_state == State.EXEC );
846 
847         static if ( __traits( compiles, ucontext_t ) )
848           cur.m_ucur = &cur.m_utxt;
849 
850         cur.m_unhandled = t;
851         cur.m_state = State.HOLD;
852         cur.switchOut();
853         cur.m_state = State.EXEC;
854     }
855 
856 
857     ///////////////////////////////////////////////////////////////////////////
858     // Fiber Accessors
859     ///////////////////////////////////////////////////////////////////////////
860 
861 
862     /**
863      * Provides a reference to the calling fiber or null if no fiber is
864      * currently active.
865      *
866      * Returns:
867      *  The fiber object representing the calling fiber or null if no fiber
868      *  is currently active within this thread. The result of deleting this object is undefined.
869      */
870     static Fiber getThis() @safe nothrow @nogc
871     {
872         return sm_this;
873     }
874 
875 
876     ///////////////////////////////////////////////////////////////////////////
877     // Static Initialization
878     ///////////////////////////////////////////////////////////////////////////
879 
880 
881     version (Posix)
882     {
883         static this()
884         {
885             static if ( __traits( compiles, ucontext_t ) )
886             {
887               int status = getcontext( &sm_utxt );
888               assert( status == 0 );
889             }
890         }
891     }
892 
893 private:
894 
895     //
896     // Fiber entry point.  Invokes the function or delegate passed on
897     // construction (if any).
898     //
899     final void run()
900     {
901         m_call();
902     }
903 
904     //
905     // Standard fiber data
906     //
907     Callable            m_call;
908     bool                m_isRunning;
909     Throwable           m_unhandled;
910     State               m_state;
911 
912 
913 private:
914     ///////////////////////////////////////////////////////////////////////////
915     // Stack Management
916     ///////////////////////////////////////////////////////////////////////////
917 
918 
919     //
920     // Allocate a new stack for this fiber.
921     //
922     final void allocStack( size_t sz, size_t guardPageSize ) nothrow @system
923     in
924     {
925         assert( !m_pmem && !m_ctxt );
926     }
927     do
928     {
929         // adjust alloc size to a multiple of pageSize
930         sz += pageSize - 1;
931         sz -= sz % pageSize;
932 
933         // NOTE: This instance of Thread.Context is dynamic so Fiber objects
934         //       can be collected by the GC so long as no user level references
935         //       to the object exist.  If m_ctxt were not dynamic then its
936         //       presence in the global context list would be enough to keep
937         //       this object alive indefinitely.  An alternative to allocating
938         //       room for this struct explicitly would be to mash it into the
939         //       base of the stack being allocated below.  However, doing so
940         //       requires too much special logic to be worthwhile.
941         m_ctxt = new StackContext;
942 
943         version (Windows)
944         {
945             // reserve memory for stack
946             m_pmem = VirtualAlloc( null,
947                                    sz + guardPageSize,
948                                    MEM_RESERVE,
949                                    PAGE_NOACCESS );
950             if ( !m_pmem )
951                 onOutOfMemoryError();
952 
953             version (StackGrowsDown)
954             {
955                 void* stack = m_pmem + guardPageSize;
956                 void* guard = m_pmem;
957                 void* pbase = stack + sz;
958             }
959             else
960             {
961                 void* stack = m_pmem;
962                 void* guard = m_pmem + sz;
963                 void* pbase = stack;
964             }
965 
966             // allocate reserved stack segment
967             stack = VirtualAlloc( stack,
968                                   sz,
969                                   MEM_COMMIT,
970                                   PAGE_READWRITE );
971             if ( !stack )
972                 onOutOfMemoryError();
973 
974             if (guardPageSize)
975             {
976                 // allocate reserved guard page
977                 guard = VirtualAlloc( guard,
978                                       guardPageSize,
979                                       MEM_COMMIT,
980                                       PAGE_READWRITE | PAGE_GUARD );
981                 if ( !guard )
982                     onOutOfMemoryError();
983             }
984 
985             m_ctxt.bstack = pbase;
986             m_ctxt.tstack = pbase;
987             m_size = sz;
988         }
989         else
990         {
991             version (Posix) import core.sys.posix.sys.mman; // mmap, MAP_ANON
992 
993             static if ( __traits( compiles, ucontext_t ) )
994             {
995                 // Stack size must be at least the minimum allowable by the OS.
996                 if (sz < MINSIGSTKSZ)
997                     sz = MINSIGSTKSZ;
998             }
999 
1000             static if ( __traits( compiles, mmap ) )
1001             {
1002                 // Allocate more for the memory guard
1003                 sz += guardPageSize;
1004 
1005                 int mmap_flags = MAP_PRIVATE | MAP_ANON;
1006                 version (OpenBSD)
1007                     mmap_flags |= MAP_STACK;
1008 
1009                 m_pmem = mmap( null,
1010                                sz,
1011                                PROT_READ | PROT_WRITE,
1012                                mmap_flags,
1013                                -1,
1014                                0 );
1015                 if ( m_pmem == MAP_FAILED )
1016                     m_pmem = null;
1017             }
1018             else static if ( __traits( compiles, valloc ) )
1019             {
1020                 m_pmem = valloc( sz );
1021             }
1022             else static if ( __traits( compiles, malloc ) )
1023             {
1024                 m_pmem = malloc( sz );
1025             }
1026             else
1027             {
1028                 m_pmem = null;
1029             }
1030 
1031             if ( !m_pmem )
1032                 onOutOfMemoryError();
1033 
1034             version (StackGrowsDown)
1035             {
1036                 m_ctxt.bstack = m_pmem + sz;
1037                 m_ctxt.tstack = m_pmem + sz;
1038                 void* guard = m_pmem;
1039             }
1040             else
1041             {
1042                 m_ctxt.bstack = m_pmem;
1043                 m_ctxt.tstack = m_pmem;
1044                 void* guard = m_pmem + sz - guardPageSize;
1045             }
1046             m_size = sz;
1047 
1048             static if ( __traits( compiles, mmap ) )
1049             {
1050                 if (guardPageSize)
1051                 {
1052                     // protect end of stack
1053                     if ( mprotect(guard, guardPageSize, PROT_NONE) == -1 )
1054                         abort();
1055                 }
1056             }
1057             else
1058             {
1059                 // Supported only for mmap allocated memory - results are
1060                 // undefined if applied to memory not obtained by mmap
1061             }
1062         }
1063 
1064         Thread.add( m_ctxt );
1065     }
1066 
1067 
1068     //
1069     // Free this fiber's stack.
1070     //
1071     final void freeStack() nothrow @nogc @system
1072     in
1073     {
1074         assert( m_pmem && m_ctxt );
1075     }
1076     do
1077     {
1078         // NOTE: m_ctxt is guaranteed to be alive because it is held in the
1079         //       global context list.
1080         Thread.slock.lock_nothrow();
1081         scope(exit) Thread.slock.unlock_nothrow();
1082         Thread.remove( m_ctxt );
1083 
1084         version (Windows)
1085         {
1086             VirtualFree( m_pmem, 0, MEM_RELEASE );
1087         }
1088         else
1089         {
1090             import core.sys.posix.sys.mman; // munmap
1091 
1092             static if ( __traits( compiles, mmap ) )
1093             {
1094                 munmap( m_pmem, m_size );
1095             }
1096             else static if ( __traits( compiles, valloc ) )
1097             {
1098                 free( m_pmem );
1099             }
1100             else static if ( __traits( compiles, malloc ) )
1101             {
1102                 free( m_pmem );
1103             }
1104         }
1105         m_pmem = null;
1106         m_ctxt = null;
1107     }
1108 
1109 
1110     //
1111     // Initialize the allocated stack.
1112     // Look above the definition of 'class Fiber' for some information about the implementation of this routine
1113     //
1114     final void initStack() nothrow @nogc @system
1115     in
1116     {
1117         assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack );
1118         assert( cast(size_t) m_ctxt.bstack % (void*).sizeof == 0 );
1119     }
1120     do
1121     {
1122         void* pstack = m_ctxt.tstack;
1123         scope( exit )  m_ctxt.tstack = pstack;
1124 
1125         void push( size_t val ) nothrow
1126         {
1127             version (StackGrowsDown)
1128             {
1129                 pstack -= size_t.sizeof;
1130                 *(cast(size_t*) pstack) = val;
1131             }
1132             else
1133             {
1134                 pstack += size_t.sizeof;
1135                 *(cast(size_t*) pstack) = val;
1136             }
1137         }
1138 
1139         // NOTE: On OS X the stack must be 16-byte aligned according
1140         // to the IA-32 call spec. For x86_64 the stack also needs to
1141         // be aligned to 16-byte according to SysV AMD64 ABI.
1142         version (AlignFiberStackTo16Byte)
1143         {
1144             version (StackGrowsDown)
1145             {
1146                 pstack = cast(void*)(cast(size_t)(pstack) - (cast(size_t)(pstack) & 0x0F));
1147             }
1148             else
1149             {
1150                 pstack = cast(void*)(cast(size_t)(pstack) + (cast(size_t)(pstack) & 0x0F));
1151             }
1152         }
1153 
1154         version (AsmX86_Windows)
1155         {
1156             version (StackGrowsDown) {} else static assert( false );
1157 
1158             // On Windows Server 2008 and 2008 R2, an exploit mitigation
1159             // technique known as SEHOP is activated by default. To avoid
1160             // hijacking of the exception handler chain, the presence of a
1161             // Windows-internal handler (ntdll.dll!FinalExceptionHandler) at
1162             // its end is tested by RaiseException. If it is not present, all
1163             // handlers are disregarded, and the program is thus aborted
1164             // (see http://blogs.technet.com/b/srd/archive/2009/02/02/
1165             // preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx).
1166             // For new threads, this handler is installed by Windows immediately
1167             // after creation. To make exception handling work in fibers, we
1168             // have to insert it for our new stacks manually as well.
1169             //
1170             // To do this, we first determine the handler by traversing the SEH
1171             // chain of the current thread until its end, and then construct a
1172             // registration block for the last handler on the newly created
1173             // thread. We then continue to push all the initial register values
1174             // for the first context switch as for the other implementations.
1175             //
1176             // Note that this handler is never actually invoked, as we install
1177             // our own one on top of it in the fiber entry point function.
1178             // Thus, it should not have any effects on OSes not implementing
1179             // exception chain verification.
1180 
1181             alias fp_t = void function(); // Actual signature not relevant.
1182             static struct EXCEPTION_REGISTRATION
1183             {
1184                 EXCEPTION_REGISTRATION* next; // sehChainEnd if last one.
1185                 fp_t handler;
1186             }
1187             enum sehChainEnd = cast(EXCEPTION_REGISTRATION*) 0xFFFFFFFF;
1188 
1189             __gshared static fp_t finalHandler = null;
1190             if ( finalHandler is null )
1191             {
1192                 static EXCEPTION_REGISTRATION* fs0() nothrow
1193                 {
1194                     asm pure nothrow @nogc
1195                     {
1196                         naked;
1197                         mov EAX, FS:[0];
1198                         ret;
1199                     }
1200                 }
1201                 auto reg = fs0();
1202                 while ( reg.next != sehChainEnd ) reg = reg.next;
1203 
1204                 // Benign races are okay here, just to avoid re-lookup on every
1205                 // fiber creation.
1206                 finalHandler = reg.handler;
1207             }
1208 
1209             // When linking with /safeseh (supported by LDC, but not DMD)
1210             // the exception chain must not extend to the very top
1211             // of the stack, otherwise the exception chain is also considered
1212             // invalid. Reserving additional 4 bytes at the top of the stack will
1213             // keep the EXCEPTION_REGISTRATION below that limit
1214             size_t reserve = EXCEPTION_REGISTRATION.sizeof + 4;
1215             pstack -= reserve;
1216             *(cast(EXCEPTION_REGISTRATION*)pstack) =
1217                 EXCEPTION_REGISTRATION( sehChainEnd, finalHandler );
1218             auto pChainEnd = pstack;
1219 
1220             push( cast(size_t) &fiber_entryPoint );                 // EIP
1221             push( cast(size_t) m_ctxt.bstack - reserve );           // EBP
1222             push( 0x00000000 );                                     // EDI
1223             push( 0x00000000 );                                     // ESI
1224             push( 0x00000000 );                                     // EBX
1225             push( cast(size_t) pChainEnd );                         // FS:[0]
1226             push( cast(size_t) m_ctxt.bstack );                     // FS:[4]
1227             push( cast(size_t) m_ctxt.bstack - m_size );            // FS:[8]
1228             push( 0x00000000 );                                     // EAX
1229         }
1230         else version (AsmX86_64_Windows)
1231         {
1232             // Using this trampoline instead of the raw fiber_entryPoint
1233             // ensures that during context switches, source and destination
1234             // stacks have the same alignment. Otherwise, the stack would need
1235             // to be shifted by 8 bytes for the first call, as fiber_entryPoint
1236             // is an actual function expecting a stack which is not aligned
1237             // to 16 bytes.
1238             static void trampoline()
1239             {
1240                 asm pure nothrow @nogc
1241                 {
1242                     naked;
1243                     sub RSP, 32; // Shadow space (Win64 calling convention)
1244                     call fiber_entryPoint;
1245                     xor RCX, RCX; // This should never be reached, as
1246                     jmp RCX;      // fiber_entryPoint must never return.
1247                 }
1248             }
1249 
1250             push( cast(size_t) &trampoline );                       // RIP
1251             push( 0x00000000_00000000 );                            // RBP
1252             push( 0x00000000_00000000 );                            // R12
1253             push( 0x00000000_00000000 );                            // R13
1254             push( 0x00000000_00000000 );                            // R14
1255             push( 0x00000000_00000000 );                            // R15
1256             push( 0x00000000_00000000 );                            // RDI
1257             push( 0x00000000_00000000 );                            // RSI
1258             push( 0x00000000_00000000 );                            // XMM6 (high)
1259             push( 0x00000000_00000000 );                            // XMM6 (low)
1260             push( 0x00000000_00000000 );                            // XMM7 (high)
1261             push( 0x00000000_00000000 );                            // XMM7 (low)
1262             push( 0x00000000_00000000 );                            // XMM8 (high)
1263             push( 0x00000000_00000000 );                            // XMM8 (low)
1264             push( 0x00000000_00000000 );                            // XMM9 (high)
1265             push( 0x00000000_00000000 );                            // XMM9 (low)
1266             push( 0x00000000_00000000 );                            // XMM10 (high)
1267             push( 0x00000000_00000000 );                            // XMM10 (low)
1268             push( 0x00000000_00000000 );                            // XMM11 (high)
1269             push( 0x00000000_00000000 );                            // XMM11 (low)
1270             push( 0x00000000_00000000 );                            // XMM12 (high)
1271             push( 0x00000000_00000000 );                            // XMM12 (low)
1272             push( 0x00000000_00000000 );                            // XMM13 (high)
1273             push( 0x00000000_00000000 );                            // XMM13 (low)
1274             push( 0x00000000_00000000 );                            // XMM14 (high)
1275             push( 0x00000000_00000000 );                            // XMM14 (low)
1276             push( 0x00000000_00000000 );                            // XMM15 (high)
1277             push( 0x00000000_00000000 );                            // XMM15 (low)
1278             push( 0x00000000_00000000 );                            // RBX
1279             push( 0xFFFFFFFF_FFFFFFFF );                            // GS:[0]
1280             version (StackGrowsDown)
1281             {
1282                 push( cast(size_t) m_ctxt.bstack );                 // GS:[8]
1283                 push( cast(size_t) m_ctxt.bstack - m_size );        // GS:[16]
1284             }
1285             else
1286             {
1287                 push( cast(size_t) m_ctxt.bstack );                 // GS:[8]
1288                 push( cast(size_t) m_ctxt.bstack + m_size );        // GS:[16]
1289             }
1290         }
1291         else version (AsmX86_Posix)
1292         {
1293             push( 0x00000000 );                                     // Return address of fiber_entryPoint call
1294             push( cast(size_t) &fiber_entryPoint );                 // EIP
1295             push( cast(size_t) m_ctxt.bstack );                     // EBP
1296             push( 0x00000000 );                                     // EDI
1297             push( 0x00000000 );                                     // ESI
1298             push( 0x00000000 );                                     // EBX
1299             push( 0x00000000 );                                     // EAX
1300         }
1301         else version (AsmX86_64_Posix)
1302         {
1303             push( 0x00000000_00000000 );                            // Return address of fiber_entryPoint call
1304             push( cast(size_t) &fiber_entryPoint );                 // RIP
1305             push( cast(size_t) m_ctxt.bstack );                     // RBP
1306             push( 0x00000000_00000000 );                            // RBX
1307             push( 0x00000000_00000000 );                            // R12
1308             push( 0x00000000_00000000 );                            // R13
1309             push( 0x00000000_00000000 );                            // R14
1310             push( 0x00000000_00000000 );                            // R15
1311         }
1312         else version (AsmPPC_Posix)
1313         {
1314             version (StackGrowsDown)
1315             {
1316                 pstack -= int.sizeof * 5;
1317             }
1318             else
1319             {
1320                 pstack += int.sizeof * 5;
1321             }
1322 
1323             push( cast(size_t) &fiber_entryPoint );     // link register
1324             push( 0x00000000 );                         // control register
1325             push( 0x00000000 );                         // old stack pointer
1326 
1327             // GPR values
1328             version (StackGrowsDown)
1329             {
1330                 pstack -= int.sizeof * 20;
1331             }
1332             else
1333             {
1334                 pstack += int.sizeof * 20;
1335             }
1336 
1337             assert( (cast(size_t) pstack & 0x0f) == 0 );
1338         }
1339         else version (AsmPPC_Darwin)
1340         {
1341             version (StackGrowsDown) {}
1342             else static assert(false, "PowerPC Darwin only supports decrementing stacks");
1343 
1344             uint wsize = size_t.sizeof;
1345 
1346             // linkage + regs + FPRs + VRs
1347             uint space = 8 * wsize + 20 * wsize + 18 * 8 + 12 * 16;
1348             (cast(ubyte*)pstack - space)[0 .. space] = 0;
1349 
1350             pstack -= wsize * 6;
1351             *cast(size_t*)pstack = cast(size_t) &fiber_entryPoint; // LR
1352             pstack -= wsize * 22;
1353 
1354             // On Darwin PPC64 pthread self is in R13 (which is reserved).
1355             // At present, it is not safe to migrate fibers between threads, but if that
1356             // changes, then updating the value of R13 will also need to be handled.
1357             version (PPC64)
1358               *cast(size_t*)(pstack + wsize) = cast(size_t) Thread.getThis().m_addr;
1359             assert( (cast(size_t) pstack & 0x0f) == 0 );
1360         }
1361         else version (AsmMIPS_O32_Posix)
1362         {
1363             version (StackGrowsDown) {}
1364             else static assert(0);
1365 
1366             /* We keep the FP registers and the return address below
1367              * the stack pointer, so they don't get scanned by the
1368              * GC. The last frame before swapping the stack pointer is
1369              * organized like the following.
1370              *
1371              *     |-----------|<= frame pointer
1372              *     |    $gp    |
1373              *     |   $s0-8   |
1374              *     |-----------|<= stack pointer
1375              *     |    $ra    |
1376              *     |  align(8) |
1377              *     |  $f20-30  |
1378              *     |-----------|
1379              *
1380              */
1381             enum SZ_GP = 10 * size_t.sizeof; // $gp + $s0-8
1382             enum SZ_RA = size_t.sizeof;      // $ra
1383             version (MIPS_HardFloat)
1384             {
1385                 enum SZ_FP = 6 * 8;          // $f20-30
1386                 enum ALIGN = -(SZ_FP + SZ_RA) & (8 - 1);
1387             }
1388             else
1389             {
1390                 enum SZ_FP = 0;
1391                 enum ALIGN = 0;
1392             }
1393 
1394             enum BELOW = SZ_FP + ALIGN + SZ_RA;
1395             enum ABOVE = SZ_GP;
1396             enum SZ = BELOW + ABOVE;
1397 
1398             (cast(ubyte*)pstack - SZ)[0 .. SZ] = 0;
1399             pstack -= ABOVE;
1400             *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint;
1401         }
1402         else version (AsmLoongArch64_Posix)
1403         {
1404             // Like others, FP registers and return address ($r1) are kept
1405             // below the saved stack top (tstack) to hide from GC scanning.
1406             // fiber_switchContext expects newp sp to look like this:
1407             //   10: $r21 (reserved)
1408             //    9: $r22 (frame pointer)
1409             //    8: $r23
1410             //   ...
1411             //    0: $r31 <-- newp tstack
1412             //   -1: $r1  (return address)  [&fiber_entryPoint]
1413             //   -2: $f24
1414             //   ...
1415             //   -9: $f31
1416 
1417             version (StackGrowsDown) {}
1418             else
1419                 static assert(false, "Only full descending stacks supported on LoongArch64");
1420 
1421             // Only need to set return address ($r1).  Everything else is fine
1422             // zero initialized.
1423             pstack -= size_t.sizeof * 11;    // skip past space reserved for $r21-$r31
1424             push (cast(size_t) &fiber_entryPoint);
1425             pstack += size_t.sizeof;         // adjust sp (newp) above lr
1426         }
1427         else version (AsmAArch64_Posix)
1428         {
1429             // Like others, FP registers and return address (lr) are kept
1430             // below the saved stack top (tstack) to hide from GC scanning.
1431             // fiber_switchContext expects newp sp to look like this:
1432             //   19: x19
1433             //   ...
1434             //    9: x29 (fp)  <-- newp tstack
1435             //    8: x30 (lr)  [&fiber_entryPoint]
1436             //    7: d8
1437             //   ...
1438             //    0: d15
1439 
1440             version (StackGrowsDown) {}
1441             else
1442                 static assert(false, "Only full descending stacks supported on AArch64");
1443 
1444             // Only need to set return address (lr).  Everything else is fine
1445             // zero initialized.
1446             pstack -= size_t.sizeof * 11;    // skip past x19-x29
1447             push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs
1448             pstack += size_t.sizeof;         // adjust sp (newp) above lr
1449         }
1450         else version (AsmARM_Posix)
1451         {
1452             /* We keep the FP registers and the return address below
1453              * the stack pointer, so they don't get scanned by the
1454              * GC. The last frame before swapping the stack pointer is
1455              * organized like the following.
1456              *
1457              *   |  |-----------|<= 'frame starts here'
1458              *   |  |     fp    | (the actual frame pointer, r11 isn't
1459              *   |  |   r10-r4  |  updated and still points to the previous frame)
1460              *   |  |-----------|<= stack pointer
1461              *   |  |     lr    |
1462              *   |  | 4byte pad |
1463              *   |  |   d15-d8  |(if FP supported)
1464              *   |  |-----------|
1465              *   Y
1466              *   stack grows down: The pointer value here is smaller than some lines above
1467              */
1468             // frame pointer can be zero, r10-r4 also zero initialized
1469             version (StackGrowsDown)
1470                 pstack -= int.sizeof * 8;
1471             else
1472                 static assert(false, "Only full descending stacks supported on ARM");
1473 
1474             // link register
1475             push( cast(size_t) &fiber_entryPoint );
1476             /*
1477              * We do not push padding and d15-d8 as those are zero initialized anyway
1478              * Position the stack pointer above the lr register
1479              */
1480             pstack += int.sizeof * 1;
1481         }
1482         else static if ( __traits( compiles, ucontext_t ) )
1483         {
1484             getcontext( &m_utxt );
1485             m_utxt.uc_stack.ss_sp   = m_pmem;
1486             m_utxt.uc_stack.ss_size = m_size;
1487             makecontext( &m_utxt, &fiber_entryPoint, 0 );
1488             // NOTE: If ucontext is being used then the top of the stack will
1489             //       be a pointer to the ucontext_t struct for that fiber.
1490             push( cast(size_t) &m_utxt );
1491         }
1492 	else version (FreeStanding)
1493 		assert(0);
1494         else
1495             static assert(0, "Not implemented");
1496     }
1497 
1498 
1499     StackContext*   m_ctxt;
1500     size_t          m_size;
1501     void*           m_pmem;
1502 
1503     static if ( __traits( compiles, ucontext_t ) )
1504     {
1505         // NOTE: The static ucontext instance is used to represent the context
1506         //       of the executing thread.
1507         static ucontext_t       sm_utxt = void;
1508         ucontext_t              m_utxt  = void;
1509         ucontext_t*             m_ucur  = null;
1510     }
1511 
1512 
1513 private:
1514     ///////////////////////////////////////////////////////////////////////////
1515     // Storage of Active Fiber
1516     ///////////////////////////////////////////////////////////////////////////
1517 
1518 
1519     //
1520     // Sets a thread-local reference to the current fiber object.
1521     //
1522     static void setThis( Fiber f ) nothrow @nogc
1523     {
1524         sm_this = f;
1525     }
1526 
1527     static Fiber sm_this;
1528 
1529 
1530 private:
1531     ///////////////////////////////////////////////////////////////////////////
1532     // Context Switching
1533     ///////////////////////////////////////////////////////////////////////////
1534 
1535 
1536     //
1537     // Switches into the stack held by this fiber.
1538     //
1539     final void switchIn() nothrow @nogc @system
1540     {
1541         Thread  tobj = Thread.getThis();
1542         void**  oldp = &tobj.m_curr.tstack;
1543         void*   newp = m_ctxt.tstack;
1544 
1545         // NOTE: The order of operations here is very important.  The current
1546         //       stack top must be stored before m_lock is set, and pushContext
1547         //       must not be called until after m_lock is set.  This process
1548         //       is intended to prevent a race condition with the suspend
1549         //       mechanism used for garbage collection.  If it is not followed,
1550         //       a badly timed collection could cause the GC to scan from the
1551         //       bottom of one stack to the top of another, or to miss scanning
1552         //       a stack that still contains valid data.  The old stack pointer
1553         //       oldp will be set again before the context switch to guarantee
1554         //       that it points to exactly the correct stack location so the
1555         //       successive pop operations will succeed.
1556         *oldp = getStackTop();
1557         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
1558         tobj.pushContext( m_ctxt );
1559 
1560         fiber_switchContext( oldp, newp );
1561 
1562         // NOTE: As above, these operations must be performed in a strict order
1563         //       to prevent Bad Things from happening.
1564         tobj.popContext();
1565         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
1566         tobj.m_curr.tstack = tobj.m_curr.bstack;
1567     }
1568 
1569 
1570     //
1571     // Switches out of the current stack and into the enclosing stack.
1572     //
1573     final void switchOut() nothrow @nogc @system
1574     {
1575         Thread  tobj = Thread.getThis();
1576         void**  oldp = &m_ctxt.tstack;
1577         void*   newp = tobj.m_curr.within.tstack;
1578 
1579         // NOTE: The order of operations here is very important.  The current
1580         //       stack top must be stored before m_lock is set, and pushContext
1581         //       must not be called until after m_lock is set.  This process
1582         //       is intended to prevent a race condition with the suspend
1583         //       mechanism used for garbage collection.  If it is not followed,
1584         //       a badly timed collection could cause the GC to scan from the
1585         //       bottom of one stack to the top of another, or to miss scanning
1586         //       a stack that still contains valid data.  The old stack pointer
1587         //       oldp will be set again before the context switch to guarantee
1588         //       that it points to exactly the correct stack location so the
1589         //       successive pop operations will succeed.
1590         *oldp = getStackTop();
1591         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
1592 
1593         fiber_switchContext( oldp, newp );
1594 
1595         // NOTE: As above, these operations must be performed in a strict order
1596         //       to prevent Bad Things from happening.
1597         // NOTE: If use of this fiber is multiplexed across threads, the thread
1598         //       executing here may be different from the one above, so get the
1599         //       current thread handle before unlocking, etc.
1600         tobj = Thread.getThis();
1601         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
1602         tobj.m_curr.tstack = tobj.m_curr.bstack;
1603     }
1604 }
1605 
1606 ///
1607 unittest {
1608     int counter;
1609 
1610     class DerivedFiber : Fiber
1611     {
1612         this()
1613         {
1614             super( &run );
1615         }
1616 
1617     private :
1618         void run()
1619         {
1620             counter += 2;
1621         }
1622     }
1623 
1624     void fiberFunc()
1625     {
1626         counter += 4;
1627         Fiber.yield();
1628         counter += 8;
1629     }
1630 
1631     // create instances of each type
1632     Fiber derived = new DerivedFiber();
1633     Fiber composed = new Fiber( &fiberFunc );
1634 
1635     assert( counter == 0 );
1636 
1637     derived.call();
1638     assert( counter == 2, "Derived fiber increment." );
1639 
1640     composed.call();
1641     assert( counter == 6, "First composed fiber increment." );
1642 
1643     counter += 16;
1644     assert( counter == 22, "Calling context increment." );
1645 
1646     composed.call();
1647     assert( counter == 30, "Second composed fiber increment." );
1648 
1649     // since each fiber has run to completion, each should have state TERM
1650     assert( derived.state == Fiber.State.TERM );
1651     assert( composed.state == Fiber.State.TERM );
1652 }
1653 
1654 version (CoreUnittest)
1655 {
1656     class TestFiber : Fiber
1657     {
1658         this()
1659         {
1660             super(&run);
1661         }
1662 
1663         void run()
1664         {
1665             foreach (i; 0 .. 1000)
1666             {
1667                 sum += i;
1668                 Fiber.yield();
1669             }
1670         }
1671 
1672         enum expSum = 1000 * 999 / 2;
1673         size_t sum;
1674     }
1675 
1676     void runTen()
1677     {
1678         TestFiber[10] fibs;
1679         foreach (ref fib; fibs)
1680             fib = new TestFiber();
1681 
1682         bool cont;
1683         do {
1684             cont = false;
1685             foreach (fib; fibs) {
1686                 if (fib.state == Fiber.State.HOLD)
1687                 {
1688                     fib.call();
1689                     cont |= fib.state != Fiber.State.TERM;
1690                 }
1691             }
1692         } while (cont);
1693 
1694         foreach (fib; fibs)
1695         {
1696             assert(fib.sum == TestFiber.expSum);
1697         }
1698     }
1699 }
1700 
1701 
1702 // Single thread running separate fibers
1703 unittest
1704 {
1705     runTen();
1706 }
1707 
1708 
1709 // Multiple threads running separate fibers
1710 unittest
1711 {
1712     auto group = new ThreadGroup();
1713     foreach (_; 0 .. 4)
1714     {
1715         group.create(&runTen);
1716     }
1717     group.joinAll();
1718 }
1719 
1720 
1721 // Multiple threads running shared fibers
1722 unittest
1723 {
1724     shared bool[10] locks;
1725     TestFiber[10] fibs;
1726 
1727     void runShared()
1728     {
1729         bool cont;
1730         do {
1731             cont = false;
1732             foreach (idx; 0 .. 10)
1733             {
1734                 if (cas(&locks[idx], false, true))
1735                 {
1736                     if (fibs[idx].state == Fiber.State.HOLD)
1737                     {
1738                         fibs[idx].call();
1739                         cont |= fibs[idx].state != Fiber.State.TERM;
1740                     }
1741                     locks[idx] = false;
1742                 }
1743                 else
1744                 {
1745                     cont = true;
1746                 }
1747             }
1748         } while (cont);
1749     }
1750 
1751     foreach (ref fib; fibs)
1752     {
1753         fib = new TestFiber();
1754     }
1755 
1756     auto group = new ThreadGroup();
1757     foreach (_; 0 .. 4)
1758     {
1759         group.create(&runShared);
1760     }
1761     group.joinAll();
1762 
1763     foreach (fib; fibs)
1764     {
1765         assert(fib.sum == TestFiber.expSum);
1766     }
1767 }
1768 
1769 
1770 // Test exception handling inside fibers.
1771 unittest
1772 {
1773     enum MSG = "Test message.";
1774     string caughtMsg;
1775     (new Fiber({
1776         try
1777         {
1778             throw new Exception(MSG);
1779         }
1780         catch (Exception e)
1781         {
1782             caughtMsg = e.msg;
1783         }
1784     })).call();
1785     assert(caughtMsg == MSG);
1786 }
1787 
1788 
1789 unittest
1790 {
1791     int x = 0;
1792 
1793     (new Fiber({
1794         x++;
1795     })).call();
1796     assert( x == 1 );
1797 }
1798 
1799 nothrow unittest
1800 {
1801     new Fiber({}).call!(Fiber.Rethrow.no)();
1802 }
1803 
1804 unittest
1805 {
1806     new Fiber({}).call(Fiber.Rethrow.yes);
1807     new Fiber({}).call(Fiber.Rethrow.no);
1808 }
1809 
1810 unittest
1811 {
1812     enum MSG = "Test message.";
1813 
1814     try
1815     {
1816         (new Fiber(function() {
1817             throw new Exception( MSG );
1818         })).call();
1819         assert( false, "Expected rethrown exception." );
1820     }
1821     catch ( Throwable t )
1822     {
1823         assert( t.msg == MSG );
1824     }
1825 }
1826 
1827 // Test exception chaining when switching contexts in finally blocks.
1828 unittest
1829 {
1830     static void throwAndYield(string msg) {
1831       try {
1832         throw new Exception(msg);
1833       } finally {
1834         Fiber.yield();
1835       }
1836     }
1837 
1838     static void fiber(string name) {
1839       try {
1840         try {
1841           throwAndYield(name ~ ".1");
1842         } finally {
1843           throwAndYield(name ~ ".2");
1844         }
1845       } catch (Exception e) {
1846         assert(e.msg == name ~ ".1");
1847         assert(e.next);
1848         assert(e.next.msg == name ~ ".2");
1849         assert(!e.next.next);
1850       }
1851     }
1852 
1853     auto first = new Fiber(() => fiber("first"));
1854     auto second = new Fiber(() => fiber("second"));
1855     first.call();
1856     second.call();
1857     first.call();
1858     second.call();
1859     first.call();
1860     second.call();
1861     assert(first.state == Fiber.State.TERM);
1862     assert(second.state == Fiber.State.TERM);
1863 }
1864 
1865 // Test Fiber resetting
1866 unittest
1867 {
1868     static string method;
1869 
1870     static void foo()
1871     {
1872         method = "foo";
1873     }
1874 
1875     void bar()
1876     {
1877         method = "bar";
1878     }
1879 
1880     static void expect(Fiber fib, string s)
1881     {
1882         assert(fib.state == Fiber.State.HOLD);
1883         fib.call();
1884         assert(fib.state == Fiber.State.TERM);
1885         assert(method == s); method = null;
1886     }
1887     auto fib = new Fiber(&foo);
1888     expect(fib, "foo");
1889 
1890     fib.reset();
1891     expect(fib, "foo");
1892 
1893     fib.reset(&foo);
1894     expect(fib, "foo");
1895 
1896     fib.reset(&bar);
1897     expect(fib, "bar");
1898 
1899     fib.reset(function void(){method = "function";});
1900     expect(fib, "function");
1901 
1902     fib.reset(delegate void(){method = "delegate";});
1903     expect(fib, "delegate");
1904 }
1905 
1906 // Test unsafe reset in hold state
1907 unittest
1908 {
1909     auto fib = new Fiber(function {ubyte[2048] buf = void; Fiber.yield();}, 4096);
1910     foreach (_; 0 .. 10)
1911     {
1912         fib.call();
1913         assert(fib.state == Fiber.State.HOLD);
1914         fib.reset();
1915     }
1916 }
1917 
1918 // stress testing GC stack scanning
1919 unittest
1920 {
1921     import core.memory;
1922     import core.time : dur;
1923 
1924     static void unreferencedThreadObject()
1925     {
1926         static void sleep() { Thread.sleep(dur!"msecs"(100)); }
1927         auto thread = new Thread(&sleep).start();
1928     }
1929     unreferencedThreadObject();
1930     GC.collect();
1931 
1932     static class Foo
1933     {
1934         this(int value)
1935         {
1936             _value = value;
1937         }
1938 
1939         int bar()
1940         {
1941             return _value;
1942         }
1943 
1944         int _value;
1945     }
1946 
1947     static void collect()
1948     {
1949         auto foo = new Foo(2);
1950         assert(foo.bar() == 2);
1951         GC.collect();
1952         Fiber.yield();
1953         GC.collect();
1954         assert(foo.bar() == 2);
1955     }
1956 
1957     auto fiber = new Fiber(&collect);
1958 
1959     fiber.call();
1960     GC.collect();
1961     fiber.call();
1962 
1963     // thread reference
1964     auto foo = new Foo(2);
1965 
1966     void collect2()
1967     {
1968         assert(foo.bar() == 2);
1969         GC.collect();
1970         Fiber.yield();
1971         GC.collect();
1972         assert(foo.bar() == 2);
1973     }
1974 
1975     fiber = new Fiber(&collect2);
1976 
1977     fiber.call();
1978     GC.collect();
1979     fiber.call();
1980 
1981     static void recurse(size_t cnt)
1982     {
1983         --cnt;
1984         Fiber.yield();
1985         if (cnt)
1986         {
1987             auto fib = new Fiber(() { recurse(cnt); });
1988             fib.call();
1989             GC.collect();
1990             fib.call();
1991         }
1992     }
1993     fiber = new Fiber(() { recurse(20); });
1994     fiber.call();
1995 }
1996 
1997 
1998 version (AsmX86_64_Windows)
1999 {
2000     // Test Windows x64 calling convention
2001     unittest
2002     {
2003         void testNonvolatileRegister(alias REG)()
2004         {
2005             auto zeroRegister = new Fiber(() {
2006                 mixin("asm pure nothrow @nogc { naked; xor "~REG~", "~REG~"; ret; }");
2007             });
2008             long after;
2009 
2010             mixin("asm pure nothrow @nogc { mov "~REG~", 0xFFFFFFFFFFFFFFFF; }");
2011             zeroRegister.call();
2012             mixin("asm pure nothrow @nogc { mov after, "~REG~"; }");
2013 
2014             assert(after == -1);
2015         }
2016 
2017         void testNonvolatileRegisterSSE(alias REG)()
2018         {
2019             auto zeroRegister = new Fiber(() {
2020                 mixin("asm pure nothrow @nogc { naked; xorpd "~REG~", "~REG~"; ret; }");
2021             });
2022             long[2] before = [0xFFFFFFFF_FFFFFFFF, 0xFFFFFFFF_FFFFFFFF], after;
2023 
2024             mixin("asm pure nothrow @nogc { movdqu "~REG~", before; }");
2025             zeroRegister.call();
2026             mixin("asm pure nothrow @nogc { movdqu after, "~REG~"; }");
2027 
2028             assert(before == after);
2029         }
2030 
2031         testNonvolatileRegister!("R12")();
2032         testNonvolatileRegister!("R13")();
2033         testNonvolatileRegister!("R14")();
2034         testNonvolatileRegister!("R15")();
2035         testNonvolatileRegister!("RDI")();
2036         testNonvolatileRegister!("RSI")();
2037         testNonvolatileRegister!("RBX")();
2038 
2039         testNonvolatileRegisterSSE!("XMM6")();
2040         testNonvolatileRegisterSSE!("XMM7")();
2041         testNonvolatileRegisterSSE!("XMM8")();
2042         testNonvolatileRegisterSSE!("XMM9")();
2043         testNonvolatileRegisterSSE!("XMM10")();
2044         testNonvolatileRegisterSSE!("XMM11")();
2045         testNonvolatileRegisterSSE!("XMM12")();
2046         testNonvolatileRegisterSSE!("XMM13")();
2047         testNonvolatileRegisterSSE!("XMM14")();
2048         testNonvolatileRegisterSSE!("XMM15")();
2049     }
2050 }
2051 
2052 
2053 version (D_InlineAsm_X86_64)
2054 {
2055     unittest
2056     {
2057         void testStackAlignment()
2058         {
2059             void* pRSP;
2060             asm pure nothrow @nogc
2061             {
2062                 mov pRSP, RSP;
2063             }
2064             assert((cast(size_t)pRSP & 0xF) == 0);
2065         }
2066 
2067         auto fib = new Fiber(&testStackAlignment);
2068         fib.call();
2069     }
2070 }