OpenTTD/src/fiber_win32.cpp

207 lines
4.5 KiB
C++

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