rubidium@8934: /* $Id$ */ rubidium@8934: rubidium@8934: /** @file fiber_win32.cpp Win32 implementation of Fiber. */ rubidium@8934: rubidium@8934: #include "stdafx.h" rubidium@8934: #include "fiber.hpp" rubidium@8934: #include rubidium@8934: #include rubidium@8934: #include rubidium@8934: rubidium@8934: class Fiber_Win32 : public Fiber { rubidium@8934: private: rubidium@8934: LPVOID m_fiber; rubidium@8934: FiberFunc m_proc; rubidium@8934: void *m_param; rubidium@8934: bool m_attached; rubidium@8934: rubidium@8934: static Fiber_Win32 *s_main; rubidium@8934: rubidium@8934: public: rubidium@8934: /** rubidium@8934: * Create a win32 fiber and start it, calling proc(param). rubidium@8934: */ rubidium@8934: Fiber_Win32(FiberFunc proc, void *param) : rubidium@8934: m_fiber(NULL), rubidium@8934: m_proc(proc), rubidium@8934: m_param(param), rubidium@8934: m_attached(false) rubidium@8934: { rubidium@8934: CreateFiber(); rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Create a win32 fiber and attach current thread to it. rubidium@8934: */ rubidium@8934: Fiber_Win32(void *param) : rubidium@8934: m_fiber(NULL), rubidium@8934: m_proc(NULL), rubidium@8934: m_param(param), rubidium@8934: m_attached(true) rubidium@8934: { rubidium@8934: ConvertThreadToFiber(); rubidium@8934: if (s_main == NULL) s_main = this; rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ ~Fiber_Win32() rubidium@8934: { rubidium@8934: if (this->m_fiber != NULL) { rubidium@8934: if (this->m_attached) { rubidium@8934: this->ConvertFiberToThread(); rubidium@8934: } else { rubidium@8934: this->DeleteFiber(); rubidium@8934: } rubidium@8934: } rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ void SwitchToFiber() rubidium@8934: { rubidium@8934: typedef VOID (WINAPI *FnSwitchToFiber)(LPVOID fiber); rubidium@8934: rubidium@8934: static FnSwitchToFiber fnSwitchToFiber = (FnSwitchToFiber)stGetProcAddr("SwitchToFiber"); rubidium@8934: assert(fnSwitchToFiber != NULL); rubidium@8934: rubidium@8934: fnSwitchToFiber(this->m_fiber); rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ void Exit() rubidium@8934: { rubidium@8934: /* Simply switch back to the main fiber, we kill the fiber sooner or later */ rubidium@8934: s_main->SwitchToFiber(); rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ bool IsRunning() rubidium@8934: { rubidium@8934: return this->m_fiber != NULL; rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ void *GetFiberData() rubidium@8934: { rubidium@8934: return this->m_param; rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Win95 doesn't have Fiber support. So check if we have Fiber support, rubidium@8934: * and else fall back on Fiber_Thread. rubidium@8934: */ rubidium@8934: static bool IsSupported() rubidium@8934: { rubidium@8934: static bool first_run = true; rubidium@8934: static bool is_supported = false; rubidium@8934: rubidium@8934: if (first_run) { rubidium@8934: first_run = false; rubidium@8934: static const char *names[] = { rubidium@8934: "ConvertThreadToFiber", rubidium@8934: "CreateFiber", rubidium@8934: "DeleteFiber", rubidium@8934: "ConvertFiberToThread", rubidium@8934: "SwitchToFiber"}; rubidium@8934: for (size_t i = 0; i < lengthof(names); i++) { rubidium@8934: if (stGetProcAddr(names[i]) == NULL) return false; rubidium@8934: } rubidium@8934: is_supported = true; rubidium@8934: } rubidium@8934: return is_supported; rubidium@8934: } rubidium@8934: rubidium@8934: private: rubidium@8934: /** rubidium@8934: * Get a function from kernel32.dll. rubidium@8934: * @param name Function to get. rubidium@8934: * @return Proc to the function, or NULL when not found. rubidium@8934: */ rubidium@8934: static FARPROC stGetProcAddr(const char *name) rubidium@8934: { rubidium@8934: static HMODULE hKernel = LoadLibraryA("kernel32.dll"); rubidium@8934: return GetProcAddress(hKernel, name); rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * First function which is called within the fiber. rubidium@8934: */ rubidium@8934: static VOID CALLBACK stFiberProc(LPVOID fiber) rubidium@8934: { rubidium@8934: Fiber_Win32 *cur = (Fiber_Win32 *)fiber; rubidium@8934: cur->m_proc(cur->m_param); rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Delete a fiber. rubidium@8934: */ rubidium@8934: void DeleteFiber() rubidium@8934: { rubidium@8934: typedef VOID (WINAPI *FnDeleteFiber)(LPVOID lpFiber); rubidium@8934: rubidium@8934: static FnDeleteFiber fnDeleteFiber = (FnDeleteFiber)stGetProcAddr("DeleteFiber"); rubidium@8934: assert(fnDeleteFiber != NULL); rubidium@8934: rubidium@8934: fnDeleteFiber(this->m_fiber); rubidium@8934: this->m_fiber = NULL; rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Convert a current thread to a fiber. rubidium@8934: */ rubidium@8934: void ConvertThreadToFiber() rubidium@8934: { rubidium@8934: typedef LPVOID (WINAPI *FnConvertThreadToFiber)(LPVOID lpParameter); rubidium@8934: rubidium@8934: static FnConvertThreadToFiber fnConvertThreadToFiber = (FnConvertThreadToFiber)stGetProcAddr("ConvertThreadToFiber"); rubidium@8934: assert(fnConvertThreadToFiber != NULL); rubidium@8934: rubidium@8934: this->m_fiber = fnConvertThreadToFiber(this); rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Create a new fiber. rubidium@8934: */ rubidium@8934: void CreateFiber() rubidium@8934: { rubidium@8934: typedef LPVOID (WINAPI *FnCreateFiber)(SIZE_T dwStackSize, LPFIBER_START_ROUTINE lpStartAddress, LPVOID lpParameter); rubidium@8934: rubidium@8934: static FnCreateFiber fnCreateFiber = (FnCreateFiber)stGetProcAddr("CreateFiber"); rubidium@8934: assert(fnCreateFiber != NULL); rubidium@8934: rubidium@8934: this->m_fiber = fnCreateFiber(0, &stFiberProc, this); rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Convert a fiber back to a thread. rubidium@8934: */ rubidium@8934: void ConvertFiberToThread() rubidium@8934: { rubidium@8934: typedef BOOL (WINAPI *FnConvertFiberToThread)(); rubidium@8934: rubidium@8934: static FnConvertFiberToThread fnConvertFiberToThread = (FnConvertFiberToThread)stGetProcAddr("ConvertFiberToThread"); rubidium@8934: assert(fnConvertFiberToThread != NULL); rubidium@8934: rubidium@8934: fnConvertFiberToThread(); rubidium@8934: this->m_fiber = NULL; rubidium@8934: } rubidium@8934: }; rubidium@8934: rubidium@8934: /* Initialize the static member of Fiber_Win32 */ rubidium@8934: /* static */ Fiber_Win32 *Fiber_Win32::s_main = NULL; rubidium@8934: rubidium@8934: /* Include Fiber_Thread, as Win95 needs it */ rubidium@8934: #include "fiber_thread.cpp" rubidium@8934: rubidium@8934: /* static */ Fiber *Fiber::New(FiberFunc proc, void *param) rubidium@8934: { rubidium@8934: if (Fiber_Win32::IsSupported()) return new Fiber_Win32(proc, param); rubidium@8934: return new Fiber_Thread(proc, param); rubidium@8934: } rubidium@8934: rubidium@8934: /* static */ Fiber *Fiber::AttachCurrent(void *param) rubidium@8934: { rubidium@8934: if (Fiber_Win32::IsSupported()) return new Fiber_Win32(param); rubidium@8934: return new Fiber_Thread(param); rubidium@8934: } rubidium@8934: rubidium@8934: /* static */ void *Fiber::GetCurrentFiberData() rubidium@8934: { rubidium@8934: if (Fiber_Win32::IsSupported()) return ((Fiber *)::GetFiberData())->GetFiberData(); rubidium@8934: return Fiber_Thread::GetCurrentFiber()->GetFiberData(); rubidium@8934: }