diff --git a/src/core/thread.d b/src/core/thread.d index 388c1393b49..8a859b2b331 100644 --- a/src/core/thread.d +++ b/src/core/thread.d @@ -2790,16 +2790,20 @@ private { version( D_InlineAsm_X86 ) { - version( X86_64 ) - { + version( Windows ) + version = AsmX86_Win32; + else version( Posix ) + version = AsmX86_Posix; - } - else + version( OSX ) + version = AlignFiberStackTo16Byte; + } + else version( D_InlineAsm_X86_64 ) + { + version( Posix ) { - version( Windows ) - version = AsmX86_Win32; - else version( Posix ) - version = AsmX86_Posix; + version = AsmX86_64_Posix; + version = AlignFiberStackTo16Byte; } } else version( PPC ) @@ -2817,6 +2821,7 @@ private version( AsmX86_Win32 ) {} else version( AsmX86_Posix ) {} else version( AsmPPC_Posix ) {} else + version( AsmX86_64_Posix ) {} else { // NOTE: The ucontext implementation requires architecture specific // data definitions to operate so testing for it must be done @@ -2968,7 +2973,39 @@ private pop EBP; // 'return' to complete switch - ret; + pop ECX; + jmp ECX; + } + } + else version( AsmX86_64_Posix ) + { + asm + { + naked; + // save current stack state + push RBX; + push RBP; + 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 RBP; + pop RBX; + + // 'return' to complete switch + pop RCX; + jmp RCX; } } else static if( __traits( compiles, ucontext_t ) ) @@ -3310,18 +3347,11 @@ class Fiber * * Returns: * The fiber object representing the calling fiber or null if no fiber - * is currently active. The result of deleting this object is undefined. + * is currently active within this thread. The result of deleting this object is undefined. */ static Fiber getThis() { - version( Windows ) - { - return cast(Fiber) TlsGetValue( sm_this ); - } - else version( Posix ) - { - return cast(Fiber) pthread_getspecific( sm_this ); - } + return sm_this; } @@ -3330,29 +3360,18 @@ class Fiber /////////////////////////////////////////////////////////////////////////// - shared static this() + version( Posix ) { - version( Windows ) - { - sm_this = TlsAlloc(); - assert( sm_this != TLS_OUT_OF_INDEXES ); - } - else version( Posix ) + static this() { - int status; - - status = pthread_key_create( &sm_this, null ); - assert( status == 0 ); - - static if( __traits( compiles, ucontext_t ) ) - { - status = getcontext( &sm_utxt ); - assert( status == 0 ); - } + static if( __traits( compiles, ucontext_t ) ) + { + int status = getcontext( &sm_utxt ); + assert( status == 0 ); + } } } - private: // // Initializes a fiber object which has no associated executable function. @@ -3600,9 +3619,10 @@ private: } } - // NOTE: On OS X the stack must be 16-byte aligned according to the - // IA-32 call spec. - version( OSX ) + // NOTE: On OS X the stack must be 16-byte aligned according + // to the IA-32 call spec. For x86_64 the stack also needs to + // be aligned to 16-byte according to SysV AMD64 ABI. + version( AlignFiberStackTo16Byte ) { version( StackGrowsDown ) { @@ -3636,7 +3656,7 @@ private: } else version( AsmX86_Posix ) { - push( 0x00000000 ); // Pad stack for OSX + push( 0x00000000 ); // Return address of fiber_entryPoint call push( cast(size_t) &fiber_entryPoint ); // EIP push( 0x00000000 ); // EBP push( 0x00000000 ); // EAX @@ -3644,6 +3664,17 @@ private: push( 0x00000000 ); // ESI push( 0x00000000 ); // EDI } + else version( AsmX86_64_Posix ) + { + push(0); // Return address of fiber_entryPoint call + push( cast(size_t) &fiber_entryPoint ); // RIP + push(0); // RBX + push(0); // RBP + push(0); // R12 + push(0); // R13 + push(0); // R14 + push(0); // R15 + } else version( AsmPPC_Posix ) { version( StackGrowsDown ) @@ -3691,8 +3722,8 @@ private: static if( __traits( compiles, ucontext_t ) ) { // NOTE: The static ucontext instance is used to represent the context - // of the main application thread. - __gshared ucontext_t sm_utxt = void; + // of the executing thread. + static ucontext_t sm_utxt = void; ucontext_t m_utxt = void; ucontext_t* m_ucur = null; } @@ -3709,18 +3740,10 @@ private: // static void setThis( Fiber f ) { - version( Windows ) - { - TlsSetValue( sm_this, cast(void*) f ); - } - else version( Posix ) - { - pthread_setspecific( sm_this, cast(void*) f ); - } + sm_this = f; } - - __gshared Thread.TLSKey sm_this; + static Fiber sm_this; private: @@ -3799,6 +3822,139 @@ private: } } +version( unittest ) +{ + import core.atomic; + + class TestFiber : Fiber + { + this() + { + super(&run); + } + + void run() + { + foreach(i; 0 .. 1000) + { + sum += i; + Fiber.yield(); + } + } + + enum expSum = 1000 * 999 / 2; + size_t sum; + } + + void runTen() + { + TestFiber[10] fibs; + foreach(ref fib; fibs) + fib = new TestFiber(); + + bool cont; + do { + cont = false; + foreach(fib; fibs) { + if (fib.state == Fiber.State.HOLD) + { + fib.call(); + cont |= fib.state != Fiber.State.TERM; + } + } + } while (cont); + + foreach(fib; fibs) + { + assert(fib.sum == TestFiber.expSum); + } + } +} + +// Single thread running separate fibers +unittest +{ + runTen(); +} + +// Multiple threads running separate fibers +unittest +{ + auto group = new ThreadGroup(); + foreach(_; 0 .. 4) + { + group.create(&runTen); + } + group.joinAll(); +} + +// Multiple threads running shared fibers +unittest +{ + shared bool[10] locks; + TestFiber[10] fibs; + + void runShared() + { + bool cont; + do { + cont = false; + foreach(idx; 0 .. 10) + { + if (cas(&locks[idx], false, true)) + { + if (fibs[idx].state == Fiber.State.HOLD) + { + fibs[idx].call(); + cont |= fibs[idx].state != Fiber.State.TERM; + } + locks[idx] = false; + } + else + { + cont = true; + } + } + } while (cont); + } + + foreach(ref fib; fibs) + { + fib = new TestFiber(); + } + + auto group = new ThreadGroup(); + foreach(_; 0 .. 4) + { + group.create(&runShared); + } + group.joinAll(); + + foreach(fib; fibs) + { + assert(fib.sum == TestFiber.expSum); + } +} + +version( AsmX86_64_Posix ) +{ + unittest + { + void testStackAlignment() + { + void* pRSP; + asm + { + mov pRSP, RSP; + } + assert((cast(size_t)pRSP & 0xF) == 0); + } + + auto fib = new Fiber(&testStackAlignment); + fib.call(); + } +} + version( OSX ) { // NOTE: The Mach-O object file format does not allow for thread local