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_DEFAULT__
266bbc0262SSergey Makeev #define __MT_FIBER_DEFAULT__
276bbc0262SSergey Makeev 
286bbc0262SSergey Makeev #include "MTAtomic.h"
296bbc0262SSergey Makeev 
306bbc0262SSergey Makeev namespace MT
316bbc0262SSergey Makeev {
326bbc0262SSergey Makeev 
FiberGetSelf()33feebdc7dSs.makeev_local 	inline void* FiberGetSelf()
34feebdc7dSs.makeev_local 	{
35feebdc7dSs.makeev_local 		MW_BOOL isThreadAFiber = ::IsThreadAFiber();
36feebdc7dSs.makeev_local 		if  (isThreadAFiber == 0)
37feebdc7dSs.makeev_local 		{
38feebdc7dSs.makeev_local 			// GetCurrentFiber() return invalid values is current thread is not fiber.
39feebdc7dSs.makeev_local 			return nullptr;
40feebdc7dSs.makeev_local 		}
41feebdc7dSs.makeev_local 
42feebdc7dSs.makeev_local 		//This function is equal to GetCurrentFiber() macro
43feebdc7dSs.makeev_local 		void* pFiber = (void*)ReadTeb(MW_CURRENT_FIBER_OFFSET);
44feebdc7dSs.makeev_local 		return pFiber;
45feebdc7dSs.makeev_local 	}
46feebdc7dSs.makeev_local 
47feebdc7dSs.makeev_local 
486bbc0262SSergey Makeev 	//
496bbc0262SSergey Makeev 	// Fibers implementation using system fibers
506bbc0262SSergey Makeev 	// Beware! Windows Fibers are wasteful use of Virtual Memory space for the stack. ( 1Mb reserved for each Fiber )
516bbc0262SSergey Makeev 	//
526bbc0262SSergey Makeev 	class Fiber
536bbc0262SSergey Makeev 	{
546bbc0262SSergey Makeev 		void* funcData;
556bbc0262SSergey Makeev 		TThreadEntryPoint func;
566bbc0262SSergey Makeev 
576bbc0262SSergey Makeev 		void* fiber;
586bbc0262SSergey Makeev 
FiberFuncInternal(void * pFiber)596bbc0262SSergey Makeev 		static void __stdcall FiberFuncInternal(void* pFiber)
606bbc0262SSergey Makeev 		{
616bbc0262SSergey Makeev 			Fiber* self = (Fiber*)pFiber;
626bbc0262SSergey Makeev 			self->func(self->funcData);
636bbc0262SSergey Makeev 		}
646bbc0262SSergey Makeev 
CleanUp()653d930776Ss.makeev_local 		void CleanUp()
663d930776Ss.makeev_local 		{
673d930776Ss.makeev_local 			if (fiber)
683d930776Ss.makeev_local 			{
693d930776Ss.makeev_local 				// Do not destroy fibers created using ::ConvertThreadToFiberEx
703d930776Ss.makeev_local 				if (func != nullptr)
713d930776Ss.makeev_local 				{
723d930776Ss.makeev_local 					::DeleteFiber(fiber);
733d930776Ss.makeev_local 				}
74*d8cd6e1fSTsarevich Dmitry 				else
75*d8cd6e1fSTsarevich Dmitry 				{
76*d8cd6e1fSTsarevich Dmitry 					::ConvertFiberToThread();
77*d8cd6e1fSTsarevich Dmitry 				}
783d930776Ss.makeev_local 				fiber = nullptr;
793d930776Ss.makeev_local 			}
803d930776Ss.makeev_local 		}
813d930776Ss.makeev_local 
826bbc0262SSergey Makeev 	public:
836bbc0262SSergey Makeev 
842e846c40SSergey Makeev 		MT_NOCOPYABLE(Fiber);
852e846c40SSergey Makeev 
Fiber()866bbc0262SSergey Makeev 		Fiber()
876bbc0262SSergey Makeev 			: fiber(nullptr)
886bbc0262SSergey Makeev 		{
896bbc0262SSergey Makeev 		}
906bbc0262SSergey Makeev 
~Fiber()916bbc0262SSergey Makeev 		~Fiber()
926bbc0262SSergey Makeev 		{
933d930776Ss.makeev_local 			CleanUp();
946bbc0262SSergey Makeev 		}
956bbc0262SSergey Makeev 
966bbc0262SSergey Makeev 
CreateFromCurrentThreadAndRun(TThreadEntryPoint entryPoint,void * userData)97ae5bbefbSs.makeev_local 		void CreateFromCurrentThreadAndRun(TThreadEntryPoint entryPoint, void *userData)
986bbc0262SSergey Makeev 		{
996bbc0262SSergey Makeev 			MT_ASSERT(fiber == nullptr, "Fiber already created");
1006bbc0262SSergey Makeev 
1016bbc0262SSergey Makeev 			func = nullptr;
1026bbc0262SSergey Makeev 			funcData = nullptr;
1036bbc0262SSergey Makeev 
1043d930776Ss.makeev_local 			void* fiberSelf = FiberGetSelf();
1053d930776Ss.makeev_local 			if (fiberSelf != nullptr)
1063d930776Ss.makeev_local 			{
1073d930776Ss.makeev_local 				fiber = fiberSelf;
1083d930776Ss.makeev_local 			} else
1093d930776Ss.makeev_local 			{
11087ecbf38Ss.makeev 				fiber = ::ConvertThreadToFiberEx(nullptr, MW_FIBER_FLAG_FLOAT_SWITCH);
1116bbc0262SSergey Makeev 				MT_ASSERT(fiber != nullptr, "Can't create fiber");
1123d930776Ss.makeev_local 			}
11302d170cfSs.makeev_local 
11402d170cfSs.makeev_local 			entryPoint(userData);
1153d930776Ss.makeev_local 
1163d930776Ss.makeev_local 			CleanUp();
1176bbc0262SSergey Makeev 		}
1186bbc0262SSergey Makeev 
1196bbc0262SSergey Makeev 
Create(size_t stackSize,TThreadEntryPoint entryPoint,void * userData)1206bbc0262SSergey Makeev 		void Create(size_t stackSize, TThreadEntryPoint entryPoint, void* userData)
1216bbc0262SSergey Makeev 		{
1226bbc0262SSergey Makeev 			MT_ASSERT(fiber == nullptr, "Fiber already created");
1236bbc0262SSergey Makeev 
1246bbc0262SSergey Makeev 			func = entryPoint;
1256bbc0262SSergey Makeev 			funcData = userData;
1266bbc0262SSergey Makeev 			fiber = ::CreateFiber( stackSize, FiberFuncInternal, this );
1276bbc0262SSergey Makeev 			MT_ASSERT(fiber != nullptr, "Can't create fiber");
1286bbc0262SSergey Makeev 		}
1296bbc0262SSergey Makeev 
130d7cf17b1Ss.makeev_local #ifdef MT_INSTRUMENTED_BUILD
SetName(const char * fiberName)131d7cf17b1Ss.makeev_local 		void SetName(const char* fiberName)
132d7cf17b1Ss.makeev_local 		{
133d7cf17b1Ss.makeev_local 			MT_UNUSED(fiberName);
134d7cf17b1Ss.makeev_local 		}
135d7cf17b1Ss.makeev_local #endif
136d7cf17b1Ss.makeev_local 
SwitchTo(Fiber & from,Fiber & to)1376bbc0262SSergey Makeev 		static void SwitchTo(Fiber & from, Fiber & to)
1386bbc0262SSergey Makeev 		{
13987ecbf38Ss.makeev 			MT_USED_IN_ASSERT(from);
14087ecbf38Ss.makeev 
1416bbc0262SSergey Makeev 			HardwareFullMemoryBarrier();
1426bbc0262SSergey Makeev 
1436bbc0262SSergey Makeev 			MT_ASSERT(from.fiber != nullptr, "Invalid from fiber");
1446bbc0262SSergey Makeev 			MT_ASSERT(to.fiber != nullptr, "Invalid to fiber");
1456bbc0262SSergey Makeev 
14687ecbf38Ss.makeev 			::SwitchToFiber( (void*)to.fiber );
1476bbc0262SSergey Makeev 		}
1486bbc0262SSergey Makeev 
1496bbc0262SSergey Makeev 
1506bbc0262SSergey Makeev 	};
1516bbc0262SSergey Makeev 
1526bbc0262SSergey Makeev }
1536bbc0262SSergey Makeev 
1546bbc0262SSergey Makeev #endif
155