summaryrefslogtreecommitdiff
path: root/src/fiber_win32.cpp
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2008-04-14 19:54:33 +0000
committerrubidium <rubidium@openttd.org>2008-04-14 19:54:33 +0000
commit12188e7a5dde4cdd4304b3e738b7905e55f3ad9a (patch)
treea38226d767be0babf008f3304e7a61a4a00af2aa /src/fiber_win32.cpp
parenta8008db23daf504af9f313ff0c53c5b89e756e05 (diff)
downloadopenttd-12188e7a5dde4cdd4304b3e738b7905e55f3ad9a.tar.xz
(svn r12706) -Merge: the thread rewrite from NoAI. The rewrite makes the threading we have better extendable.
Diffstat (limited to 'src/fiber_win32.cpp')
-rw-r--r--src/fiber_win32.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/fiber_win32.cpp b/src/fiber_win32.cpp
new file mode 100644
index 000000000..61718188c
--- /dev/null
+++ b/src/fiber_win32.cpp
@@ -0,0 +1,206 @@
+/* $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();
+}