16bbc0262SSergey Makeev // The MIT License (MIT) 26bbc0262SSergey Makeev // 36bbc0262SSergey Makeev // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 46bbc0262SSergey Makeev // 56bbc0262SSergey Makeev // Permission is hereby granted, free of charge, to any person obtaining a copy 66bbc0262SSergey Makeev // of this software and associated documentation files (the "Software"), to deal 76bbc0262SSergey Makeev // in the Software without restriction, including without limitation the rights 86bbc0262SSergey Makeev // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 96bbc0262SSergey Makeev // copies of the Software, and to permit persons to whom the Software is 106bbc0262SSergey Makeev // furnished to do so, subject to the following conditions: 116bbc0262SSergey Makeev // 126bbc0262SSergey Makeev // The above copyright notice and this permission notice shall be included in 136bbc0262SSergey Makeev // all copies or substantial portions of the Software. 146bbc0262SSergey Makeev // 156bbc0262SSergey Makeev // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 166bbc0262SSergey Makeev // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 176bbc0262SSergey Makeev // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 186bbc0262SSergey Makeev // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 196bbc0262SSergey Makeev // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 206bbc0262SSergey Makeev // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 216bbc0262SSergey Makeev // THE SOFTWARE. 226bbc0262SSergey Makeev 236bbc0262SSergey Makeev #pragma once 246bbc0262SSergey Makeev 256bbc0262SSergey Makeev #ifndef __MT_FIBER_OPTIMIZED__ 266bbc0262SSergey Makeev #define __MT_FIBER_OPTIMIZED__ 276bbc0262SSergey Makeev 281e78cb24Ss.makeev_local #include <MTConfig.h> 2902d170cfSs.makeev_local #include <Platform/Common/MTAtomic.h> 30721f8c0bSs.makeev_local #include <string> 316bbc0262SSergey Makeev 326bbc0262SSergey Makeev 336bbc0262SSergey Makeev namespace MT 346bbc0262SSergey Makeev { 356bbc0262SSergey Makeev 366bbc0262SSergey Makeev // 376bbc0262SSergey Makeev // Windows fiber implementation through GetThreadContext / SetThreadContext 386bbc0262SSergey Makeev // I don't use standard Windows Fibers since they are wasteful use of Virtual Memory space for the stack. ( 1Mb for each Fiber ) 396bbc0262SSergey Makeev // 406bbc0262SSergey Makeev class Fiber 416bbc0262SSergey Makeev { 4253ef36e3Ss.makeev_local MW_CONTEXT fiberContext; 4353ef36e3Ss.makeev_local 446bbc0262SSergey Makeev void* funcData; 456bbc0262SSergey Makeev TThreadEntryPoint func; 466bbc0262SSergey Makeev 476bbc0262SSergey Makeev Memory::StackDesc stackDesc; 486bbc0262SSergey Makeev 496bbc0262SSergey Makeev bool isInitialized; 506bbc0262SSergey Makeev 511e78cb24Ss.makeev_local #if MT_PTR64 526bbc0262SSergey Makeev // https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention 536bbc0262SSergey Makeev // The Microsoft x64 calling convention is followed on Microsoft Windows. 546bbc0262SSergey Makeev // It uses registers RCX, RDX, R8, R9 for the first four integer or pointer arguments (in that order), and XMM0, XMM1, XMM2, XMM3 are used for floating point arguments. 556bbc0262SSergey Makeev 566bbc0262SSergey Makeev // Additional arguments are pushed onto the stack (right to left). FiberFuncInternal(long,long,long,long,void * pFiber)576bbc0262SSergey Makeev static void __stdcall FiberFuncInternal(long /*ecx*/, long /*edx*/, long /*r8*/, long /*r9*/, void *pFiber) 58d0d10efeSs.makeev #else 59d0d10efeSs.makeev // https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall 60d0d10efeSs.makeev // The stdcall calling convention is a variation on the Pascal calling convention in which the callee is responsible for cleaning up the stack, 61d0d10efeSs.makeev // but the parameters are pushed onto the stack in right-to-left order, as in the _cdecl calling convention. 62d0d10efeSs.makeev static void __stdcall FiberFuncInternal(void *pFiber) 636bbc0262SSergey Makeev #endif 646bbc0262SSergey Makeev { 656bbc0262SSergey Makeev MT_ASSERT(pFiber != nullptr, "Invalid fiber"); 666bbc0262SSergey Makeev Fiber* self = (Fiber*)pFiber; 676bbc0262SSergey Makeev 686bbc0262SSergey Makeev MT_ASSERT(self->isInitialized == true, "Using non initialized fiber"); 696bbc0262SSergey Makeev 706bbc0262SSergey Makeev MT_ASSERT(self->func != nullptr, "Invalid fiber func"); 716bbc0262SSergey Makeev self->func(self->funcData); 726bbc0262SSergey Makeev } 736bbc0262SSergey Makeev CleanUp()74*3d930776Ss.makeev_local void CleanUp() 75*3d930776Ss.makeev_local { 76*3d930776Ss.makeev_local if (isInitialized) 77*3d930776Ss.makeev_local { 78*3d930776Ss.makeev_local // if func != null than we have stack memory ownership 79*3d930776Ss.makeev_local if (func != nullptr) 80*3d930776Ss.makeev_local { 81*3d930776Ss.makeev_local Memory::FreeStack(stackDesc); 82*3d930776Ss.makeev_local } 83*3d930776Ss.makeev_local 84*3d930776Ss.makeev_local isInitialized = false; 85*3d930776Ss.makeev_local } 86*3d930776Ss.makeev_local } 87*3d930776Ss.makeev_local 88*3d930776Ss.makeev_local 896bbc0262SSergey Makeev public: 906bbc0262SSergey Makeev 912e846c40SSergey Makeev MT_NOCOPYABLE(Fiber); 922e846c40SSergey Makeev Fiber()936bbc0262SSergey Makeev Fiber() 946bbc0262SSergey Makeev : funcData(nullptr) 956bbc0262SSergey Makeev , func(nullptr) 966bbc0262SSergey Makeev , isInitialized(false) 976bbc0262SSergey Makeev { 981e78cb24Ss.makeev_local #if MT_PTR64 9953ef36e3Ss.makeev_local MT_ASSERT(IsPointerAligned( this, 16 ), "Fiber must be aligned by 16 bytes"); 10053ef36e3Ss.makeev_local MT_ASSERT(IsPointerAligned( &fiberContext, 16 ), "MW_CONTEXT must be aligned by 16 bytes"); 10153ef36e3Ss.makeev_local #endif 102d0d10efeSs.makeev memset(&fiberContext, 0, sizeof(MW_CONTEXT)); 1036bbc0262SSergey Makeev } 1046bbc0262SSergey Makeev ~Fiber()1056bbc0262SSergey Makeev ~Fiber() 1066bbc0262SSergey Makeev { 107*3d930776Ss.makeev_local CleanUp(); 1086bbc0262SSergey Makeev } 1096bbc0262SSergey Makeev CreateFromCurrentThreadAndRun(TThreadEntryPoint entryPoint,void * userData)110ae5bbefbSs.makeev_local void CreateFromCurrentThreadAndRun(TThreadEntryPoint entryPoint, void *userData) 1116bbc0262SSergey Makeev { 1126bbc0262SSergey Makeev MT_ASSERT(!isInitialized, "Already initialized"); 1136bbc0262SSergey Makeev 114d0d10efeSs.makeev fiberContext.ContextFlags = MW_CONTEXT_FULL; 115d0d10efeSs.makeev MW_BOOL res = GetThreadContext( GetCurrentThread(), &fiberContext ); 1162e846c40SSergey Makeev MT_USED_IN_ASSERT(res); 1176bbc0262SSergey Makeev MT_ASSERT(res != 0, "GetThreadContext - failed"); 1186bbc0262SSergey Makeev 1196bbc0262SSergey Makeev func = nullptr; 1206bbc0262SSergey Makeev funcData = nullptr; 1216bbc0262SSergey Makeev 1226bbc0262SSergey Makeev //Get thread stack information from thread environment block. 123d0d10efeSs.makeev stackDesc.stackTop = (void*)ReadTeb( MW_STACK_BASE_OFFSET /*FIELD_OFFSET(NT_TIB, StackBase)*/ ); 124d0d10efeSs.makeev stackDesc.stackBottom = (void*)ReadTeb( MW_STACK_STACK_LIMIT_OFFSET /*FIELD_OFFSET(NT_TIB, StackLimit)*/ ); 1256bbc0262SSergey Makeev 1266bbc0262SSergey Makeev isInitialized = true; 12702d170cfSs.makeev_local 12802d170cfSs.makeev_local entryPoint(userData); 129*3d930776Ss.makeev_local 130*3d930776Ss.makeev_local CleanUp(); 1316bbc0262SSergey Makeev } 1326bbc0262SSergey Makeev Create(size_t stackSize,TThreadEntryPoint entryPoint,void * userData)1336bbc0262SSergey Makeev void Create(size_t stackSize, TThreadEntryPoint entryPoint, void* userData) 1346bbc0262SSergey Makeev { 1356bbc0262SSergey Makeev MT_ASSERT(!isInitialized, "Already initialized"); 1366bbc0262SSergey Makeev 1376bbc0262SSergey Makeev func = entryPoint; 1386bbc0262SSergey Makeev funcData = userData; 1396bbc0262SSergey Makeev 140d0d10efeSs.makeev fiberContext.ContextFlags = MW_CONTEXT_FULL; 141d0d10efeSs.makeev MW_BOOL res = GetThreadContext( GetCurrentThread(), &fiberContext ); 1422e846c40SSergey Makeev MT_USED_IN_ASSERT(res); 1436bbc0262SSergey Makeev MT_ASSERT(res != 0, "GetThreadContext - failed"); 1446bbc0262SSergey Makeev 1456bbc0262SSergey Makeev stackDesc = Memory::AllocStack(stackSize); 1466bbc0262SSergey Makeev 147721f8c0bSs.makeev_local void (*pFunc)() = (void(*)())&FiberFuncInternal; 1486bbc0262SSergey Makeev 1496bbc0262SSergey Makeev char* sp = (char *)stackDesc.stackTop; 1506bbc0262SSergey Makeev char * paramOnStack = nullptr; 1516bbc0262SSergey Makeev 1526bbc0262SSergey Makeev // setup function address and stack pointer 1531e78cb24Ss.makeev_local #if MT_PTR64 1546bbc0262SSergey Makeev 1556bbc0262SSergey Makeev // http://blogs.msdn.com/b/oldnewthing/archive/2004/01/14/58579.aspx 1566bbc0262SSergey Makeev // Furthermore, space for the register parameters is reserved on the stack, in case the called function wants to spill them 1576bbc0262SSergey Makeev 1586bbc0262SSergey Makeev sp -= 16; // pointer size and stack alignment 1596bbc0262SSergey Makeev paramOnStack = sp; 1606bbc0262SSergey Makeev sp -= 40; // reserve for register params 1616bbc0262SSergey Makeev fiberContext.Rsp = (unsigned long long)sp; 1626bbc0262SSergey Makeev MT_ASSERT(((unsigned long long)paramOnStack & 0xF) == 0, "Params on X64 stack must be alligned to 16 bytes"); 163721f8c0bSs.makeev_local fiberContext.Rip = (unsigned long long) pFunc; 164d0d10efeSs.makeev 165d0d10efeSs.makeev #else 166d0d10efeSs.makeev 167d0d10efeSs.makeev sp -= sizeof(void*); // reserve stack space for one pointer argument 168d0d10efeSs.makeev paramOnStack = sp; 169d0d10efeSs.makeev sp -= sizeof(void*); 170d0d10efeSs.makeev fiberContext.Esp = (unsigned long long)sp; 171721f8c0bSs.makeev_local fiberContext.Eip = (unsigned long long) pFunc; 172d0d10efeSs.makeev 1736bbc0262SSergey Makeev #endif 1746bbc0262SSergey Makeev 1756bbc0262SSergey Makeev //copy param to stack here 1766bbc0262SSergey Makeev *(void**)paramOnStack = (void *)this; 1776bbc0262SSergey Makeev 178d0d10efeSs.makeev fiberContext.ContextFlags = MW_CONTEXT_FULL; 1796bbc0262SSergey Makeev 1806bbc0262SSergey Makeev isInitialized = true; 1816bbc0262SSergey Makeev } 1826bbc0262SSergey Makeev 183d7cf17b1Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD SetName(const char * fiberName)184d7cf17b1Ss.makeev_local void SetName(const char* fiberName) 185d7cf17b1Ss.makeev_local { 186d7cf17b1Ss.makeev_local MT_UNUSED(fiberName); 187d7cf17b1Ss.makeev_local } 188d7cf17b1Ss.makeev_local #endif 1896bbc0262SSergey Makeev SwitchTo(Fiber & from,Fiber & to)1906bbc0262SSergey Makeev static void SwitchTo(Fiber & from, Fiber & to) 1916bbc0262SSergey Makeev { 1926bbc0262SSergey Makeev HardwareFullMemoryBarrier(); 1936bbc0262SSergey Makeev 1946bbc0262SSergey Makeev MT_ASSERT(from.isInitialized, "Invalid from fiber"); 1956bbc0262SSergey Makeev MT_ASSERT(to.isInitialized, "Invalid to fiber"); 1966bbc0262SSergey Makeev 197d0d10efeSs.makeev MW_HANDLE thread = GetCurrentThread(); 1986bbc0262SSergey Makeev 199d0d10efeSs.makeev from.fiberContext.ContextFlags = MW_CONTEXT_FULL; 200d0d10efeSs.makeev MW_BOOL res = GetThreadContext(thread, &from.fiberContext ); 2016bbc0262SSergey Makeev MT_ASSERT(res != 0, "GetThreadContext - failed"); 2026bbc0262SSergey Makeev 2036bbc0262SSergey Makeev // Modify current stack information in TEB 2046bbc0262SSergey Makeev // 2056bbc0262SSergey Makeev // __chkstk function use TEB info and probe sampling to commit new stack pages 2066bbc0262SSergey Makeev // https://support.microsoft.com/en-us/kb/100775 2076bbc0262SSergey Makeev // 208d0d10efeSs.makeev WriteTeb(MW_STACK_BASE_OFFSET /*FIELD_OFFSET(NT_TIB, StackBase)*/ , (uint64)to.stackDesc.stackTop); 209d0d10efeSs.makeev WriteTeb(MW_STACK_STACK_LIMIT_OFFSET/*FIELD_OFFSET(NT_TIB, StackLimit)*/, (uint64)to.stackDesc.stackBottom); 2106bbc0262SSergey Makeev 2116bbc0262SSergey Makeev res = SetThreadContext(thread, &to.fiberContext ); 2126bbc0262SSergey Makeev MT_ASSERT(res != 0, "SetThreadContext - failed"); 2136bbc0262SSergey Makeev 2146bbc0262SSergey Makeev //Restore stack information 215d0d10efeSs.makeev WriteTeb(MW_STACK_BASE_OFFSET /*FIELD_OFFSET(NT_TIB, StackBase)*/, (uint64)from.stackDesc.stackTop); 216d0d10efeSs.makeev WriteTeb(MW_STACK_STACK_LIMIT_OFFSET /*FIELD_OFFSET(NT_TIB, StackLimit)*/, (uint64)from.stackDesc.stackBottom); 2176bbc0262SSergey Makeev } 2186bbc0262SSergey Makeev 2196bbc0262SSergey Makeev 2206bbc0262SSergey Makeev }; 2216bbc0262SSergey Makeev 2226bbc0262SSergey Makeev 2236bbc0262SSergey Makeev } 2246bbc0262SSergey Makeev 2256bbc0262SSergey Makeev #undef ReadTeb 2266bbc0262SSergey Makeev #undef WriteTeb 2276bbc0262SSergey Makeev 2286bbc0262SSergey Makeev 2296bbc0262SSergey Makeev #endif 230