Wolfgang Thallergcc16 merge934c3885 months agoHistory
1433 lines · 53.2 KB · D
RawDownload
/** * The fiber module provides lightweight threads aka fibers. * * Copyright: Copyright Sean Kelly 2005 - 2012. * License: Distributed under the *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). *    (See accompanying file LICENSE) * Authors:   Sean Kelly, Walter Bright, Alex Rønne Petersen, Martin Nowak * Source:    $(DRUNTIMESRC core/thread/fiber/package.d) */ /* NOTE: This file has been patched from the original DMD distribution to * work with the GDC compiler. */module core.thread.fiber; import core.thread.context;import core.thread.fiber.base : fiber_entryPoint, FiberBase;import core.thread.threadbase;import core.thread.threadgroup;import core.thread.types; import core.memory : pageSize; ///////////////////////////////////////////////////////////////////////////////// Fiber Platform Detection/////////////////////////////////////////////////////////////////////////////// version (GNU){    import gcc.builtins;    version (GNU_StackGrowsDown)        version = StackGrowsDown;}else{    // this should be true for most architectures    version = StackGrowsDown;} version (Windows){    import core.stdc.stdlib : free, malloc;    import core.sys.windows.winbase;    import core.sys.windows.winnt;} package{    version (D_InlineAsm_X86)    {        version (Windows)            version = AsmX86_Windows;        else version (Posix)            version = AsmX86_Posix;         version = AlignFiberStackTo16Byte;    }    else version (D_InlineAsm_X86_64)    {        version (Windows)        {            version = AsmX86_64_Windows;            version = AlignFiberStackTo16Byte;        }        else version (Posix)        {            version = AsmX86_64_Posix;            version = AlignFiberStackTo16Byte;        }    }    else version (X86)    {        version = AlignFiberStackTo16Byte;         version (GNU_CET)        {            // fiber_switchContext does not support shadow stack from            // Intel CET.  So use ucontext implementation.        }        else        {            version = AsmExternal;             version (MinGW)                version = GNU_AsmX86_Windows;            else version (OSX)                version = AsmX86_Posix;            else version (Posix)                version = AsmX86_Posix;        }    }    else version (X86_64)    {        version = AlignFiberStackTo16Byte;         version (GNU_CET)        {            // fiber_switchContext does not support shadow stack from            // Intel CET.  So use ucontext implementation.        }        else version (D_X32)        {            // let X32 be handled by ucontext swapcontext        }        else        {            version = AsmExternal;             version (MinGW)                version = GNU_AsmX86_64_Windows;            else version (OSX)                version = AsmX86_64_Posix;            else version (Posix)                version = AsmX86_64_Posix;        }    }    else version (PPC)    {        version (OSX)        {            version = AsmPPC_Darwin;            version = AsmExternal;            version = AlignFiberStackTo16Byte;        }        else version (Posix)        {            version = AsmPPC_Posix;            version = AsmExternal;        }    }    else version (PPC64)    {        version (OSX)        {            version = AsmPPC_Darwin;            version = AsmExternal;            version = AlignFiberStackTo16Byte;        }        else version (Posix)        {            version = AlignFiberStackTo16Byte;        }    }    else version (MIPS_O32)    {        version (Posix)        {            version = AsmMIPS_O32_Posix;            version = AsmExternal;        }    }    else version (MIPS_N64)    {        version (Posix)        {            version = AsmMIPS_N64_Posix;            version = AsmExternal;        }    }    else version (AArch64)    {        version (Posix)        {            version = AsmAArch64_Posix;            version = AsmExternal;            version = AlignFiberStackTo16Byte;        }    }    else version (ARM)    {        version (Posix)        {            version = AsmARM_Posix;            version = AsmExternal;        }    }    else version (SPARC)    {        // NOTE: The SPARC ABI specifies only doubleword alignment.        version = AlignFiberStackTo16Byte;    }    else version (SPARC64)    {        version = AlignFiberStackTo16Byte;    }    else version (LoongArch64)    {        version (Posix)        {            version = AsmLoongArch64_Posix;            version = AsmExternal;            version = AlignFiberStackTo16Byte;        }    }     version (Posix)    {        version (AsmX86_Windows)    {} else        version (AsmX86_Posix)      {} else        version (AsmX86_64_Windows) {} else        version (AsmX86_64_Posix)   {} else        version (AsmExternal)       {} else        {            // NOTE: The ucontext implementation requires architecture specific            //       data definitions to operate so testing for it must be done            //       by checking for the existence of ucontext_t rather than by            //       a version identifier.  Please note that this is considered            //       an obsolescent feature according to the POSIX spec, so a            //       custom solution is still preferred.            import core.sys.posix.ucontext : getcontext, makecontext, MINSIGSTKSZ, swapcontext, ucontext_t;        }    }} ///////////////////////////////////////////////////////////////////////////////// Fiber Entry Point and Context Switch/////////////////////////////////////////////////////////////////////////////// package{    import core.atomic : atomicStore, cas, MemoryOrder;    import core.exception : onOutOfMemoryError;    import core.stdc.stdlib : abort;     // Look above the definition of 'class Fiber' for some information about the implementation of this routine    version (AsmExternal)    {        extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc;        version (AArch64)            extern (C) void fiber_trampoline() nothrow;    }    else    extern (C) void fiber_switchContext( void** oldp, void* newp ) nothrow @nogc    {        // NOTE: The data pushed and popped in this routine must match the        //       default stack created by Fiber.initStack or the initial        //       switch into a new context will fail.         version (AsmX86_Windows)        {            asm pure nothrow @nogc            {                naked;                 // save current stack state                push EBP;                mov  EBP, ESP;                push EDI;                push ESI;                push EBX;                push dword ptr FS:[0];                push dword ptr FS:[4];                push dword ptr FS:[8];                push EAX;                 // store oldp again with more accurate address                mov EAX, dword ptr 8[EBP];                mov [EAX], ESP;                // load newp to begin context switch                mov ESP, dword ptr 12[EBP];                 // load saved state from new stack                pop EAX;                pop dword ptr FS:[8];                pop dword ptr FS:[4];                pop dword ptr FS:[0];                pop EBX;                pop ESI;                pop EDI;                pop EBP;                 // 'return' to complete switch                pop ECX;                jmp ECX;            }        }        else version (AsmX86_64_Windows)        {            asm pure nothrow @nogc            {                naked;                 // save current stack state                // NOTE: When changing the layout of registers on the stack,                //       make sure that the XMM registers are still aligned.                //       On function entry, the stack is guaranteed to not                //       be aligned to 16 bytes because of the return address                //       on the stack.                push RBP;                mov  RBP, RSP;                push R12;                push R13;                push R14;                push R15;                push RDI;                push RSI;                // 7 registers = 56 bytes; stack is now aligned to 16 bytes                sub RSP, 160;                movdqa [RSP + 144], XMM6;                movdqa [RSP + 128], XMM7;                movdqa [RSP + 112], XMM8;                movdqa [RSP + 96], XMM9;                movdqa [RSP + 80], XMM10;                movdqa [RSP + 64], XMM11;                movdqa [RSP + 48], XMM12;                movdqa [RSP + 32], XMM13;                movdqa [RSP + 16], XMM14;                movdqa [RSP], XMM15;                push RBX;                xor  RAX,RAX;                push qword ptr GS:[RAX];                push qword ptr GS:8[RAX];                push qword ptr GS:16[RAX];                 // store oldp                mov [RCX], RSP;                // load newp to begin context switch                mov RSP, RDX;                 // load saved state from new stack                pop qword ptr GS:16[RAX];                pop qword ptr GS:8[RAX];                pop qword ptr GS:[RAX];                pop RBX;                movdqa XMM15, [RSP];                movdqa XMM14, [RSP + 16];                movdqa XMM13, [RSP + 32];                movdqa XMM12, [RSP + 48];                movdqa XMM11, [RSP + 64];                movdqa XMM10, [RSP + 80];                movdqa XMM9, [RSP + 96];                movdqa XMM8, [RSP + 112];                movdqa XMM7, [RSP + 128];                movdqa XMM6, [RSP + 144];                add RSP, 160;                pop RSI;                pop RDI;                pop R15;                pop R14;                pop R13;                pop R12;                pop RBP;                 // 'return' to complete switch                pop RCX;                jmp RCX;            }        }        else version (AsmX86_Posix)        {            asm pure nothrow @nogc            {                naked;                 // save current stack state                push EBP;                mov  EBP, ESP;                push EDI;                push ESI;                push EBX;                push EAX;                 // store oldp again with more accurate address                mov EAX, dword ptr 8[EBP];                mov [EAX], ESP;                // load newp to begin context switch                mov ESP, dword ptr 12[EBP];                 // load saved state from new stack                pop EAX;                pop EBX;                pop ESI;                pop EDI;                pop EBP;                 // 'return' to complete switch                pop ECX;                jmp ECX;            }        }        else version (AsmX86_64_Posix)        {            asm pure nothrow @nogc            {                naked;                 // save current stack state                push RBP;                mov  RBP, RSP;                push RBX;                push R12;                push R13;                push R14;                push R15;                 // store oldp                mov [RDI], RSP;                // load newp to begin context switch                mov RSP, RSI;                 // load saved state from new stack                pop R15;                pop R14;                pop R13;                pop R12;                pop RBX;                pop RBP;                 // 'return' to complete switch                pop RCX;                jmp RCX;            }        }        else static if ( __traits( compiles, ucontext_t ) )        {            Fiber   cfib = Fiber.getThis();            void*   ucur = cfib.m_ucur;             *oldp = &ucur;            swapcontext( **(cast(ucontext_t***) oldp),                          *(cast(ucontext_t**)  newp) );        }        else            static assert(0, "Not implemented");    }}  ///////////////////////////////////////////////////////////////////////////////// Fiber////////////////////////////////////////////////////////////////////////////////* * Documentation of Fiber internals: * * The main routines to implement when porting Fibers to new architectures are * fiber_switchContext and initStack. Some version constants have to be defined * for the new platform as well, search for "Fiber Platform Detection and Memory Allocation". * * Fibers are based on a concept called 'Context'. A Context describes the execution * state of a Fiber or main thread which is fully described by the stack, some * registers and a return address at which the Fiber/Thread should continue executing. * Please note that not only each Fiber has a Context, but each thread also has got a * Context which describes the threads stack and state. If you call Fiber fib; fib.call * the first time in a thread you switch from Threads Context into the Fibers Context. * If you call fib.yield in that Fiber you switch out of the Fibers context and back * into the Thread Context. (However, this is not always the case. You can call a Fiber * from within another Fiber, then you switch Contexts between the Fibers and the Thread * Context is not involved) * * In all current implementations the registers and the return address are actually * saved on a Contexts stack. * * The fiber_switchContext routine has got two parameters: * void** a:  This is the _location_ where we have to store the current stack pointer, *            the stack pointer of the currently executing Context (Fiber or Thread). * void*  b:  This is the pointer to the stack of the Context which we want to switch into. *            Note that we get the same pointer here as the one we stored into the void** a *            in a previous call to fiber_switchContext. * * In the simplest case, a fiber_switchContext rountine looks like this: * fiber_switchContext: *     push {return Address} *     push {registers} *     copy {stack pointer} into {location pointed to by a} *     //We have now switch to the stack of a different Context! *     copy {b} into {stack pointer} *     pop {registers} *     pop {return Address} *     jump to {return Address} * * The GC uses the value returned in parameter a to scan the Fibers stack. It scans from * the stack base to that value. As the GC dislikes false pointers we can actually optimize * this a little: By storing registers which can not contain references to memory managed * by the GC outside of the region marked by the stack base pointer and the stack pointer * saved in fiber_switchContext we can prevent the GC from scanning them. * Such registers are usually floating point registers and the return address. In order to * implement this, we return a modified stack pointer from fiber_switchContext. However, * we have to remember that when we restore the registers from the stack! * * --------------------------- <= Stack Base * |          Frame          | <= Many other stack frames * |          Frame          | * |-------------------------| <= The last stack frame. This one is created by fiber_switchContext * | registers with pointers | * |                         | <= Stack pointer. GC stops scanning here * |   return address        | * |floating point registers | * --------------------------- <= Real Stack End * * fiber_switchContext: *     push {registers with pointers} *     copy {stack pointer} into {location pointed to by a} *     push {return Address} *     push {Floating point registers} *     //We have now switch to the stack of a different Context! *     copy {b} into {stack pointer} *     //We now have to adjust the stack pointer to point to 'Real Stack End' so we can pop *     //the FP registers *     //+ or - depends on if your stack grows downwards or upwards

Lines 1–500 of 1433.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t