Home | History | Annotate | Line # | Download | only in thread
      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 Rnne 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 
     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 
     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 (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     }
     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     }
    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     }
    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     }
    143     else version (MIPS_O32)
    144     {
    145         version (Posix)
    146         {
    147             version = AsmMIPS_O32_Posix;
    148             version = AsmExternal;
    149         }
    150     }
    151     else version (AArch64)
    152     {
    153         version (Posix)
    154         {
    155             version = AsmAArch64_Posix;
    156             version = AsmExternal;
    157             version = AlignFiberStackTo16Byte;
    158         }
    159     }
    160     else version (ARM)
    161     {
    162         version (Posix)
    163         {
    164             version = AsmARM_Posix;
    165             version = AsmExternal;
    166         }
    167     }
    168     else version (SPARC)
    169     {
    170         // NOTE: The SPARC ABI specifies only doubleword alignment.
    171         version = AlignFiberStackTo16Byte;
    172     }
    173     else version (SPARC64)
    174     {
    175         version = AlignFiberStackTo16Byte;
    176     }
    177 
    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 
    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
    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
    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;
    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      */
    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      */
    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      */
    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
    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 
    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      */
    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
    798     final void reset( void function() fn ) nothrow @nogc
    799     {
    800         reset();
    801         m_call  = fn;
    802     }
    803 
    804     /// ditto
    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      */
    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      */
    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      */
    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      */
    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 
    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     //
    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     //
    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 
    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                 int mmap_flags = MAP_PRIVATE | MAP_ANON;
   1047                 version (OpenBSD)
   1048                     mmap_flags |= MAP_STACK;
   1049 
   1050                 m_pmem = mmap( null,
   1051                                sz,
   1052                                PROT_READ | PROT_WRITE,
   1053                                mmap_flags,
   1054                                -1,
   1055                                0 );
   1056                 if ( m_pmem == MAP_FAILED )
   1057                     m_pmem = null;
   1058             }
   1059             else static if ( __traits( compiles, valloc ) )
   1060             {
   1061                 m_pmem = valloc( sz );
   1062             }
   1063             else static if ( __traits( compiles, malloc ) )
   1064             {
   1065                 m_pmem = malloc( sz );
   1066             }
   1067             else
   1068             {
   1069                 m_pmem = null;
   1070             }
   1071 
   1072             if ( !m_pmem )
   1073                 onOutOfMemoryError();
   1074 
   1075             version (StackGrowsDown)
   1076             {
   1077                 m_ctxt.bstack = m_pmem + sz;
   1078                 m_ctxt.tstack = m_pmem + sz;
   1079                 void* guard = m_pmem;
   1080             }
   1081             else
   1082             {
   1083                 m_ctxt.bstack = m_pmem;
   1084                 m_ctxt.tstack = m_pmem;
   1085                 void* guard = m_pmem + sz - guardPageSize;
   1086             }
   1087             m_size = sz;
   1088 
   1089             static if ( __traits( compiles, mmap ) )
   1090             {
   1091                 if (guardPageSize)
   1092                 {
   1093                     // protect end of stack
   1094                     if ( mprotect(guard, guardPageSize, PROT_NONE) == -1 )
   1095                         abort();
   1096                 }
   1097             }
   1098             else
   1099             {
   1100                 // Supported only for mmap allocated memory - results are
   1101                 // undefined if applied to memory not obtained by mmap
   1102             }
   1103         }
   1104 
   1105         Thread.add( m_ctxt );
   1106     }
   1107 
   1108 
   1109     //
   1110     // Free this fiber's stack.
   1111     //
   1112     final void freeStack() nothrow @nogc
   1113     in
   1114     {
   1115         assert( m_pmem && m_ctxt );
   1116     }
   1117     do
   1118     {
   1119         // NOTE: m_ctxt is guaranteed to be alive because it is held in the
   1120         //       global context list.
   1121         Thread.slock.lock_nothrow();
   1122         scope(exit) Thread.slock.unlock_nothrow();
   1123         Thread.remove( m_ctxt );
   1124 
   1125         version (Windows)
   1126         {
   1127             VirtualFree( m_pmem, 0, MEM_RELEASE );
   1128         }
   1129         else
   1130         {
   1131             import core.sys.posix.sys.mman; // munmap
   1132 
   1133             static if ( __traits( compiles, mmap ) )
   1134             {
   1135                 munmap( m_pmem, m_size );
   1136             }
   1137             else static if ( __traits( compiles, valloc ) )
   1138             {
   1139                 free( m_pmem );
   1140             }
   1141             else static if ( __traits( compiles, malloc ) )
   1142             {
   1143                 free( m_pmem );
   1144             }
   1145         }
   1146         m_pmem = null;
   1147         m_ctxt = null;
   1148     }
   1149 
   1150 
   1151     //
   1152     // Initialize the allocated stack.
   1153     // Look above the definition of 'class Fiber' for some information about the implementation of this routine
   1154     //
   1155     final void initStack() nothrow @nogc
   1156     in
   1157     {
   1158         assert( m_ctxt.tstack && m_ctxt.tstack == m_ctxt.bstack );
   1159         assert( cast(size_t) m_ctxt.bstack % (void*).sizeof == 0 );
   1160     }
   1161     do
   1162     {
   1163         void* pstack = m_ctxt.tstack;
   1164         scope( exit )  m_ctxt.tstack = pstack;
   1165 
   1166         void push( size_t val ) nothrow
   1167         {
   1168             version (StackGrowsDown)
   1169             {
   1170                 pstack -= size_t.sizeof;
   1171                 *(cast(size_t*) pstack) = val;
   1172             }
   1173             else
   1174             {
   1175                 pstack += size_t.sizeof;
   1176                 *(cast(size_t*) pstack) = val;
   1177             }
   1178         }
   1179 
   1180         // NOTE: On OS X the stack must be 16-byte aligned according
   1181         // to the IA-32 call spec. For x86_64 the stack also needs to
   1182         // be aligned to 16-byte according to SysV AMD64 ABI.
   1183         version (AlignFiberStackTo16Byte)
   1184         {
   1185             version (StackGrowsDown)
   1186             {
   1187                 pstack = cast(void*)(cast(size_t)(pstack) - (cast(size_t)(pstack) & 0x0F));
   1188             }
   1189             else
   1190             {
   1191                 pstack = cast(void*)(cast(size_t)(pstack) + (cast(size_t)(pstack) & 0x0F));
   1192             }
   1193         }
   1194 
   1195         version (AsmX86_Windows)
   1196         {
   1197             version (StackGrowsDown) {} else static assert( false );
   1198 
   1199             // On Windows Server 2008 and 2008 R2, an exploit mitigation
   1200             // technique known as SEHOP is activated by default. To avoid
   1201             // hijacking of the exception handler chain, the presence of a
   1202             // Windows-internal handler (ntdll.dll!FinalExceptionHandler) at
   1203             // its end is tested by RaiseException. If it is not present, all
   1204             // handlers are disregarded, and the program is thus aborted
   1205             // (see http://blogs.technet.com/b/srd/archive/2009/02/02/
   1206             // preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx).
   1207             // For new threads, this handler is installed by Windows immediately
   1208             // after creation. To make exception handling work in fibers, we
   1209             // have to insert it for our new stacks manually as well.
   1210             //
   1211             // To do this, we first determine the handler by traversing the SEH
   1212             // chain of the current thread until its end, and then construct a
   1213             // registration block for the last handler on the newly created
   1214             // thread. We then continue to push all the initial register values
   1215             // for the first context switch as for the other implementations.
   1216             //
   1217             // Note that this handler is never actually invoked, as we install
   1218             // our own one on top of it in the fiber entry point function.
   1219             // Thus, it should not have any effects on OSes not implementing
   1220             // exception chain verification.
   1221 
   1222             alias fp_t = void function(); // Actual signature not relevant.
   1223             static struct EXCEPTION_REGISTRATION
   1224             {
   1225                 EXCEPTION_REGISTRATION* next; // sehChainEnd if last one.
   1226                 fp_t handler;
   1227             }
   1228             enum sehChainEnd = cast(EXCEPTION_REGISTRATION*) 0xFFFFFFFF;
   1229 
   1230             __gshared static fp_t finalHandler = null;
   1231             if ( finalHandler is null )
   1232             {
   1233                 static EXCEPTION_REGISTRATION* fs0() nothrow
   1234                 {
   1235                     asm pure nothrow @nogc
   1236                     {
   1237                         naked;
   1238                         mov EAX, FS:[0];
   1239                         ret;
   1240                     }
   1241                 }
   1242                 auto reg = fs0();
   1243                 while ( reg.next != sehChainEnd ) reg = reg.next;
   1244 
   1245                 // Benign races are okay here, just to avoid re-lookup on every
   1246                 // fiber creation.
   1247                 finalHandler = reg.handler;
   1248             }
   1249 
   1250             // When linking with /safeseh (supported by LDC, but not DMD)
   1251             // the exception chain must not extend to the very top
   1252             // of the stack, otherwise the exception chain is also considered
   1253             // invalid. Reserving additional 4 bytes at the top of the stack will
   1254             // keep the EXCEPTION_REGISTRATION below that limit
   1255             size_t reserve = EXCEPTION_REGISTRATION.sizeof + 4;
   1256             pstack -= reserve;
   1257             *(cast(EXCEPTION_REGISTRATION*)pstack) =
   1258                 EXCEPTION_REGISTRATION( sehChainEnd, finalHandler );
   1259             auto pChainEnd = pstack;
   1260 
   1261             push( cast(size_t) &fiber_entryPoint );                 // EIP
   1262             push( cast(size_t) m_ctxt.bstack - reserve );           // EBP
   1263             push( 0x00000000 );                                     // EDI
   1264             push( 0x00000000 );                                     // ESI
   1265             push( 0x00000000 );                                     // EBX
   1266             push( cast(size_t) pChainEnd );                         // FS:[0]
   1267             push( cast(size_t) m_ctxt.bstack );                     // FS:[4]
   1268             push( cast(size_t) m_ctxt.bstack - m_size );            // FS:[8]
   1269             push( 0x00000000 );                                     // EAX
   1270         }
   1271         else version (AsmX86_64_Windows)
   1272         {
   1273             // Using this trampoline instead of the raw fiber_entryPoint
   1274             // ensures that during context switches, source and destination
   1275             // stacks have the same alignment. Otherwise, the stack would need
   1276             // to be shifted by 8 bytes for the first call, as fiber_entryPoint
   1277             // is an actual function expecting a stack which is not aligned
   1278             // to 16 bytes.
   1279             static void trampoline()
   1280             {
   1281                 asm pure nothrow @nogc
   1282                 {
   1283                     naked;
   1284                     sub RSP, 32; // Shadow space (Win64 calling convention)
   1285                     call fiber_entryPoint;
   1286                     xor RCX, RCX; // This should never be reached, as
   1287                     jmp RCX;      // fiber_entryPoint must never return.
   1288                 }
   1289             }
   1290 
   1291             push( cast(size_t) &trampoline );                       // RIP
   1292             push( 0x00000000_00000000 );                            // RBP
   1293             push( 0x00000000_00000000 );                            // R12
   1294             push( 0x00000000_00000000 );                            // R13
   1295             push( 0x00000000_00000000 );                            // R14
   1296             push( 0x00000000_00000000 );                            // R15
   1297             push( 0x00000000_00000000 );                            // RDI
   1298             push( 0x00000000_00000000 );                            // RSI
   1299             push( 0x00000000_00000000 );                            // XMM6 (high)
   1300             push( 0x00000000_00000000 );                            // XMM6 (low)
   1301             push( 0x00000000_00000000 );                            // XMM7 (high)
   1302             push( 0x00000000_00000000 );                            // XMM7 (low)
   1303             push( 0x00000000_00000000 );                            // XMM8 (high)
   1304             push( 0x00000000_00000000 );                            // XMM8 (low)
   1305             push( 0x00000000_00000000 );                            // XMM9 (high)
   1306             push( 0x00000000_00000000 );                            // XMM9 (low)
   1307             push( 0x00000000_00000000 );                            // XMM10 (high)
   1308             push( 0x00000000_00000000 );                            // XMM10 (low)
   1309             push( 0x00000000_00000000 );                            // XMM11 (high)
   1310             push( 0x00000000_00000000 );                            // XMM11 (low)
   1311             push( 0x00000000_00000000 );                            // XMM12 (high)
   1312             push( 0x00000000_00000000 );                            // XMM12 (low)
   1313             push( 0x00000000_00000000 );                            // XMM13 (high)
   1314             push( 0x00000000_00000000 );                            // XMM13 (low)
   1315             push( 0x00000000_00000000 );                            // XMM14 (high)
   1316             push( 0x00000000_00000000 );                            // XMM14 (low)
   1317             push( 0x00000000_00000000 );                            // XMM15 (high)
   1318             push( 0x00000000_00000000 );                            // XMM15 (low)
   1319             push( 0x00000000_00000000 );                            // RBX
   1320             push( 0xFFFFFFFF_FFFFFFFF );                            // GS:[0]
   1321             version (StackGrowsDown)
   1322             {
   1323                 push( cast(size_t) m_ctxt.bstack );                 // GS:[8]
   1324                 push( cast(size_t) m_ctxt.bstack - m_size );        // GS:[16]
   1325             }
   1326             else
   1327             {
   1328                 push( cast(size_t) m_ctxt.bstack );                 // GS:[8]
   1329                 push( cast(size_t) m_ctxt.bstack + m_size );        // GS:[16]
   1330             }
   1331         }
   1332         else version (AsmX86_Posix)
   1333         {
   1334             push( 0x00000000 );                                     // Return address of fiber_entryPoint call
   1335             push( cast(size_t) &fiber_entryPoint );                 // EIP
   1336             push( cast(size_t) m_ctxt.bstack );                     // EBP
   1337             push( 0x00000000 );                                     // EDI
   1338             push( 0x00000000 );                                     // ESI
   1339             push( 0x00000000 );                                     // EBX
   1340             push( 0x00000000 );                                     // EAX
   1341         }
   1342         else version (AsmX86_64_Posix)
   1343         {
   1344             push( 0x00000000_00000000 );                            // Return address of fiber_entryPoint call
   1345             push( cast(size_t) &fiber_entryPoint );                 // RIP
   1346             push( cast(size_t) m_ctxt.bstack );                     // RBP
   1347             push( 0x00000000_00000000 );                            // RBX
   1348             push( 0x00000000_00000000 );                            // R12
   1349             push( 0x00000000_00000000 );                            // R13
   1350             push( 0x00000000_00000000 );                            // R14
   1351             push( 0x00000000_00000000 );                            // R15
   1352         }
   1353         else version (AsmPPC_Posix)
   1354         {
   1355             version (StackGrowsDown)
   1356             {
   1357                 pstack -= int.sizeof * 5;
   1358             }
   1359             else
   1360             {
   1361                 pstack += int.sizeof * 5;
   1362             }
   1363 
   1364             push( cast(size_t) &fiber_entryPoint );     // link register
   1365             push( 0x00000000 );                         // control register
   1366             push( 0x00000000 );                         // old stack pointer
   1367 
   1368             // GPR values
   1369             version (StackGrowsDown)
   1370             {
   1371                 pstack -= int.sizeof * 20;
   1372             }
   1373             else
   1374             {
   1375                 pstack += int.sizeof * 20;
   1376             }
   1377 
   1378             assert( (cast(size_t) pstack & 0x0f) == 0 );
   1379         }
   1380         else version (AsmPPC_Darwin)
   1381         {
   1382             version (StackGrowsDown) {}
   1383             else static assert(false, "PowerPC Darwin only supports decrementing stacks");
   1384 
   1385             uint wsize = size_t.sizeof;
   1386 
   1387             // linkage + regs + FPRs + VRs
   1388             uint space = 8 * wsize + 20 * wsize + 18 * 8 + 12 * 16;
   1389             (cast(ubyte*)pstack - space)[0 .. space] = 0;
   1390 
   1391             pstack -= wsize * 6;
   1392             *cast(size_t*)pstack = cast(size_t) &fiber_entryPoint; // LR
   1393             pstack -= wsize * 22;
   1394 
   1395             // On Darwin PPC64 pthread self is in R13 (which is reserved).
   1396             // At present, it is not safe to migrate fibers between threads, but if that
   1397             // changes, then updating the value of R13 will also need to be handled.
   1398             version (PPC64)
   1399               *cast(size_t*)(pstack + wsize) = cast(size_t) Thread.getThis().m_addr;
   1400             assert( (cast(size_t) pstack & 0x0f) == 0 );
   1401         }
   1402         else version (AsmMIPS_O32_Posix)
   1403         {
   1404             version (StackGrowsDown) {}
   1405             else static assert(0);
   1406 
   1407             /* We keep the FP registers and the return address below
   1408              * the stack pointer, so they don't get scanned by the
   1409              * GC. The last frame before swapping the stack pointer is
   1410              * organized like the following.
   1411              *
   1412              *     |-----------|<= frame pointer
   1413              *     |    $gp    |
   1414              *     |   $s0-8   |
   1415              *     |-----------|<= stack pointer
   1416              *     |    $ra    |
   1417              *     |  align(8) |
   1418              *     |  $f20-30  |
   1419              *     |-----------|
   1420              *
   1421              */
   1422             enum SZ_GP = 10 * size_t.sizeof; // $gp + $s0-8
   1423             enum SZ_RA = size_t.sizeof;      // $ra
   1424             version (MIPS_HardFloat)
   1425             {
   1426                 enum SZ_FP = 6 * 8;          // $f20-30
   1427                 enum ALIGN = -(SZ_FP + SZ_RA) & (8 - 1);
   1428             }
   1429             else
   1430             {
   1431                 enum SZ_FP = 0;
   1432                 enum ALIGN = 0;
   1433             }
   1434 
   1435             enum BELOW = SZ_FP + ALIGN + SZ_RA;
   1436             enum ABOVE = SZ_GP;
   1437             enum SZ = BELOW + ABOVE;
   1438 
   1439             (cast(ubyte*)pstack - SZ)[0 .. SZ] = 0;
   1440             pstack -= ABOVE;
   1441             *cast(size_t*)(pstack - SZ_RA) = cast(size_t)&fiber_entryPoint;
   1442         }
   1443         else version (AsmAArch64_Posix)
   1444         {
   1445             // Like others, FP registers and return address (lr) are kept
   1446             // below the saved stack top (tstack) to hide from GC scanning.
   1447             // fiber_switchContext expects newp sp to look like this:
   1448             //   19: x19
   1449             //   ...
   1450             //    9: x29 (fp)  <-- newp tstack
   1451             //    8: x30 (lr)  [&fiber_entryPoint]
   1452             //    7: d8
   1453             //   ...
   1454             //    0: d15
   1455 
   1456             version (StackGrowsDown) {}
   1457             else
   1458                 static assert(false, "Only full descending stacks supported on AArch64");
   1459 
   1460             // Only need to set return address (lr).  Everything else is fine
   1461             // zero initialized.
   1462             pstack -= size_t.sizeof * 11;    // skip past x19-x29
   1463             push(cast(size_t) &fiber_trampoline); // see threadasm.S for docs
   1464             pstack += size_t.sizeof;         // adjust sp (newp) above lr
   1465         }
   1466         else version (AsmARM_Posix)
   1467         {
   1468             /* We keep the FP registers and the return address below
   1469              * the stack pointer, so they don't get scanned by the
   1470              * GC. The last frame before swapping the stack pointer is
   1471              * organized like the following.
   1472              *
   1473              *   |  |-----------|<= 'frame starts here'
   1474              *   |  |     fp    | (the actual frame pointer, r11 isn't
   1475              *   |  |   r10-r4  |  updated and still points to the previous frame)
   1476              *   |  |-----------|<= stack pointer
   1477              *   |  |     lr    |
   1478              *   |  | 4byte pad |
   1479              *   |  |   d15-d8  |(if FP supported)
   1480              *   |  |-----------|
   1481              *   Y
   1482              *   stack grows down: The pointer value here is smaller than some lines above
   1483              */
   1484             // frame pointer can be zero, r10-r4 also zero initialized
   1485             version (StackGrowsDown)
   1486                 pstack -= int.sizeof * 8;
   1487             else
   1488                 static assert(false, "Only full descending stacks supported on ARM");
   1489 
   1490             // link register
   1491             push( cast(size_t) &fiber_entryPoint );
   1492             /*
   1493              * We do not push padding and d15-d8 as those are zero initialized anyway
   1494              * Position the stack pointer above the lr register
   1495              */
   1496             pstack += int.sizeof * 1;
   1497         }
   1498         else version (GNU_AsmX86_Windows)
   1499         {
   1500             version (StackGrowsDown) {} else static assert( false );
   1501 
   1502             // Currently, MinGW doesn't utilize SEH exceptions.
   1503             // See DMD AsmX86_Windows If this code ever becomes fails and SEH is used.
   1504 
   1505             push( 0x00000000 );                                     // Return address of fiber_entryPoint call
   1506             push( cast(size_t) &fiber_entryPoint );                 // EIP
   1507             push( 0x00000000 );                                     // EBP
   1508             push( 0x00000000 );                                     // EDI
   1509             push( 0x00000000 );                                     // ESI
   1510             push( 0x00000000 );                                     // EBX
   1511             push( 0xFFFFFFFF );                                     // FS:[0] - Current SEH frame
   1512             push( cast(size_t) m_ctxt.bstack );                     // FS:[4] - Top of stack
   1513             push( cast(size_t) m_ctxt.bstack - m_size );            // FS:[8] - Bottom of stack
   1514             push( 0x00000000 );                                     // EAX
   1515         }
   1516         else version (GNU_AsmX86_64_Windows)
   1517         {
   1518             push( 0x00000000_00000000 );                            // Return address of fiber_entryPoint call
   1519             push( cast(size_t) &fiber_entryPoint );                 // RIP
   1520             push( 0x00000000_00000000 );                            // RBP
   1521             push( 0x00000000_00000000 );                            // RBX
   1522             push( 0x00000000_00000000 );                            // R12
   1523             push( 0x00000000_00000000 );                            // R13
   1524             push( 0x00000000_00000000 );                            // R14
   1525             push( 0x00000000_00000000 );                            // R15
   1526             push( 0xFFFFFFFF_FFFFFFFF );                            // GS:[0] - Current SEH frame
   1527             version (StackGrowsDown)
   1528             {
   1529                 push( cast(size_t) m_ctxt.bstack );                 // GS:[8]  - Top of stack
   1530                 push( cast(size_t) m_ctxt.bstack - m_size );        // GS:[16] - Bottom of stack
   1531             }
   1532             else
   1533             {
   1534                 push( cast(size_t) m_ctxt.bstack );                 // GS:[8]  - Top of stack
   1535                 push( cast(size_t) m_ctxt.bstack + m_size );        // GS:[16] - Bottom of stack
   1536             }
   1537         }
   1538         else static if ( __traits( compiles, ucontext_t ) )
   1539         {
   1540             getcontext( &m_utxt );
   1541             m_utxt.uc_stack.ss_sp   = m_pmem;
   1542             m_utxt.uc_stack.ss_size = m_size;
   1543             makecontext( &m_utxt, &fiber_entryPoint, 0 );
   1544             // NOTE: If ucontext is being used then the top of the stack will
   1545             //       be a pointer to the ucontext_t struct for that fiber.
   1546             push( cast(size_t) &m_utxt );
   1547         }
   1548         else
   1549             static assert(0, "Not implemented");
   1550     }
   1551 
   1552 
   1553     StackContext*   m_ctxt;
   1554     size_t          m_size;
   1555     void*           m_pmem;
   1556 
   1557     static if ( __traits( compiles, ucontext_t ) )
   1558     {
   1559         // NOTE: The static ucontext instance is used to represent the context
   1560         //       of the executing thread.
   1561         static ucontext_t       sm_utxt = void;
   1562         ucontext_t              m_utxt  = void;
   1563         ucontext_t*             m_ucur  = null;
   1564     }
   1565     else static if (GNU_Enable_CET)
   1566     {
   1567         // When libphobos was built with --enable-cet, these fields need to
   1568         // always be present in the Fiber class layout.
   1569         import core.sys.posix.ucontext;
   1570         static ucontext_t       sm_utxt = void;
   1571         ucontext_t              m_utxt  = void;
   1572         ucontext_t*             m_ucur  = null;
   1573     }
   1574 
   1575 
   1576 private:
   1577     ///////////////////////////////////////////////////////////////////////////
   1578     // Storage of Active Fiber
   1579     ///////////////////////////////////////////////////////////////////////////
   1580 
   1581 
   1582     //
   1583     // Sets a thread-local reference to the current fiber object.
   1584     //
   1585     static void setThis( Fiber f ) nothrow @nogc
   1586     {
   1587         sm_this = f;
   1588     }
   1589 
   1590     static Fiber sm_this;
   1591 
   1592 
   1593 private:
   1594     ///////////////////////////////////////////////////////////////////////////
   1595     // Context Switching
   1596     ///////////////////////////////////////////////////////////////////////////
   1597 
   1598 
   1599     //
   1600     // Switches into the stack held by this fiber.
   1601     //
   1602     final void switchIn() nothrow @nogc
   1603     {
   1604         Thread  tobj = Thread.getThis();
   1605         void**  oldp = &tobj.m_curr.tstack;
   1606         void*   newp = m_ctxt.tstack;
   1607 
   1608         // NOTE: The order of operations here is very important.  The current
   1609         //       stack top must be stored before m_lock is set, and pushContext
   1610         //       must not be called until after m_lock is set.  This process
   1611         //       is intended to prevent a race condition with the suspend
   1612         //       mechanism used for garbage collection.  If it is not followed,
   1613         //       a badly timed collection could cause the GC to scan from the
   1614         //       bottom of one stack to the top of another, or to miss scanning
   1615         //       a stack that still contains valid data.  The old stack pointer
   1616         //       oldp will be set again before the context switch to guarantee
   1617         //       that it points to exactly the correct stack location so the
   1618         //       successive pop operations will succeed.
   1619         *oldp = getStackTop();
   1620         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
   1621         tobj.pushContext( m_ctxt );
   1622 
   1623         fiber_switchContext( oldp, newp );
   1624 
   1625         // NOTE: As above, these operations must be performed in a strict order
   1626         //       to prevent Bad Things from happening.
   1627         tobj.popContext();
   1628         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
   1629         tobj.m_curr.tstack = tobj.m_curr.bstack;
   1630     }
   1631 
   1632 
   1633     //
   1634     // Switches out of the current stack and into the enclosing stack.
   1635     //
   1636     final void switchOut() nothrow @nogc
   1637     {
   1638         Thread  tobj = Thread.getThis();
   1639         void**  oldp = &m_ctxt.tstack;
   1640         void*   newp = tobj.m_curr.within.tstack;
   1641 
   1642         // NOTE: The order of operations here is very important.  The current
   1643         //       stack top must be stored before m_lock is set, and pushContext
   1644         //       must not be called until after m_lock is set.  This process
   1645         //       is intended to prevent a race condition with the suspend
   1646         //       mechanism used for garbage collection.  If it is not followed,
   1647         //       a badly timed collection could cause the GC to scan from the
   1648         //       bottom of one stack to the top of another, or to miss scanning
   1649         //       a stack that still contains valid data.  The old stack pointer
   1650         //       oldp will be set again before the context switch to guarantee
   1651         //       that it points to exactly the correct stack location so the
   1652         //       successive pop operations will succeed.
   1653         *oldp = getStackTop();
   1654         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, true);
   1655 
   1656         fiber_switchContext( oldp, newp );
   1657 
   1658         // NOTE: As above, these operations must be performed in a strict order
   1659         //       to prevent Bad Things from happening.
   1660         // NOTE: If use of this fiber is multiplexed across threads, the thread
   1661         //       executing here may be different from the one above, so get the
   1662         //       current thread handle before unlocking, etc.
   1663         tobj = Thread.getThis();
   1664         atomicStore!(MemoryOrder.raw)(*cast(shared)&tobj.m_lock, false);
   1665         tobj.m_curr.tstack = tobj.m_curr.bstack;
   1666     }
   1667 }
   1668 
   1669 ///
   1670 unittest {
   1671     int counter;
   1672 
   1673     class DerivedFiber : Fiber
   1674     {
   1675         this()
   1676         {
   1677             super( &run );
   1678         }
   1679 
   1680     private :
   1681         void run()
   1682         {
   1683             counter += 2;
   1684         }
   1685     }
   1686 
   1687     void fiberFunc()
   1688     {
   1689         counter += 4;
   1690         Fiber.yield();
   1691         counter += 8;
   1692     }
   1693 
   1694     // create instances of each type
   1695     Fiber derived = new DerivedFiber();
   1696     Fiber composed = new Fiber( &fiberFunc );
   1697 
   1698     assert( counter == 0 );
   1699 
   1700     derived.call();
   1701     assert( counter == 2, "Derived fiber increment." );
   1702 
   1703     composed.call();
   1704     assert( counter == 6, "First composed fiber increment." );
   1705 
   1706     counter += 16;
   1707     assert( counter == 22, "Calling context increment." );
   1708 
   1709     composed.call();
   1710     assert( counter == 30, "Second composed fiber increment." );
   1711 
   1712     // since each fiber has run to completion, each should have state TERM
   1713     assert( derived.state == Fiber.State.TERM );
   1714     assert( composed.state == Fiber.State.TERM );
   1715 }
   1716 
   1717 version (CoreUnittest)
   1718 {
   1719     class TestFiber : Fiber
   1720     {
   1721         this()
   1722         {
   1723             super(&run);
   1724         }
   1725 
   1726         void run()
   1727         {
   1728             foreach (i; 0 .. 1000)
   1729             {
   1730                 sum += i;
   1731                 Fiber.yield();
   1732             }
   1733         }
   1734 
   1735         enum expSum = 1000 * 999 / 2;
   1736         size_t sum;
   1737     }
   1738 
   1739     void runTen()
   1740     {
   1741         TestFiber[10] fibs;
   1742         foreach (ref fib; fibs)
   1743             fib = new TestFiber();
   1744 
   1745         bool cont;
   1746         do {
   1747             cont = false;
   1748             foreach (fib; fibs) {
   1749                 if (fib.state == Fiber.State.HOLD)
   1750                 {
   1751                     fib.call();
   1752                     cont |= fib.state != Fiber.State.TERM;
   1753                 }
   1754             }
   1755         } while (cont);
   1756 
   1757         foreach (fib; fibs)
   1758         {
   1759             assert(fib.sum == TestFiber.expSum);
   1760         }
   1761     }
   1762 }
   1763 
   1764 
   1765 // Single thread running separate fibers
   1766 unittest
   1767 {
   1768     runTen();
   1769 }
   1770 
   1771 
   1772 // Multiple threads running separate fibers
   1773 unittest
   1774 {
   1775     auto group = new ThreadGroup();
   1776     foreach (_; 0 .. 4)
   1777     {
   1778         group.create(&runTen);
   1779     }
   1780     group.joinAll();
   1781 }
   1782 
   1783 
   1784 // Multiple threads running shared fibers
   1785 version (PPC)   version = UnsafeFiberMigration;
   1786 version (PPC64) version = UnsafeFiberMigration;
   1787 version (OSX)
   1788 {
   1789     version (X86)    version = UnsafeFiberMigration;
   1790     version (X86_64) version = UnsafeFiberMigration;
   1791 }
   1792 
   1793 version (UnsafeFiberMigration)
   1794 {
   1795     // XBUG: core.thread fibers are supposed to be safe to migrate across
   1796     // threads, however, there is a problem: GCC always assumes that the
   1797     // address of thread-local variables don't change while on a given stack.
   1798     // In consequence, migrating fibers between threads currently is an unsafe
   1799     // thing to do, and will break on some targets (possibly PR26461).
   1800 }
   1801 else
   1802 {
   1803     version = FiberMigrationUnittest;
   1804 }
   1805 
   1806 version (FiberMigrationUnittest)
   1807 unittest
   1808 {
   1809     shared bool[10] locks;
   1810     TestFiber[10] fibs;
   1811 
   1812     void runShared()
   1813     {
   1814         bool cont;
   1815         do {
   1816             cont = false;
   1817             foreach (idx; 0 .. 10)
   1818             {
   1819                 if (cas(&locks[idx], false, true))
   1820                 {
   1821                     if (fibs[idx].state == Fiber.State.HOLD)
   1822                     {
   1823                         fibs[idx].call();
   1824                         cont |= fibs[idx].state != Fiber.State.TERM;
   1825                     }
   1826                     locks[idx] = false;
   1827                 }
   1828                 else
   1829                 {
   1830                     cont = true;
   1831                 }
   1832             }
   1833         } while (cont);
   1834     }
   1835 
   1836     foreach (ref fib; fibs)
   1837     {
   1838         fib = new TestFiber();
   1839     }
   1840 
   1841     auto group = new ThreadGroup();
   1842     foreach (_; 0 .. 4)
   1843     {
   1844         group.create(&runShared);
   1845     }
   1846     group.joinAll();
   1847 
   1848     foreach (fib; fibs)
   1849     {
   1850         assert(fib.sum == TestFiber.expSum);
   1851     }
   1852 }
   1853 
   1854 
   1855 // Test exception handling inside fibers.
   1856 unittest
   1857 {
   1858     enum MSG = "Test message.";
   1859     string caughtMsg;
   1860     (new Fiber({
   1861         try
   1862         {
   1863             throw new Exception(MSG);
   1864         }
   1865         catch (Exception e)
   1866         {
   1867             caughtMsg = e.msg;
   1868         }
   1869     })).call();
   1870     assert(caughtMsg == MSG);
   1871 }
   1872 
   1873 
   1874 unittest
   1875 {
   1876     int x = 0;
   1877 
   1878     (new Fiber({
   1879         x++;
   1880     })).call();
   1881     assert( x == 1 );
   1882 }
   1883 
   1884 nothrow unittest
   1885 {
   1886     new Fiber({}).call!(Fiber.Rethrow.no)();
   1887 }
   1888 
   1889 unittest
   1890 {
   1891     new Fiber({}).call(Fiber.Rethrow.yes);
   1892     new Fiber({}).call(Fiber.Rethrow.no);
   1893 }
   1894 
   1895 unittest
   1896 {
   1897     enum MSG = "Test message.";
   1898 
   1899     try
   1900     {
   1901         (new Fiber(function() {
   1902             throw new Exception( MSG );
   1903         })).call();
   1904         assert( false, "Expected rethrown exception." );
   1905     }
   1906     catch ( Throwable t )
   1907     {
   1908         assert( t.msg == MSG );
   1909     }
   1910 }
   1911 
   1912 // Test exception chaining when switching contexts in finally blocks.
   1913 unittest
   1914 {
   1915     static void throwAndYield(string msg) {
   1916       try {
   1917         throw new Exception(msg);
   1918       } finally {
   1919         Fiber.yield();
   1920       }
   1921     }
   1922 
   1923     static void fiber(string name) {
   1924       try {
   1925         try {
   1926           throwAndYield(name ~ ".1");
   1927         } finally {
   1928           throwAndYield(name ~ ".2");
   1929         }
   1930       } catch (Exception e) {
   1931         assert(e.msg == name ~ ".1");
   1932         assert(e.next);
   1933         assert(e.next.msg == name ~ ".2");
   1934         assert(!e.next.next);
   1935       }
   1936     }
   1937 
   1938     auto first = new Fiber(() => fiber("first"));
   1939     auto second = new Fiber(() => fiber("second"));
   1940     first.call();
   1941     second.call();
   1942     first.call();
   1943     second.call();
   1944     first.call();
   1945     second.call();
   1946     assert(first.state == Fiber.State.TERM);
   1947     assert(second.state == Fiber.State.TERM);
   1948 }
   1949 
   1950 // Test Fiber resetting
   1951 unittest
   1952 {
   1953     static string method;
   1954 
   1955     static void foo()
   1956     {
   1957         method = "foo";
   1958     }
   1959 
   1960     void bar()
   1961     {
   1962         method = "bar";
   1963     }
   1964 
   1965     static void expect(Fiber fib, string s)
   1966     {
   1967         assert(fib.state == Fiber.State.HOLD);
   1968         fib.call();
   1969         assert(fib.state == Fiber.State.TERM);
   1970         assert(method == s); method = null;
   1971     }
   1972     auto fib = new Fiber(&foo);
   1973     expect(fib, "foo");
   1974 
   1975     fib.reset();
   1976     expect(fib, "foo");
   1977 
   1978     fib.reset(&foo);
   1979     expect(fib, "foo");
   1980 
   1981     fib.reset(&bar);
   1982     expect(fib, "bar");
   1983 
   1984     fib.reset(function void(){method = "function";});
   1985     expect(fib, "function");
   1986 
   1987     fib.reset(delegate void(){method = "delegate";});
   1988     expect(fib, "delegate");
   1989 }
   1990 
   1991 // Test unsafe reset in hold state
   1992 unittest
   1993 {
   1994     auto fib = new Fiber(function {ubyte[2048] buf = void; Fiber.yield();}, 4096);
   1995     foreach (_; 0 .. 10)
   1996     {
   1997         fib.call();
   1998         assert(fib.state == Fiber.State.HOLD);
   1999         fib.reset();
   2000     }
   2001 }
   2002 
   2003 // stress testing GC stack scanning
   2004 unittest
   2005 {
   2006     import core.memory;
   2007     import core.time : dur;
   2008 
   2009     static void unreferencedThreadObject()
   2010     {
   2011         static void sleep() { Thread.sleep(dur!"msecs"(100)); }
   2012         auto thread = new Thread(&sleep).start();
   2013     }
   2014     unreferencedThreadObject();
   2015     GC.collect();
   2016 
   2017     static class Foo
   2018     {
   2019         this(int value)
   2020         {
   2021             _value = value;
   2022         }
   2023 
   2024         int bar()
   2025         {
   2026             return _value;
   2027         }
   2028 
   2029         int _value;
   2030     }
   2031 
   2032     static void collect()
   2033     {
   2034         auto foo = new Foo(2);
   2035         assert(foo.bar() == 2);
   2036         GC.collect();
   2037         Fiber.yield();
   2038         GC.collect();
   2039         assert(foo.bar() == 2);
   2040     }
   2041 
   2042     auto fiber = new Fiber(&collect);
   2043 
   2044     fiber.call();
   2045     GC.collect();
   2046     fiber.call();
   2047 
   2048     // thread reference
   2049     auto foo = new Foo(2);
   2050 
   2051     void collect2()
   2052     {
   2053         assert(foo.bar() == 2);
   2054         GC.collect();
   2055         Fiber.yield();
   2056         GC.collect();
   2057         assert(foo.bar() == 2);
   2058     }
   2059 
   2060     fiber = new Fiber(&collect2);
   2061 
   2062     fiber.call();
   2063     GC.collect();
   2064     fiber.call();
   2065 
   2066     static void recurse(size_t cnt)
   2067     {
   2068         --cnt;
   2069         Fiber.yield();
   2070         if (cnt)
   2071         {
   2072             auto fib = new Fiber(() { recurse(cnt); });
   2073             fib.call();
   2074             GC.collect();
   2075             fib.call();
   2076         }
   2077     }
   2078     fiber = new Fiber(() { recurse(20); });
   2079     fiber.call();
   2080 }
   2081 
   2082 
   2083 version (AsmX86_64_Windows)
   2084 {
   2085     // Test Windows x64 calling convention
   2086     unittest
   2087     {
   2088         void testNonvolatileRegister(alias REG)()
   2089         {
   2090             auto zeroRegister = new Fiber(() {
   2091                 mixin("asm pure nothrow @nogc { naked; xor "~REG~", "~REG~"; ret; }");
   2092             });
   2093             long after;
   2094 
   2095             mixin("asm pure nothrow @nogc { mov "~REG~", 0xFFFFFFFFFFFFFFFF; }");
   2096             zeroRegister.call();
   2097             mixin("asm pure nothrow @nogc { mov after, "~REG~"; }");
   2098 
   2099             assert(after == -1);
   2100         }
   2101 
   2102         void testNonvolatileRegisterSSE(alias REG)()
   2103         {
   2104             auto zeroRegister = new Fiber(() {
   2105                 mixin("asm pure nothrow @nogc { naked; xorpd "~REG~", "~REG~"; ret; }");
   2106             });
   2107             long[2] before = [0xFFFFFFFF_FFFFFFFF, 0xFFFFFFFF_FFFFFFFF], after;
   2108 
   2109             mixin("asm pure nothrow @nogc { movdqu "~REG~", before; }");
   2110             zeroRegister.call();
   2111             mixin("asm pure nothrow @nogc { movdqu after, "~REG~"; }");
   2112 
   2113             assert(before == after);
   2114         }
   2115 
   2116         testNonvolatileRegister!("R12")();
   2117         testNonvolatileRegister!("R13")();
   2118         testNonvolatileRegister!("R14")();
   2119         testNonvolatileRegister!("R15")();
   2120         testNonvolatileRegister!("RDI")();
   2121         testNonvolatileRegister!("RSI")();
   2122         testNonvolatileRegister!("RBX")();
   2123 
   2124         testNonvolatileRegisterSSE!("XMM6")();
   2125         testNonvolatileRegisterSSE!("XMM7")();
   2126         testNonvolatileRegisterSSE!("XMM8")();
   2127         testNonvolatileRegisterSSE!("XMM9")();
   2128         testNonvolatileRegisterSSE!("XMM10")();
   2129         testNonvolatileRegisterSSE!("XMM11")();
   2130         testNonvolatileRegisterSSE!("XMM12")();
   2131         testNonvolatileRegisterSSE!("XMM13")();
   2132         testNonvolatileRegisterSSE!("XMM14")();
   2133         testNonvolatileRegisterSSE!("XMM15")();
   2134     }
   2135 }
   2136 
   2137 
   2138 version (D_InlineAsm_X86_64)
   2139 {
   2140     unittest
   2141     {
   2142         void testStackAlignment()
   2143         {
   2144             void* pRSP;
   2145             asm pure nothrow @nogc
   2146             {
   2147                 mov pRSP, RSP;
   2148             }
   2149             assert((cast(size_t)pRSP & 0xF) == 0);
   2150         }
   2151 
   2152         auto fib = new Fiber(&testStackAlignment);
   2153         fib.call();
   2154     }
   2155 }
   2156