(svn r12538) [NoAI] -Codechange: introducing fiber.hpp, a class to have fibers in your application via either Windows Fibers, or via thread.h
[NoAI] -Codechange: rewritten ai_threads.cpp, to work with Fiber class. Reduces code duplication, and should fix all the stupid asserts we had with newgames and dying AIs
-NOTE: now my head spins.. 24 hours of working with threads/fibers is bad for health :p Tnx glx for the testing!
--- a/projects/generate Tue Apr 01 13:45:04 2008 +0000
+++ b/projects/generate Wed Apr 02 10:55:28 2008 +0000
@@ -83,7 +83,7 @@
if ($0 == "MSVC" && "'$os'" != "MSVC") { next; }
if ($0 == "DIRECTMUSIC" && "'$enable_directmusic'" != "1") { next; }
if ($0 == "LIBTIMIDITY" && "'$libtimidity'" == "" ) { next; }
- if ($0 == "NO_THREADS" && "'$with_threads'" == "0") { next; }
+ if ($0 == "HAVE_THREAD" && "'$with_threads'" == "0") { next; }
skip += 1;
--- a/projects/generate.vbs Tue Apr 01 13:45:04 2008 +0000
+++ b/projects/generate.vbs Wed Apr 02 10:55:28 2008 +0000
@@ -72,7 +72,7 @@
line = "WIN32" Or _
line = "MSVC" Or _
line = "DIRECTMUSIC" Or _
- line = "NO_THREADS" _
+ line = "HAVE_THREAD" _
) Then skip = skip + 1
deep = deep + 1
Case "#"
--- a/projects/openttd_vs80.vcproj Tue Apr 01 13:45:04 2008 +0000
+++ b/projects/openttd_vs80.vcproj Wed Apr 02 10:55:28 2008 +0000
@@ -748,6 +748,10 @@
>
</File>
<File
+ RelativePath=".\..\src\fiber_win32.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\tile_map.cpp"
>
</File>
@@ -944,6 +948,10 @@
>
</File>
<File
+ RelativePath=".\..\src\fiber.hpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\fileio.h"
>
</File>
--- a/projects/openttd_vs90.vcproj Tue Apr 01 13:45:04 2008 +0000
+++ b/projects/openttd_vs90.vcproj Wed Apr 02 10:55:28 2008 +0000
@@ -745,6 +745,10 @@
>
</File>
<File
+ RelativePath=".\..\src\fiber_win32.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\tile_map.cpp"
>
</File>
@@ -941,6 +945,10 @@
>
</File>
<File
+ RelativePath=".\..\src\fiber.hpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\fileio.h"
>
</File>
--- a/source.list Tue Apr 01 13:45:04 2008 +0000
+++ b/source.list Wed Apr 02 10:55:28 2008 +0000
@@ -81,17 +81,19 @@
tgp.cpp
#if HAVE_THREAD
#if WIN32
- thread_win32.cpp
+ thread_win32.cpp
+ fiber_win32.cpp
#else
#if OS2
thread_os2.cpp
#else
thread_pthread.cpp
- #endif
- #endif
+ #end
+ fiber_thread.cpp
+ #end
#else
thread_none.cpp
-#endif
+#end
tile_map.cpp
#if WIN32
#else
@@ -156,6 +158,7 @@
engine_func.h
engine_type.h
core/enum_type.hpp
+fiber.hpp
fileio.h
fios.h
fontcache.h
--- a/src/ai/ai_threads.cpp Tue Apr 01 13:45:04 2008 +0000
+++ b/src/ai/ai_threads.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -7,6 +7,7 @@
#include "../variables.h"
#include "../debug.h"
#include "../thread.h"
+#include "../fiber.hpp"
#include "../vehicle_func.h"
#include "../player_func.h"
#include "ai.h"
@@ -17,136 +18,128 @@
#include "../misc/countedptr.hpp"
#include <map>
-class AIThread : public SimpleCountedObject {
-public:
- /** Different states of the AI thread. */
- enum ThreadState {
+class AIFiber : public SimpleCountedObject {
+private:
+ typedef std::map<int, CCountedPtr<AIFiber> > Fibers;
+
+ /** Different states of the AI fiber. */
+ enum FiberState {
INITIALIZED, ///< The mutex and conditions are initialized
- STARTING, ///< The thread is just started
+ STARTING, ///< The fiber is just started
RUNNING, ///< We are inside the main AI
SUSPENDED, ///< We are suspended (either by sleep or block)
STOPPING, ///< We are being stopped
STOPPED, ///< The main AI has stopped
};
- static const int MAIN_THREAD = -1;
-
-protected:
- typedef std::map<int, CCountedPtr<AIThread> > Threads;
-
- static Threads s_threads; ///< collection of active fibers
-
- int fiber_id; ///< id of this fiber (or MAIN_THREAD for the main) - equal to the ID of the current player
- ThreadState state; ///< The current state of the AI
- int ticks_to_sleep; ///< Sleep this many runticks
- AIController *controller; ///< Controller of this AI
-
+public:
/**
- * Initialize this AI Thread base class for the given controller.
- * @param player the player ID of this AI (used as fiber_id)
- * @param controller the AI have to run
- * @note this constructor is protected. It should be called only by
- * subclass (OS specific thread implementation).
+ * Initialize this AIFiber for the given controller.
+ * @param player The PlayerID of this AI.
+ * @param controller The AI we have to run.
*/
-protected:
- AIThread(PlayerID player, AIController *controller)
+ AIFiber(PlayerID player, AIController *controller) :
+ fiber_id(player),
+ state(INITIALIZED),
+ ticks_to_sleep(0),
+ controller(controller),
+ fiber(NULL)
{
- assert(s_threads[player] == NULL);
- /* Register new thread */
- s_threads[player] = this;
+ /* The player shouldn't have a fiber yet */
+ assert(s_fibers[player] == NULL);
+ /* Register the new fiber */
+ s_fibers[player] = this;
- /* Ensure that main thread has its wrapper too */
- if (player != MAIN_THREAD) stFind(MAIN_THREAD);
-
- this->state = INITIALIZED;
- this->fiber_id = player;
- this->ticks_to_sleep = 0;
- this->controller = controller;
+ /* Ensure that main fiber has its wrapper too */
+ if (player != MAIN_FIBER) {
+ stFind(MAIN_FIBER);
+ } else {
+ this->fiber = Fiber::AttachCurrent(this);
+ assert(this->fiber->IsRunning());
+ }
}
-public:
- virtual ~AIThread()
+ ~AIFiber()
{
- /* At this point we should be already removed from the thread collection */
- assert(this->fiber_id == MAIN_THREAD || stFind(this->fiber_id) == NULL);
+ /* Make sure we are not in the active list anymore */
+ assert(this->fiber_id == MAIN_FIBER || stFind(this->fiber_id) == NULL);
+ /* Clean up */
+ delete this->fiber;
}
/**
- * Start the AI thread and return once the AI calls Sleep or
- * any other suspending command.
- * @note should be called after Initialize, but before any other function.
- */
-public:
- virtual bool Start() = 0;
-
- /**
- * The method that runs in the new Thread.
- * @note should be called by the newly spawned thread in Start.
+ * Start the AI fiber and return once the AI calls Sleep or any other
+ * suspending command.
*/
-protected:
- void ThreadProc()
+ bool Start()
{
- assert(this->state == STARTING);
- this->state = RUNNING;
+ /* Make sure we start fibers from the main fiber */
+ assert(stCurrentFiber() == stFind(MAIN_FIBER));
+ /* We shouldn't have a fiber yet */
+ assert(this->fiber == NULL);
+ /* And we shouldn't be starting the main fiber */
+ assert(fiber_id != MAIN_FIBER);
- /* Run the AI. If this function ever returns, the AI is dead */
- this->controller->Start();
+ /* Create fiber for this AI */
+ this->fiber = Fiber::New(&stFiberProc, this);
+ if (!this->fiber->IsRunning()) return false;
- if (this->state != STOPPING) {
- DEBUG(ai, 1, "We've got a suicidal AI for player %d", this->fiber_id);
- /* The AI stopped on it's own */
- this->state = STOPPED;
- } else {
- this->state = STOPPED;
- }
+ /* Switch to STARTING mode */
+ this->state = SUSPENDED;
+ this->SwitchToState(STARTING);
+
+ return true;
}
/**
- * Suspend this AI.
- * @param timeout time to suspend. < 0 means infinite (MultiPlayer only!)
- * @note should be called from the AI thread.
+ * Suspend this AI for a 'timeout' of period.
+ * @param timeout Time to suspend. < 0 means infinite (MultiPlayer only!)
+ * @note Only call this from within the fiber you want to execute the function on.
*/
-public:
void Suspend(int timeout)
{
/* Should not attempt to suspend main fiber */
- assert(this->fiber_id != MAIN_THREAD);
+ assert(this->fiber_id != MAIN_FIBER);
/* AI fiber should be running */
assert(this->state == RUNNING);
/* We can only suspend ourself */
- assert(this->CurrentThread() == this);
+ assert(this->stCurrentFiber() == this);
this->ticks_to_sleep = timeout;
- /* When AI thread gets suspended, it always switches back to main thread */
- AIThread *main = stFind(MAIN_THREAD);
- main->SwitchToThis(RUNNING);
+ /* When AI fiber gets suspended, it always switches back to main fiber */
+ AIFiber *main = stFind(MAIN_FIBER);
+ main->SwitchToState(RUNNING);
- /* The AI is told to die by the main thread */
+ /* The main fiber wants this AI to die */
if (this->state == STOPPING) {
+ /* Inform the controller */
this->controller->Stop();
- OnStop();
- /* XXX -- It might be nice to throw an exception, as it is easier to handle */
- //throw std::exception();
- //NOT_REACHED();
- main->SwitchToThis(RUNNING);
+ /* Suspend, and exit */
+ this->state = SUSPENDED;
+ this->Exit();
+
+ /* Here we will never come as... the fiber is dead */
+ assert(false);
+
return;
}
+
assert(this->state == RUNNING);
}
/**
- * Set the AI thread to resume at the next call of RunTick.
- * @note should NOT be called from the AI thread.
+ * Set the AI fiber to resume at the next call of RunTick.
+ * @note Only call this from within the main fiber.
*/
-public:
void Resume()
{
/* Should be called from main fiber only */
- assert(CurrentThread() == stFind(MAIN_THREAD));
+ assert(stCurrentFiber() == stFind(MAIN_FIBER));
/* AI fiber should be suspended */
assert(this->state == SUSPENDED);
+
/* Normally the ticks_to_sleep hangs at -1 for MP. Possible the MP is
* faster then the delay requested by the user. In this case the value
* is lower. To let the normal delay system kick in, we reverse the value
@@ -156,22 +149,20 @@
}
/**
- * Let the AI thread run for a while and return when it is done.
- * However, when the thread is suspended and the suspend timeout
- * has not yet passed, nothing happens except decrementing the
- * before mentioned timeout.
- * @note should NOT be called from the AI thread.
+ * Let the AI fiber run for a while and return when it is done.
+ * However, when the fiber is suspended and the suspend timeout has not yet
+ * passed, nothing happens except decrementing the before mentioned timeout.
+ * @note Only call this from within the main fiber.
*/
-public:
void RunTick()
{
/* If we already stopped, don't do anything */
if (this->state == STOPPED) return;
/* Should be called from main fiber only */
- assert(CurrentThread() == stFind(MAIN_THREAD));
+ assert(stCurrentFiber() == stFind(MAIN_FIBER));
/* Only AI fibers should be resumed this way */
- assert(this != stFind(MAIN_THREAD));
+ assert(this != stFind(MAIN_FIBER));
/* AI fiber should be still suspended */
assert(this->state == SUSPENDED);
@@ -186,664 +177,182 @@
if (this->ticks_to_sleep < 0) return; // We have to wait infinitely
if (--this->ticks_to_sleep > 0) return; // We have to wait a little longer
- /* Make the thread running again */
- this->SwitchToThis(RUNNING);
- }
-
- /**
- * Stop the AI thread and wait till it is stopped.
- * @note should NOT be called from the AI thread.
- */
-public:
- virtual void Stop() = 0;
-
- /**
- * Called before the AI thread finishes. Should wakeup the main thread.
- */
-public:
- virtual void OnStop() {};
-
- /**
- * Called in the context of thread that wants to yield execution and switch
- * to 'this' thread. One of those threads should be always the main thread
- * (fiber_id == MAIN_THREAD).
- */
-public:
- virtual void SwitchToThis(ThreadState new_state) = 0;
-
- /**
- * Find and return the thread object by its 'id' (player_id or MAIN_THREAD).
- * Returns NULL if such thread object doesn't exist. If
- * (fiber_id == MAIN_THREAD) and the thread object with this id doesn't
- * exist, the new one is created and attached to the current (main) thread.
- */
-public:
- static AIThread *stFind(int fiber_id)
- {
- Threads::iterator it = s_threads.find(fiber_id);
- if (it == s_threads.end()) {
- if (fiber_id != MAIN_THREAD) return NULL;
- /* main thread doesn't have its own thread object, create it */
- return stNew((PlayerID)MAIN_THREAD, NULL);
- }
- return (*it).second;
- }
-
- /**
- * Remove thread object from the collection by its 'id' and destroy it.
- */
-public:
- static void stRelease(int fiber_id)
- {
- Threads::iterator it = s_threads.find(fiber_id);
- if (it != s_threads.end()) s_threads.erase(it);
- }
-
- /**
- * Find the thread object that belongs to the currently running thread (caller).
- */
-public:
- static AIThread *stCurrentThread()
- {
- AIThread *main = stFind(MAIN_THREAD);
- AIThread *cur = main->CurrentThread();
- return cur;
+ /* Make the fiber running again */
+ this->SwitchToState(RUNNING);
}
/**
- * Find the thread object that belongs to the currently running thread (caller).
- */
-public:
- virtual AIThread *CurrentThread() = 0;
-
- /**
- * Create new AI thread object for given player and add it into thread collection.
+ * Try to stop the AI, and mark it as such. You can cleanup the AI after
+ * this if you like; it won't be become active for sure.
+ * @note There is no guarantee the fiber dies or anything, we just promise
+ * it won't be called anymore.
+ * @note Only call this from within the main fiber.
*/
-public:
- static AIThread *stNew(PlayerID player, AIController *controller);
-};
-
-/**
- * Collection of all AI Thread objects.
- */
-/* static */ AIThread::Threads AIThread::s_threads;
-
-/**
- * AI thread implementation using Win32 thread API. This should be used only on Win95
- * where Fiber API is not supported. Main OTTD thread has assigned the first object
- * in collection (AIThread::s_threads[MAIN_THREAD]). Other threads created by this main
- * thread have assigned higher slots [PlayerID]. This implementation is supposed to emulate
- * Win32 fibers using normal Win32 threads. Synchronisation is done using simple event
- * object (kind of bi-state semaphore gate - 0/1 - which is reset to 0 when thread passes
- * the wait function and must be set back to 1 in order to fire another thread).
- * Each thread from the collection (except one) is wating on its own event object. So the
- * thread that wants to yeald execution must set the event object of the target thread
- * and then enter its own wait state.
- */
-class AIThread_MT : public AIThread {
- static int s_current; ///< currently executed thread
-
- AutoPtrT<ThreadSemaphore> evt_go; ///< Win32 event handle that signals the thread can go
- ThreadState next_state; ///< Next state after wakeup
- AutoPtrT<ThreadObject> thr; ///< Thread Object associated with this AI thread
-
- /**
- * Initialize this AI Thread Object for the given controller.
- * @param player the player ID of this AI (used as fiber_id) or MAIN_THREAD if attaching to the main game thread
- * @param controller the AI have to run
- */
-public:
- AIThread_MT(PlayerID player, AIController *controller)
- : AIThread(player, controller)
- {
- DEBUG(ai, 3, "+AIThread_MT(%d) from thr %d", player, ThreadObject::CurrentId());
- this->evt_go = ThreadSemaphore::New();
- this->next_state = STOPPED;
-
- /* Handle main thread differently. When AIThread::AIThread() calls stFind() the new Thread Object
- * is created for the main thread. In this case we will not create new system thread but rather
- * attach to the curent one. */
- if (player == MAIN_THREAD) {
- /* also main thread needs to know own win32 thread id */
- this->thr = ThreadObject::AttachCurrent();
- }
- }
-
- /**
- * Destructor (AI Thread Object cleanup).
- */
-public:
- /*virtual*/ ~AIThread_MT()
+ void Stop()
{
- DEBUG(ai, 3, "-AIThread_MT(%d) from thr %u", this->fiber_id, ThreadObject::CurrentId());
-
- /* Flush stdout and stderr, as killing the thread doesn't allow the thread
- * to do that (in case there still is something to print) */
- fflush(stdout);
- fflush(stderr);
-
- this->evt_go.Detach();
- }
-
- /**
- * Implementation specific Start() routine.
- * @see AIThread::Start()
- */
-public:
- virtual bool Start()
- {
- AIThread_MT *cur = stCurrentFiber();
- assert(cur != this); // target fiber is not active already
- assert(fiber_id != MAIN_THREAD); // main fiber can't be started this way
-
- cur->state = SUSPENDED;
- this->state = SUSPENDED;
- this->next_state = STARTING;
- this->thr = ThreadObject::New(&stThreadProc, this);
- cur->EnterWaitState();
-
- return true;
- }
-
- /**
- * Function that is started as process of the new AI thread (fiber).
- * @param fiber AIThread_Fiber* to the fiber that starts
- */
-protected:
- static void CDECL *stThreadProc(void *thr)
- {
- AIThread_MT *cur = (AIThread_MT*)thr;
- cur->FiberProc();
- return NULL;
- }
-
- /**
- * The method that runs in the context of new thread (virtual fiber) when started.
- */
-protected:
- void FiberProc()
- {
- try
- {
- s_current = this->fiber_id;
- this->state = this->next_state;
- AIThread::ThreadProc();
- }
- catch (std::exception &e)
- {
- DEBUG(ai, 0, "%s", e.what());
- }
- DEBUG(ai, 3, "thread %d finished", this->fiber_id);
-
- /* Tell the main thread that we are stopping */
- this->OnStop();
- }
-
- /**
- * Implementation specific way how to stop execution of the AI thread.
- * @see AIThread::Stop()
- */
-public:
- /*virtual*/ void Stop()
- {
+ /* Should be called from main fiber only */
+ assert(stCurrentFiber() == stFind(MAIN_FIBER));
/* If we already stopped, don't do anything */
if (this->state == STOPPED) return;
- AIThread_MT *cur = stCurrentFiber();
- /* The AI stopping itself */
- if (cur == this) {
- /* must be AI fiber */
- assert(this->fiber_id != MAIN_THREAD);
+ /* Trigger a STOPPING round */
+ assert(this->state == SUSPENDED);
+ this->SwitchToState(STOPPING);
+ assert(this->state == SUSPENDED);
- DEBUG(ai, 3, "this->OnStop(%d) from thr %d", this->fiber_id, ThreadObject::CurrentId());
- this->state = STOPPED;
- this->controller->Stop();
- //throw std::exception();
- //NOT_REACHED();
- return;
- }
-
- assert(cur->fiber_id == MAIN_THREAD);
- assert(this->state == SUSPENDED);
- this->SwitchToThis(STOPPING);
- assert(this->state == SUSPENDED);
+ /* Mark the fiber as STOPPED */
this->state = STOPPED;
}
/**
- * This routine should notify main thread that AI thread finished (stopped
- * forever).
+ * Find and return the fiber object by its 'id' (player_id or MAIN_FIBER).
+ * Returns NULL if such fiber object doesn't exist. If
+ * (fiber_id == MAIN_FIBER) and the fiber object with this id doesn't
+ * exist, the new one is created and attached to the current (main) fiber.
*/
-protected:
- /*virtual*/ void OnStop()
+ static AIFiber *stFind(int fiber_id)
{
- AIThread_MT *main = (AIThread_MT*)stFind(MAIN_THREAD);
- main->evt_go->Set();
- }
-
- /**
- * Implementation specific way how to yield execution to the other thread (virtual fiber).
- * @see AIThread::SwitchToThis()
- */
-protected:
- void SwitchToThis(ThreadState new_state)
- {
- AIThread_MT *cur = stCurrentFiber();
- assert(cur != this); // target fiber is not active already
- assert(this->state == SUSPENDED); // target fiber is in proper state
- assert(this->thr->IsRunning() || this->fiber_id == MAIN_THREAD); // target fiber is created
-
- cur->state = SUSPENDED;
- this->next_state = new_state;
+ Fibers::iterator it = s_fibers.find(fiber_id);
+ if (it != s_fibers.end()) return (*it).second;
+ if (fiber_id != MAIN_FIBER) return NULL;
- {
- /* Protect the AI Thread Object against sudden death in the case when
- * AI decides to stop itself by calling AI_StopPlayer(). Technically there
- * is no problem deleting the the AI Thread Object from its thread, but for
- * the code robustness there is assert(m_attached || !IsRunning()); to avoid
- * that. */
- CCountedPtr<AIThread> protect_this = this;
- this->evt_go->Set();
- cur->EnterWaitState();
- } // end of 'protection area'
- }
-
- /**
- * Wait for the 'GO' signal. This way all thread except one are waiting on their own
- * event object in order to emulate cooperative multithreading (fibers).
- */
-protected:
- void EnterWaitState()
- {
- assert(this->thr->IsCurrent()); // can wait on own event only
- this->evt_go->Wait();
- s_current = this->fiber_id;
- assert(this->state == SUSPENDED);
- this->state = this->next_state;
- assert(this->state != SUSPENDED);
- }
-
- /**
- * Ugly way how to get AI Thread object that belongs to the currently executed
- * (calling) thread.
- * @todo consider using TLS API or thread map to make it less ugly
- */
-protected:
- AIThread_MT* stCurrentFiber()
- {
- AIThread_MT *cur = (AIThread_MT*)stFind(s_current);
- assert(cur != NULL);
- assert(cur->thr->IsCurrent());
- return cur;
+ /* Main fiber doesn't have its own fiber object, create it */
+ return new AIFiber((PlayerID)MAIN_FIBER, NULL);
}
/**
- * Ugly way how to provide stCurrentFiber() functionality for the base class.
+ * Remove fiber object from the collection by its 'id' and destroy it.
*/
-protected:
- /*virtual*/ AIThread *CurrentThread()
+ static void stRelease(int fiber_id)
{
- return stCurrentFiber();
- }
-};
-
-/**
- * Ugly way how to get AI Thread object that belongs to the currently executed
- * (calling) thread.
- * @todo consider using TLS API or thread map to make it less ugly
- */
- /* static */ int AIThread_MT::s_current = AIThread::MAIN_THREAD;
-
-#ifdef _WIN32
-
-#include <windows.h>
-#include <process.h>
-
-/**
- * AI thread implementation using native Win32 fiber API. This should be preferred
- * way for most of Win32 platforms - all except Win95 (this API was introduced on Win98).
- * All fibers share the same Win32 thread but each fiber has its own stack and context.
- * They are not preempted, cannot run in parallel and they must cooperatively switch
- * (yield execution from one to another) in order to execute them all.
- * One fiber has id == MAIN_THREAD. This fiber is main (belongs to the main OTTD thread).
- * The main fiber is created using ConvertThreadToFiber() API so then it can create and
- * execute other fibers. Fiber API allows to provide one void* as fiber data. We use it
- * to hold pointer to the AI thread object (AIThread_Fiber*) it is related to and
- * which controls the fiber execution state.
- *
- * This Win32 specific implementation can be made more generic using libpth on unix.
- */
-class AIThread_Fiber : public AIThread {
- DWORD id_thr; ///< Win32 thread id (used for assert only)
- LPVOID fiber; ///< Win32 fiber
-
-public:
- /**
- * Initialize this AI Thread Object for the given controller.
- * @param player the player ID of this AI (used as fiber_id)
- * @param controller the AI have to run
- */
- AIThread_Fiber(PlayerID player, AIController *controller)
- : AIThread(player, controller)
- {
- this->id_thr = GetCurrentThreadId();
- this->fiber = NULL;
- if (player == MAIN_THREAD) {
- this->ConvertThreadToFiber();
- assert(this->fiber != NULL);
- }
- }
-
- /**
- * Destructor (AI Thread Object cleanup).
- */
-public:
- /*virtual*/ ~AIThread_Fiber()
- {
- if (this->fiber_id == MAIN_THREAD) {
- this->ConvertFiberToThread();
- } else {
- this->DeleteFiber();
- }
+ Fibers::iterator it = s_fibers.find(fiber_id);
+ if (it != s_fibers.end()) s_fibers.erase(it);
}
/**
- * Fiber specific Start() routine.
- * @see AIThread::Start()
+ * Find the fiber object that belongs to the currently running fiber (caller).
*/
-public:
- virtual bool Start()
- {
- assert(stCurrentFiber() != this); // target fiber is not active already
- assert(this->fiber == NULL); // not yet started
- assert(fiber_id != MAIN_THREAD); // main fiber can't be started this way
-
- /* create fiber for this AI thread */
- this->CreateFiber();
- if (this->fiber == NULL) return false; // unable to create fiber
-
- /* Set initial target state and switch to the target thread. This will start
- * the other fiber execution by calling its FiberProc(). */
- this->state = SUSPENDED; // initial state after creation going to wake up soon
- this->SwitchToThis(STARTING);
-
- return true; // indicate success
- }
-
- /**
- * Fiber specific way how to stop execution of the AI thread.
- * @see AIThread::Stop()
- */
-public:
- virtual void Stop()
+ static AIFiber *stCurrentFiber()
{
- /* If we already stopped, don't do anything */
- if (this->state == STOPPED) return;
-
- AIThread_Fiber *cur = stCurrentFiber();
- /* The AI stopping itself? */
- if (cur == this) {
- /* Yes, stopping itself. Must be AI fiber */
- assert(this->fiber_id != MAIN_THREAD);
-
- this->state = STOPPED;
- this->controller->Stop();
- //throw std::exception();
- //NOT_REACHED();
- return;
- }
-
- /* Main thread (fiber) stopping AI thread */
- assert(cur->fiber_id == MAIN_THREAD);
- assert(this->state == SUSPENDED);
- this->SwitchToThis(STOPPING);
- assert(this->state == SUSPENDED);
- this->state = STOPPED;
-
- this->DeleteFiber();
- }
-
- /**
- * Fiber specific way how to yield execution to the other fiber.
- * @see AIThread::SwitchToThis()
- */
-public:
- void SwitchToThis(ThreadState new_state)
- {
- assert(stCurrentFiber() != this); // target fiber is not active already
- assert(this->state == SUSPENDED); // target fiber is in proper state
- assert(this->fiber != NULL); // target fiber is created
-
- stCurrentFiber()->state = SUSPENDED;
-
- {
- /* Protect 'this' so it can't be deleted inside SwitchToFiber().
- * It could happen if AI decides to stop itself by calling AI_StopPlayer().
- * The problem is that AI Thread Object destructor calls DeleteFiber(). There
- * is built-in limitation that you can't call DeleteFiber() to delete
- * the current (running) fiber. If you do so, the thread exits and whole
- * application ends. */
- CCountedPtr<AIThread> protect_this = this;
- this->state = new_state;
- this->SwitchToFiber();
- assert(this->state == SUSPENDED);
- } // end of 'protection area'
-
- stCurrentFiber()->state = RUNNING;
- }
-
- /**
- * Get AI thread instance of the current (calling) fiber.
- */
-public:
- AIThread_Fiber* stCurrentFiber()
- {
- AIThread_Fiber *cur = (AIThread_Fiber*)::GetFiberData();
+ AIFiber *cur = (AIFiber *)Fiber::GetCurrentFiberData();
assert(cur != NULL);
return cur;
}
+private:
/**
- * Ugly way how to provide stCurrentFiber() functionality for the base class.
+ * Switch the state of a fiber. It becomes active and executed until it
+ * changes the state. The current active fiber becomes SUSPENDED.
+ * @note Only call this from an other fiber.
*/
-public:
- /* virtual */ AIThread *CurrentThread()
+ void SwitchToState(FiberState new_state)
{
- return stCurrentFiber();
+ /* You can't switch the state of an already active fiber */
+ assert(stCurrentFiber() != this);
+ /* The fiber should be suspended */
+ assert(this->state == SUSPENDED);
+ /* And there should be an active fiber on it */
+ assert(this->fiber != NULL);
+
+ /* Suspend the fiber */
+ stCurrentFiber()->state = SUSPENDED;
+
+ /* Switch to new state, activate fiber, and check if we are suspended again */
+ this->state = new_state;
+ this->fiber->SwitchToFiber();
+ assert(this->state == SUSPENDED);
}
+ /**
+ * The method that runs in the new Fiber. Automaticly called when the Fiber becomes active.
+ */
+ void FiberProc()
+ {
+ /* We should be starting */
+ assert(this->state == STARTING);
+ /* We should be the active thread */
+ assert(stCurrentFiber() == this);
+
+ /* Switch to running */
+ this->state = RUNNING;
+
+ /* Start up the AI (this should be an infinite loop) */
+ this->controller->Start();
+
+ /* If we come here, the AI exited because it wanted to */
+ DEBUG(ai, 1, "We've got a suicidal AI for player %d", this->fiber_id);
+
+ /* Wait until we are killed nicely by the game */
+ AIFiber *main = stFind(MAIN_FIBER);
+ while (this->state != STOPPING) {
+ main->SwitchToState(RUNNING);
+ }
+
+ /* Suspend, and exit */
+ this->state = SUSPENDED;
+ this->Exit();
+
+ /* Here we will never come as... the fiber is dead */
+ assert(false);
+ }
/**
- * Function that is started as process of the new AI thread (fiber).
- * @param fiber AIThread_Fiber* to the fiber that starts
+ * Function that is started as process of the new AI fiber.
*/
-protected:
- static VOID CALLBACK stFiberProc(LPVOID fiber)
+ static void CDECL stFiberProc(void *fiber)
{
- AIThread_Fiber *cur = (AIThread_Fiber*)fiber;
+ AIFiber *cur = (AIFiber *)fiber;
cur->FiberProc();
}
/**
- * The method that runs in the context of new Fiber when started.
- */
-protected:
- void FiberProc()
- {
- try
- {
- AIThread::ThreadProc();
- }
- catch (std::exception &e)
- {
- DEBUG(ai, 0, "%s", e.what());
- }
- DEBUG(ai, 3, "fiber %d finished", this->fiber_id);
- stFind(MAIN_THREAD)->SwitchToThis(RUNNING);
- }
-
- /**
- * Simple wrapper of the LoadLibrary() and GetProcAddress() APIs that returns
- * pointer to API function of the given name. The fiber specific APIs are retrieved
- * this way (dynamically) instead of just linking to the kernel32.dll statically.
- * Linking them statically would cause OTTD crash on startup due to the unsatisfied
- * imports. In our case we just get NULL if function is not there.
- */
-protected:
- static FARPROC stGetProcAddr(const char *name)
- {
- static HMODULE hKernel = LoadLibraryA("kernel32.dll");
- return GetProcAddress(hKernel, name);
- }
-
- /**
- * Dynamic wrapper of ConvertThreadToFiber() Win32 API function.
- * @see stGetProcAddr
+ * Exit a running fiber.
+ * @note Only call this from within the fiber you want to execute the function on.
*/
-protected:
- void ConvertThreadToFiber()
- {
- assert(this->fiber_id == MAIN_THREAD);
- typedef LPVOID (WINAPI *FnConvertThreadToFiber)(LPVOID lpParameter);
- static FnConvertThreadToFiber fnConvertThreadToFiber = (FnConvertThreadToFiber)stGetProcAddr("ConvertThreadToFiber");
- assert(fnConvertThreadToFiber != NULL);
- this->fiber = fnConvertThreadToFiber(this);
- }
-
- /**
- * Dynamic wrapper of CreateFiber() Win32 API function.
- * @see stGetProcAddr
- */
-protected:
- void CreateFiber()
+ void Exit()
{
- assert(this->fiber_id != MAIN_THREAD);
- typedef LPVOID (WINAPI *FnCreateFiber)(SIZE_T dwStackSize, LPFIBER_START_ROUTINE lpStartAddress, LPVOID lpParameter);
- static FnCreateFiber fnCreateFiber = (FnCreateFiber)stGetProcAddr("CreateFiber");
- assert(fnCreateFiber != NULL);
- this->fiber = fnCreateFiber(0, &stFiberProc, this);
- }
+ /* We can only exit if we are the thread */
+ assert(stCurrentFiber() == this);
+ /* We can never exit the main fiber */
+ assert(fiber_id != MAIN_FIBER);
- /**
- * Dynamic wrapper of DeleteFiber() Win32 API function.
- * @see stGetProcAddr
- */
-protected:
- void DeleteFiber()
- {
- assert(this->fiber_id != MAIN_THREAD);
- assert(this != stCurrentFiber());
- typedef VOID (WINAPI * FnDeleteFiber)(LPVOID lpFiber);
- static FnDeleteFiber fnDeleteFiber = (FnDeleteFiber)stGetProcAddr("DeleteFiber");
- assert(fnDeleteFiber != NULL);
- fnDeleteFiber(this->fiber);
- this->fiber = NULL;
+ this->fiber->Exit();
}
- /**
- * Dynamic wrapper of ConvertFiberToThread() Win32 API function.
- * @see stGetProcAddr
- */
-protected:
- void ConvertFiberToThread()
- {
- assert(this->fiber_id == MAIN_THREAD);
- typedef BOOL (WINAPI *FnConvertFiberToThread)();
- static FnConvertFiberToThread fnConvertFiberToThread = (FnConvertFiberToThread)stGetProcAddr("ConvertFiberToThread");
- assert(fnConvertFiberToThread != NULL);
- fnConvertFiberToThread();
- this->fiber = NULL;
- }
+ static Fibers s_fibers; ///< Collection of active fibers.
+ static const int MAIN_FIBER = -1;
- /**
- * Dynamic wrapper of SwitchToFiber() Win32 API function.
- * @see stGetProcAddr
- */
-protected:
- void SwitchToFiber()
- {
- assert(this->fiber != NULL);
- typedef VOID (WINAPI *FnSwitchToFiber)(LPVOID fiber);
- static FnSwitchToFiber fnSwitchToFiber = (FnSwitchToFiber)stGetProcAddr("SwitchToFiber");
- assert(fnSwitchToFiber != NULL);
- fnSwitchToFiber(this->fiber);
- }
-
- /**
- * Returns true if Win32 fiber API is supported.
- * @see stGetProcAddr
- */
-public:
- 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;
- }
+ int fiber_id; ///< id of this fiber (or MAIN_FIBER for the main).
+ FiberState state; ///< The current state of the AI.
+ int ticks_to_sleep; ///< Sleep this many runticks.
+ AIController *controller; ///< Controller of this AI.
+ Fiber *fiber; ///< Fiber we are connected to.
};
-/**
- * Create, register and return the new AI Thread object. It should choose the best
- * implementation for the current platform.
- */
-/* static */ AIThread *AIThread::stNew(PlayerID player, AIController *controller)
-{
- if (AIThread_Fiber::IsSupported()) {
- /* Fibers are supported, use them */
- return new AIThread_Fiber(player, controller);
- }
- /* Fibers are not supported (Win95?) */
- return new AIThread_MT(player, controller);
-}
-
-#else /*_WIN32*/
-
-/**
- * Create, register and return the new AI Thread object.
- */
-/* static */ AIThread *AIThread::stNew(PlayerID player, AIController *controller)
-{
- return new AIThread_MT(player, controller);
-}
-
-#endif /*!_WIN32*/
-
+/* static */ AIFiber::Fibers AIFiber::s_fibers;
/**
* Suspend the AI handler of a player.
* @param player the player to suspend for
* @param timeout Time to suspend. < 0 means infinite (MultiPlayer only!)
- * @note This should be called from within the thread running this player!
+ * @note This should be called from within the fiber running this player!
*/
void AI_SuspendPlayer(PlayerID player, int timeout)
{
DEBUG(ai, 5, "AI_SuspendPlayer(%d, %d) from thr %d", player, timeout, ThreadObject::CurrentId());
- AIThread *thr = AIThread::stCurrentThread();
+ AIFiber *thr = AIFiber::stCurrentFiber();
assert(thr != NULL);
thr->Suspend(timeout);
}
/**
- * Run the thread of an AI player. Return when it is suspended again.
+ * Run the fiber of an AI player. Return when it is suspended again.
* @param player the player to run this tick for
*/
void AI_RunTick(PlayerID player)
{
DEBUG(ai, 6, "AI_RunTick(%d) from thr %d", player, ThreadObject::CurrentId());
- AIThread *thr = AIThread::stFind(player);
+ AIFiber *thr = AIFiber::stFind(player);
if (thr == NULL) {
DEBUG(ai, 0, "AI_RunTick() called for dead AI player #%d", player);
return;
@@ -859,9 +368,9 @@
void AI_StartPlayer(PlayerID player, AIController *controller)
{
DEBUG(ai, 3, "AI_StartPlayer(%d) from thr %d", player, ThreadObject::CurrentId());
- AIThread *thr = AIThread::stFind(player);
+ AIFiber *thr = AIFiber::stFind(player);
assert(thr == NULL);
- thr = AIThread::stNew(player, controller);
+ thr = new AIFiber(player, controller);
thr->Start();
}
@@ -872,10 +381,10 @@
void AI_StopPlayer(PlayerID player)
{
DEBUG(ai, 3, "AI_StopPlayer(%d) from thr %d", player, ThreadObject::CurrentId());
- AIThread *thr = AIThread::stFind(player);
+ AIFiber *thr = AIFiber::stFind(player);
if (thr == NULL) return;
thr->Stop();
- AIThread::stRelease(player);
+ AIFiber::stRelease(player);
}
/**
@@ -885,14 +394,14 @@
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
{
DEBUG(ai, 6, "CcAI(%d) from thr %d", _current_player, ThreadObject::CurrentId());
- AIThread *thr = AIThread::stFind(_current_player);
+ AIFiber *thr = AIFiber::stFind(_current_player);
assert(thr != NULL);
/* Store if we were a success or not */
AIObject::SetLastCommandRes(success);
/* Store some values inside the AIObject static memory */
AIObject::SetNewVehicleID(_new_vehicle_id);
- /* Resume the thread now */
+ /* Resume the fiber now */
thr->Resume();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fiber.hpp Wed Apr 02 10:55:28 2008 +0000
@@ -0,0 +1,53 @@
+/* $Id$ */
+
+/** @file fiber.hpp */
+
+#ifndef FIBER_HPP
+#define FIBER_HPP
+
+typedef void (CDECL *FiberFunc)(void *);
+
+class Fiber {
+public:
+ /**
+ * Switch to this fiber.
+ */
+ virtual void SwitchToFiber() = 0;
+
+ /**
+ * Exit a fiber.
+ */
+ virtual void Exit() = 0;
+
+ /**
+ * Check if a fiber is running.
+ */
+ virtual bool IsRunning() = 0;
+
+ /**
+ * Get the 'param' data of the Fiber.
+ */
+ virtual void *GetFiberData() = 0;
+
+ /**
+ * Virtual Destructor to mute warnings.
+ */
+ virtual ~Fiber() {};
+
+ /**
+ * Create a new fiber, calling proc(param) when running.
+ */
+ static Fiber *New(FiberFunc proc, void *param);
+
+ /**
+ * Attach a current thread to a new fiber.
+ */
+ static Fiber *AttachCurrent(void *param);
+
+ /**
+ * Get the 'param' data of the current active Fiber.
+ */
+ static void *GetCurrentFiberData();
+};
+
+#endif /* FIBER_HPP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fiber_thread.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -0,0 +1,153 @@
+/* $Id$ */
+
+/** @file fiber_thread.cpp ThreadObject implementation of Fiber. */
+
+#include "stdafx.h"
+#include "fiber.hpp"
+#include "thread.h"
+#include <stdlib.h>
+
+class Fiber_Thread : public Fiber {
+private:
+ ThreadObject *m_thread;
+ FiberFunc m_proc;
+ void *m_param;
+ bool m_attached;
+ ThreadSemaphore *m_sem;
+ bool m_kill;
+
+ static Fiber_Thread *s_current;
+ static Fiber_Thread *s_main;
+
+public:
+ /**
+ * Create a ThreadObject fiber and start it, calling proc(param).
+ */
+ Fiber_Thread(FiberFunc proc, void *param) :
+ m_thread(NULL),
+ m_proc(proc),
+ m_param(param),
+ m_attached(false),
+ m_kill(false)
+ {
+ this->m_sem = ThreadSemaphore::New();
+ /* Create a thread and start stFiberProc */
+ this->m_thread = ThreadObject::New(&stFiberProc, this);
+ }
+
+ /**
+ * Create a ThreadObject fiber and attach current thread to it.
+ */
+ Fiber_Thread(void *param) :
+ m_thread(NULL),
+ m_proc(NULL),
+ m_param(param),
+ m_attached(true),
+ m_kill(false)
+ {
+ this->m_sem = ThreadSemaphore::New();
+ /* Attach the current thread to this Fiber */
+ this->m_thread = ThreadObject::AttachCurrent();
+ /* We are the current thread */
+ if (s_current == NULL) s_current = this;
+ if (s_main == NULL) s_main = this;
+ }
+
+ ~Fiber_Thread()
+ {
+ /* Remove the thread if needed */
+ if (this->m_thread != NULL) {
+ assert(this->m_attached || !this->m_thread->IsRunning());
+ delete this->m_thread;
+ }
+ /* Remove the semaphore */
+ delete this->m_sem;
+ }
+
+ /* virtual */ void SwitchToFiber()
+ {
+ /* You can't switch to yourself */
+ assert(s_current != this);
+ Fiber_Thread *cur = s_current;
+
+ /* Continue the execution of 'this' Fiber */
+ this->m_sem->Set();
+ /* Hold the execution of the current Fiber */
+ cur->m_sem->Wait();
+ if (this->m_kill) {
+ /* If the thread we switched too was killed, join it so it can finish quiting */
+ this->m_thread->Join();
+ }
+ /* If we continue, we are the current thread */
+ s_current = cur;
+ }
+
+ /* virtual */ void Exit()
+ {
+ /* Kill off our thread */
+ this->m_kill = true;
+ this->m_thread->Exit();
+ }
+
+ /* virtual */ bool IsRunning()
+ {
+ if (this->m_thread == NULL) return false;
+ return this->m_thread->IsRunning();
+ }
+
+ /* virtual */ void *GetFiberData()
+ {
+ return this->m_param;
+ }
+
+ static Fiber_Thread *GetCurrentFiber()
+ {
+ return s_current;
+ }
+
+private:
+ /**
+ * First function which is called within the fiber.
+ */
+ static void * CDECL stFiberProc(void *fiber)
+ {
+ Fiber_Thread *cur = (Fiber_Thread *)fiber;
+ /* Now suspend the thread until we get SwitchToFiber() for the first time */
+ cur->m_sem->Wait();
+ /* If we continue, we are the current thread */
+ s_current = cur;
+
+ try {
+ cur->m_proc(cur->m_param);
+ } catch (...) {
+ /* Unlock the main thread */
+ s_main->m_sem->Set();
+ throw;
+ }
+
+ return NULL;
+ }
+};
+
+/* Initialize the static member of Fiber_Thread */
+/* static */ Fiber_Thread *Fiber_Thread::s_current = NULL;
+/* static */ Fiber_Thread *Fiber_Thread::s_main = NULL;
+
+#ifndef WIN32
+
+/* static */ Fiber *Fiber::New(FiberFunc proc, void *param)
+{
+ return new Fiber_Thread(proc, param);
+}
+
+/* static */ Fiber *Fiber::AttachCurrent(void *param)
+{
+ return new Fiber_Thread(param);
+}
+
+/* static */ void *Fiber::GetCurrentFiberData()
+{
+ return Fiber_Thread::GetCurrentFiber()->GetFiberData();
+}
+
+#endif /* WIN32 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fiber_win32.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -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();
+}
--- a/src/genworld.cpp Tue Apr 01 13:45:04 2008 +0000
+++ b/src/genworld.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -84,7 +84,7 @@
/**
* The internal, real, generate function.
*/
-static void *_GenerateWorld(void *arg)
+static void * CDECL _GenerateWorld(void *arg)
{
_generating_world = true;
if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait...");
--- a/src/saveload.cpp Tue Apr 01 13:45:04 2008 +0000
+++ b/src/saveload.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -1561,7 +1561,7 @@
}
}
-static void* SaveFileToDiskThread(void *arg)
+static void * CDECL SaveFileToDiskThread(void *arg)
{
SaveFileToDisk(true);
return NULL;
--- a/src/thread.h Tue Apr 01 13:45:04 2008 +0000
+++ b/src/thread.h Wed Apr 02 10:55:28 2008 +0000
@@ -37,6 +37,16 @@
virtual bool WaitForStop() = 0;
/**
+ * Exit this thread.
+ */
+ virtual bool Exit() = 0;
+
+ /**
+ * Join this thread.
+ */
+ virtual void Join() = 0;
+
+ /**
* Check if this thread is the current active thread.
* @return True if it is the current active thread.
*/
--- a/src/thread_pthread.cpp Tue Apr 01 13:45:04 2008 +0000
+++ b/src/thread_pthread.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -117,6 +117,26 @@
return false;
}
+ /* virtual */ bool Exit()
+ {
+ /* You can only exit yourself */
+ assert(IsCurrent());
+ /* If the thread is not running, we are already closed */
+ if (!IsRunning()) return false;
+
+ /* For now we terminate by throwing an error, gives much cleaner cleanup */
+ throw 0;
+ }
+
+ /* virtual */ void Join()
+ {
+ /* You cannot join yourself */
+ assert(!IsCurrent());
+
+ pthread_join(m_thr, NULL);
+ m_thr = 0;
+ }
+
/* virtual */ bool IsCurrent()
{
return pthread_self() == m_thr;
@@ -134,7 +154,7 @@
*/
static void *stThreadProc(void *thr)
{
- return ((ThreadObject_pthread*)thr)->ThreadProc();
+ return ((ThreadObject_pthread *)thr)->ThreadProc();
}
/**
@@ -150,13 +170,11 @@
try {
m_proc(m_param);
} catch (...) {
- DEBUG(misc, 0, "Exception in thread %u!", GetId());
}
- /* The thread died, so we are no longer valid */
- m_thr = 0;
/* Notify threads waiting for our completion */
sem_post(&m_sem_stop);
+
return NULL;
}
};
--- a/src/thread_win32.cpp Tue Apr 01 13:45:04 2008 +0000
+++ b/src/thread_win32.cpp Wed Apr 02 10:55:28 2008 +0000
@@ -130,6 +130,25 @@
return res == WAIT_OBJECT_0;
}
+ /* virtual */ bool Exit()
+ {
+ /* You can only exit yourself */
+ assert(IsCurrent());
+ /* If the thread is not running, we are already closed */
+ if (!IsRunning()) return false;
+
+ /* For now we terminate by throwing an error, gives much cleaner cleanup */
+ throw 0;
+ }
+
+ /* virtual */ void Join()
+ {
+ /* You cannot join yourself */
+ assert(!IsCurrent());
+
+ WaitForSingleObject(m_h_thr, INFINITE);
+ }
+
/* virtual */ bool IsCurrent()
{
DWORD id_cur = GetCurrentThreadId();
@@ -148,7 +167,7 @@
*/
static uint CALLBACK stThreadProc(void *thr)
{
- return ((ThreadObject_Win32*)thr)->ThreadProc();
+ return ((ThreadObject_Win32 *)thr)->ThreadProc();
}
/**
@@ -160,7 +179,6 @@
try {
m_proc(m_param);
} catch (...) {
- DEBUG(misc, 0, "Exception in thread %d!", m_id_thr);
}
return 0;