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