(svn r9419) [NoAI] -Codechange: support AI threads also on Win32 (using threads on Win95 and fibers on other Win32 platforms) noai
authorKUDr
Fri, 23 Mar 2007 21:22:28 +0000
branchnoai
changeset 9514 e31710af1ca0
parent 9513 258f78c74b0c
child 9515 0d70ff51a6e5
(svn r9419) [NoAI] -Codechange: support AI threads also on Win32 (using threads on Win95 and fibers on other Win32 platforms)
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/ai/ai_threads.cpp
src/misc/countedobj.cpp
src/misc/countedptr.hpp
src/thread.cpp
src/thread.h
--- a/projects/openttd.vcproj	Thu Mar 22 23:12:05 2007 +0000
+++ b/projects/openttd.vcproj	Fri Mar 23 21:22:28 2007 +0000
@@ -1226,6 +1226,9 @@
 				RelativePath=".\..\src\misc\blob.hpp">
 			</File>
 			<File
+				RelativePath=".\..\src\misc\countedobj.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\misc\countedptr.hpp">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Thu Mar 22 23:12:05 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Fri Mar 23 21:22:28 2007 +0000
@@ -1848,6 +1848,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\misc\countedobj.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\misc\countedptr.hpp"
 				>
 			</File>
--- a/source.list	Thu Mar 22 23:12:05 2007 +0000
+++ b/source.list	Fri Mar 23 21:22:28 2007 +0000
@@ -383,6 +383,7 @@
 misc/autoptr.hpp
 misc/binaryheap.hpp
 misc/blob.hpp
+misc/countedobj.cpp
 misc/countedptr.hpp
 misc/crc32.hpp
 misc/fixedsizearray.hpp
--- a/src/ai/ai_threads.cpp	Thu Mar 22 23:12:05 2007 +0000
+++ b/src/ai/ai_threads.cpp	Fri Mar 23 21:22:28 2007 +0000
@@ -12,12 +12,13 @@
 #include "ai_threads.h"
 #include "api/ai_controller.hpp"
 #include "api/ai_object.hpp"
-
-#include <pthread.h>
+#include "../misc/autoptr.hpp"
+#include "../misc/countedptr.hpp"
+#include <map>
 
-/** The overall state of an AI threading wise */
-class AIThreadState {
-	/** Different states of the AI thread */
+class AIThread : public SimpleCountedObject {
+public:
+	/** Different states of the AI thread. */
 	enum ThreadState {
 		INITIALIZED, ///< The mutex and conditions are initialized
 		STARTING,    ///< The thread is just started
@@ -27,43 +28,48 @@
 		STOPPED,     ///< The main AI has stopped
 	};
 
-	ThreadState state;        ///< The current state of the AI
-	OTTDThread *thread;       ///< The thread of the AI
-	pthread_mutex_t mutex;    ///< Synchronization mutex
-
-	pthread_cond_t ai_cont;   ///< Signal to let the AI continue
-	pthread_cond_t main_cont; ///< Signal to let the main continue
+	static const int MAIN_THREAD = -1;
 
-	PlayerID player_id;       ///< The ID of the current player
-	int ticks_to_sleep;       ///< Sleep this many runticks
-	AIController *controller; ///< Controller of this AI
+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:
-	bool last_command_res;    ///< The result of the last
-
-	/** Basic initialisation; real initialisation is done in Initialise. */
-	AIThreadState() : state(STOPPED) {}
+	bool           last_command_res; ///< The result of the last command
 
 	/**
-	 * Initialize this AI Thread State for the given controller.
-	 * @param player the player ID of this AI
-	 * @param controller the AI to run
-	 * @note should be called before any other function.
+	 * 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).
 	 */
-	void Initialise(PlayerID player, AIController *controller)
+protected:
+	AIThread(PlayerID player, AIController *controller)
 	{
-		assert(this->state == STOPPED);
-		this->thread = NULL;
-		this->state  = INITIALIZED;
+		/* register new thread */
+		s_threads[player] = this;
 
-		pthread_mutex_init(&this->mutex, NULL);
-		pthread_cond_init(&this->ai_cont,   NULL);
-		pthread_cond_init(&this->main_cont, NULL);
+		/* ensure that main thread has its wrapper too */
+		if (player != MAIN_THREAD) stFind(MAIN_THREAD);
 
-		this->player_id        = player;
-		this->ticks_to_sleep   = 0;
+		this->state = INITIALIZED;
+		this->fiber_id = player;
+		this->ticks_to_sleep = 0;
+		this->controller = controller;
 		this->last_command_res = false;
-		this->controller       = controller;
+	}
+
+public:
+	virtual ~AIThread()
+	{
+		/* at this point we should be already removed from the thread collection */
+		assert(this->fiber_id == MAIN_THREAD || stFind(this->fiber_id) == NULL);
 	}
 
 	/**
@@ -71,54 +77,29 @@
 	 * any other suspending command.
 	 * @note should be called after Initialize, but before any other function.
 	 */
-	void Start()
-	{
-		assert(this->state == INITIALIZED);
-		pthread_mutex_lock(&this->mutex);
-
-		this->state  = STARTING;
-		this->thread = OTTDCreateThread(&AIThreadState::stThreadRun, this);
-
-		/* XXX -- We should handle this more nicely */
-		assert(this->thread != NULL);
-
-		pthread_cond_wait(&this->main_cont, &this->mutex);
-		pthread_mutex_unlock(&this->mutex);
-	}
-
-	/**
-	* Function that is started as process of the new thread.
-	* @param arg some random argument, in this case a AIThreadState
-	*/
-	static void *stThreadRun(void *arg)
-	{
-		((AIThreadState*)arg)->ThreadRun();
-		return NULL;
-	}
+public:
+	virtual bool Start() = 0;
 
 	/**
 	 * The method that runs in the new Thread.
 	 * @note should be called by the newly spawned thread in Start.
 	 */
-	void ThreadRun()
+protected:
+	void ThreadProc()
 	{
 		assert(this->state == STARTING);
+		this->state = RUNNING;
 
-		this->state = RUNNING;
-		pthread_mutex_lock(&this->mutex);
 		this->controller->Start();
 
 		if (this->state != STOPPING) {
-			DEBUG(ai, 1, "We've got a suicidal AI for player %d", this->player_id);
+			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;
-			AI_PlayerDied(this->player_id);
+			AI_PlayerDied((PlayerID)this->fiber_id);
 		} else {
 			this->state = STOPPED;
 		}
-
-		pthread_cond_signal(&this->main_cont);
-		pthread_mutex_unlock(&this->mutex);
 	}
 
 	/**
@@ -126,44 +107,61 @@
 	 * @param timeout time to suspend. < 0 means infinite (MultiPlayer only!)
 	 * @note should be called from the AI thread.
 	 */
+public:
 	void Suspend(int timeout)
 	{
-		assert(this->state == RUNNING);
+		assert(this->fiber_id != MAIN_THREAD); // should not attempt to suspend main fiber
+		assert(this->state == RUNNING); // must be in running state
+		assert(this->CurrentThread() == this); // we can only suspend self
 
-		this->state = SUSPENDED;
 		this->ticks_to_sleep = timeout;
-		pthread_cond_signal(&this->main_cont);
 
-		/* Wait till we are resurrected */
-		pthread_cond_wait(&this->ai_cont, &this->mutex);
-		this->state = RUNNING;
+		/* when AI thread gets suspended, it switches always to main thread */
+		AIThread *main = stFind(MAIN_THREAD);
+		main->SwitchToThis(RUNNING);
+		if (this->state == STOPPING) {
+			this->controller->Stop();
+			OnStop();
+			//throw std::exception();
+			//NOT_REACHED();
+			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.
 	 */
+public:
 	void Resume()
 	{
 		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
-		 *  of ticks_to_sleep. But now it doesn't directly continue when the value
-		 *  was 'hanging', so we substract 1 and it all works fine. */
+		 * 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
+		 * of ticks_to_sleep. But now it doesn't directly continue when the value
+		 * was 'hanging', so we subtract 1 and it all works fine. */
 		this->ticks_to_sleep = -this->ticks_to_sleep - 1;
 	}
 
 	/**
 	 * Let the AI thread run for a while and return when it is done.
-	 * However, when the thread is suspened and the suspend timeout
+	 * 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.
 	 */
+public:
 	void RunTick()
 	{
+		/* should be called from main fiber only */
+		assert(CurrentThread() == stFind(MAIN_THREAD));
+		/* only AI fibers should be resumed this way */
+		assert(this != stFind(MAIN_THREAD));
+		/* AI fiber should be still suspended */
 		assert(this->state == SUSPENDED);
+
 		this->controller->IncreaseTick();
 
 		/* If the value is < -1, the user wants a delay which might exceed the delay
@@ -175,46 +173,636 @@
 		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
 
-		this->state = RUNNING;
-
-		/* Resurect the AI thread */
-		pthread_mutex_lock(&this->mutex);
-		pthread_cond_signal(&this->ai_cont);
-
-		/* Wait till the AI thread has finished */
-		pthread_cond_wait(&this->main_cont, &this->mutex);
-		pthread_mutex_unlock(&this->mutex);
+		this->SwitchToThis(RUNNING);
 	}
 
 	/**
 	 * Stop the AI thread and wait till it is stopped.
 	 * @note should NOT be called from the AI thread.
 	 */
-	void Stop()
-	{
-		/* The AI stopped itself */
-		if (this->state == STOPPED) return;
-
-		assert(this->state == SUSPENDED);
+public:
+	virtual void Stop() = 0;
 
-		this->state = STOPPING;
-		this->controller->Stop();
+	/**
+	 * Called before the AI thread finishes. Should wakeup the main thread.
+	 */
+public:
+	virtual void OnStop() {};
 
-		/* Resurect the AI thread */
-		pthread_mutex_lock(&this->mutex);
-		pthread_cond_signal(&this->ai_cont);
-		pthread_mutex_unlock(&this->mutex);
+	/**
+	 * 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;
 
-		/* Wait till the AI thread has finished */
-		OTTDJoinThread(this->thread);
-		this->state = STOPPED;
+	/**
+	 * 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;
+	}
+
+	/**
+	 * 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.
+	 */
+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<AutoResetEvent> 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 = AutoResetEvent::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::Current();
+		} else {
+			/* create new Thread Object */
+			this->thr = ThreadObject::New();
+		}
+	}
+
+	/**
+	 * Destructor (AI Thread Object cleanup).
+	 */
+public:
+	/*virtual*/ ~AIThread_MT()
+	{
+		DEBUG(ai, 3, "-AIThread_MT(%d) from thr %u", this->fiber_id, ThreadObject::CurrentId());
+		bool wait_ok = this->thr->IsCurrent() ? true : this->thr->WaitForStop();
+		assert(wait_ok);
+		this->evt_go.Release();
+	}
+
+	/**
+	 * 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(!this->thr->IsRunning()); // not yet started
+		assert(fiber_id != MAIN_THREAD); // main fiber can't be started this way
+
+		cur->state = SUSPENDED;
+		this->next_state = STARTING;
+
+		{
+			/* 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;
+			if (!this->thr->Begin(&stThreadProc, this)) return false; // unable to create thread
+			cur->EnterWaitState();
+		} // end of 'protection area'
+
+		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();
+	}
+
+	/**
+	 * 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);
+	}
+
+	/**
+	 * Implementation specific way how to stop execution of the AI thread.
+	 * @see AIThread::Stop()
+	 */
+public:
+	/*virtual*/ void Stop()
+	{
+		AIThread_MT *cur = stCurrentFiber();
+		/* The AI stopping itself */
+		if (cur == this) {
+			/* must be AI fiber */
+			assert(this->fiber_id != MAIN_THREAD);
+
+			DEBUG(ai, 3, "this->OnStop(%d) from thr %d", this->fiber_id, ThreadObject::CurrentId());
+			this->state = STOPPING;
+			this->controller->Stop();
+			this->OnStop();
+			//throw std::exception();
+			//NOT_REACHED();
+			return;
+		}
+
+		assert(cur->fiber_id == MAIN_THREAD);
+		assert(this->state == SUSPENDED);
+		this->SwitchToThis(STOPPING);
+	}
+
+	/**
+	 * This routine should notify main thread that AI thread finished (stopped
+	 * forever).
+	 */
+protected:
+	/*virtual*/ void OnStop()
+	{
+		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;
+
+		{
+			/* 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;
+	}
+
+	/**
+	 * Ugly way how to provide stCurrentFiber() functionality for the base class.
+	 */
+protected:
+	/*virtual*/ AIThread* CurrentThread()
+	{
+		return stCurrentFiber();
 	}
 };
 
 /**
- * All AI Thread states.
+ * 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 AIThreadState _ai_state[MAX_PLAYERS];
+ /* 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();
+		}
+	}
+
+	/**
+	 * Fiber specific Start() routine.
+	 * @see AIThread::Start()
+	 */
+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()
+	{
+		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 = STOPPING;
+			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);
+
+		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();
+		assert(cur != NULL);
+		return cur;
+	}
+
+	/**
+	 * Ugly way how to provide stCurrentFiber() functionality for the base class.
+	 */
+public:
+	/*virtual*/ AIThread* CurrentThread()
+	{
+		return stCurrentFiber();
+	}
+
+
+	/**
+	 * Function that is started as process of the new AI thread (fiber).
+	 * @param fiber AIThread_Fiber* to the fiber that starts
+	 */
+protected:
+	static VOID CALLBACK stFiberProc(LPVOID fiber)
+	{
+		AIThread_Fiber *cur = (AIThread_Fiber*)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
+	 */
+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()
+	{
+		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);
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * 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;
+	}
+};
+
+/**
+ * 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*/
 
 
 /**
@@ -225,7 +813,10 @@
  */
 void AI_SuspendPlayer(PlayerID player, int timeout)
 {
-	_ai_state[player].Suspend(timeout);
+	DEBUG(ai, 5, "AI_SuspendPlayer(%d, %d) from thr %d", player, timeout, ThreadObject::CurrentId());
+	AIThread *thr = AIThread::stCurrentThread();
+	assert(thr != NULL);
+	thr->Suspend(timeout);
 }
 
 /**
@@ -234,7 +825,13 @@
  */
 void AI_RunTick(PlayerID player)
 {
-	_ai_state[player].RunTick();
+	DEBUG(ai, 6, "AI_RunTick(%d) from thr %d", player, ThreadObject::CurrentId());
+	AIThread *thr = AIThread::stFind(player);
+	if (thr == NULL) {
+		DEBUG(ai, 0, "AI_RunTick() called for dead AI player #%d", player);
+		return;
+	}
+	thr->RunTick();
 }
 
 /**
@@ -244,8 +841,11 @@
  */
 void AI_StartPlayer(PlayerID player, AIController *controller)
 {
-	_ai_state[player].Initialise(player, controller);
-	_ai_state[player].Start();
+	DEBUG(ai, 3, "AI_StartPlayer(%d) from thr %d", player, ThreadObject::CurrentId());
+	AIThread *thr = AIThread::stFind(player);
+	assert(thr == NULL);
+	thr = AIThread::stNew(player, controller);
+	thr->Start();
 }
 
 /**
@@ -254,7 +854,11 @@
  */
 void AI_StopPlayer(PlayerID player)
 {
-	_ai_state[player].Stop();
+	DEBUG(ai, 3, "AI_StopPlayer(%d) from thr %d", player, ThreadObject::CurrentId());
+	AIThread *thr = AIThread::stFind(player);
+	if (thr == NULL) return;
+	thr->Stop();
+	AIThread::stRelease(player);
 }
 
 /**
@@ -263,13 +867,16 @@
  */
 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);
+	assert(thr != NULL);
 	/* Store if we were a success or not and resume */
-	_ai_state[_current_player].last_command_res = success;
+	thr->last_command_res = success;
 
 	/* Store some values inside the AIObject static memory */
 	AIObject::SetNewVehicleID(_new_vehicle_id);
 
-	_ai_state[_current_player].Resume();
+	thr->Resume();
 }
 
 /**
@@ -280,5 +887,8 @@
  */
 bool AI_GetCallbackResult(PlayerID player)
 {
-	return _ai_state[player].last_command_res;
+	DEBUG(ai, 6, "AI_GetCallbackResult(%d) from thr %d", player, ThreadObject::CurrentId());
+	AIThread *thr = AIThread::stFind(player);
+	assert(thr != NULL);
+	return thr->last_command_res;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc/countedobj.cpp	Fri Mar 23 21:22:28 2007 +0000
@@ -0,0 +1,22 @@
+/* $Id$ */
+
+#include "../stdafx.h"
+
+#include "countedptr.hpp"
+
+int32 SimpleCountedObject::AddRef()
+{
+	return ++m_ref_cnt;
+}
+
+int32 SimpleCountedObject::Release()
+{
+	int32 res = --m_ref_cnt;
+	assert(res >= 0);
+	if (res == 0) {
+		FinalRelease();
+		delete this;
+	}
+	return res;
+}
+
--- a/src/misc/countedptr.hpp	Thu Mar 22 23:12:05 2007 +0000
+++ b/src/misc/countedptr.hpp	Fri Mar 23 21:22:28 2007 +0000
@@ -3,7 +3,7 @@
 #ifndef COUNTEDPTR_HPP
 #define COUNTEDPTR_HPP
 
-#if 0 // reenable when needed
+#if 1 // reenable when needed
 /** @file CCountedPtr - smart pointer implementation */
 
 /** CCountedPtr - simple reference counting smart pointer.
@@ -42,7 +42,7 @@
 
 public:
 	/** release smart pointer (and decrement ref count) if not null */
-	FORCEINLINE void Release() {if (m_pT != NULL) {m_pT->Release(); m_pT = NULL;}}
+	FORCEINLINE void Release() {if (m_pT != NULL) {Tcls* pT = m_pT; m_pT = NULL; pT->Release();}}
 
 	/** dereference of smart pointer - const way */
 	FORCEINLINE const Tcls* operator -> () const {assert(m_pT != NULL); return m_pT;};
@@ -54,7 +54,7 @@
 	FORCEINLINE operator const Tcls*() const {assert(m_pT == NULL); return m_pT;}
 
 	/** raw pointer casting operator - non-const way */
-	FORCEINLINE operator Tcls*() {assert(m_pT == NULL); return m_pT;}
+	FORCEINLINE operator Tcls*() {return m_pT;}
 
 	/** operator & to support output arguments */
 	FORCEINLINE Tcls** operator &() {assert(m_pT == NULL); return &m_pT;}
@@ -63,7 +63,7 @@
 	FORCEINLINE CCountedPtr& operator = (Tcls* pT) {Assign(pT); return *this;}
 
 	/** assignment operator from another smart ptr */
-	FORCEINLINE CCountedPtr& operator = (CCountedPtr& src) {Assign(src.m_pT); return *this;}
+	FORCEINLINE CCountedPtr& operator = (const CCountedPtr& src) {Assign(src.m_pT); return *this;}
 
 	/** assignment operator helper */
 	FORCEINLINE void Assign(Tcls* pT);
@@ -72,10 +72,10 @@
 	FORCEINLINE bool IsNull() const {return m_pT == NULL;}
 
 	/** another way how to test for NULL value */
-	FORCEINLINE bool operator == (const CCountedPtr& sp) const {return m_pT == sp.m_pT;}
+	//FORCEINLINE bool operator == (const CCountedPtr& sp) const {return m_pT == sp.m_pT;}
 
 	/** yet another way how to test for NULL value */
-	FORCEINLINE bool operator != (const CCountedPtr& sp) const {return m_pT != sp.m_pT;}
+	//FORCEINLINE bool operator != (const CCountedPtr& sp) const {return m_pT != sp.m_pT;}
 
 	/** assign pointer w/o incrementing ref count */
 	FORCEINLINE void Attach(Tcls* pT) {Release(); m_pT = pT;}
@@ -96,5 +96,65 @@
 	}
 }
 
+/**
+ * Adapter wrapper for CCountedPtr like classes that can't be used directly by stl
+ * collections as item type. For example CCountedPtr has overloaded operator & which
+ * prevents using CCountedPtr in stl collections (i.e. std::list<CCountedPtr<MyType> >)
+ */
+template <class T> struct AdaptT {
+	T m_t;
+
+	/** construct by wrapping the given object */
+	AdaptT(const T &t)
+		: m_t(t)
+	{}
+
+	/** assignment operator */
+	T& operator = (const T &t)
+	{
+		m_t = t;
+		return t;
+	}
+
+	/** type-cast operator (used when AdaptT is used instead of T) */
+	operator T& ()
+	{
+		return m_t;
+	}
+
+	/** const type-cast operator (used when AdaptT is used instead of const T) */
+	operator const T& () const
+	{
+		return m_t;
+	}
+};
+
+
+/** Simple counted object. Use it as base of your struct/class if you want to use
+ *  basic reference counting. Your struct/class will destroy and free itself when
+ *  last reference to it is released (using Relese() method). The initial reference
+ *  count (when it is created) is zero (don't forget AddRef() at least one time if
+ *  not using CCountedPtr<T>.
+ *
+ *  @see misc/countedobj.cpp for implementation.
+ */
+struct SimpleCountedObject {
+	int32 m_ref_cnt;
+
+	SimpleCountedObject()
+		: m_ref_cnt(0)
+	{}
+
+	virtual ~SimpleCountedObject()
+	{};
+
+	virtual int32 AddRef();
+	virtual int32 Release();
+	virtual void FinalRelease() {};
+};
+
+
+
+
 #endif /* 0 */
 #endif /* COUNTEDPTR_HPP */
--- a/src/thread.cpp	Thu Mar 22 23:12:05 2007 +0000
+++ b/src/thread.cpp	Fri Mar 23 21:22:28 2007 +0000
@@ -2,6 +2,7 @@
 
 #include "stdafx.h"
 #include "thread.h"
+#include "debug.h"
 #include <stdlib.h>
 #include "helpers.hpp"
 
@@ -66,6 +67,8 @@
 #elif defined(UNIX)
 
 #include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>
 
 struct OTTDThread {
 	pthread_t thread;
@@ -101,18 +104,175 @@
 	pthread_exit(NULL);
 }
 
+
+/**
+ * C++ thread wrapper - posix pthread version
+ */
+struct ThreadObject_pthread : public ThreadObject {
+	pthread_t m_thr;             ///< system thread identifier
+	void (CDECL *m_proc)(void*); ///< external thread procedure
+	void      *m_param;          ///< parameter for the external thread procedure
+	bool      m_attached;        ///< true if the ThreadObject was attached to an existing thread
+	sem_t     m_sem_start;       ///< here the new thread waits before it starts
+	sem_t     m_sem_stop;        ///< here the other thread can wait for this thread to end
+
+	ThreadObject_pthread()
+		: m_thr(0)
+		, m_proc(NULL)
+		, m_param(NULL)
+		, m_attached(false)
+	{
+		sem_init(&m_sem_start, 0, 0);
+		sem_init(&m_sem_stop, 0, 0);
+	}
+
+	/** Virtual destructor to allow 'delete' operator to work properly. */
+	/*virtual*/ ~ThreadObject_pthread()
+	{
+		sem_destroy(&m_sem_stop);
+		sem_destroy(&m_sem_start);
+	};
+
+	/** Returns true if thread is running. */
+	/*virtual*/ bool IsRunning()
+	{
+		return m_thr != 0;
+	}
+
+	/** Waits for the thread exit. Returns true if thread exited. */
+	/*virtual*/ bool WaitForStop()
+	{
+		assert(!IsCurrent());
+		int ret = sem_wait(&m_sem_stop);
+		if (ret == 0) {
+			/* we have passed semaphore so increment it again */
+			sem_post(&m_sem_stop);
+			return true;
+		}
+		return false;
+	}
+
+	/** Returns true if this Thread Object belongs to the current (calling) thread. */
+	/*virtual*/ bool IsCurrent()
+	{
+		return pthread_self() == m_thr;
+	}
+
+	/** Returns thread id of the thread associated to this Thread Object. */
+	/*virtual*/ uint Id()
+	{
+		return (uint)m_thr;
+	}
+
+	/** Begin thread execution by calling given procedure and passing given parameter to it. */
+	/*virtual*/ bool Begin(void (CDECL *proc)(void*), void *param)
+	{
+		m_proc = proc;
+		m_param = param;
+		pthread_create(&m_thr, NULL, &stThreadProc, this);
+		sem_post(&m_sem_start);
+		return true;
+	}
+
+	/** Thread procedure that is called by new thread. */
+	static void* stThreadProc(void *thr)
+	{
+		return ((ThreadObject_pthread*)thr)->ThreadProc();
+	}
+
+	/** Thread method that is called by new thread. */
+	void* ThreadProc()
+	{
+		/* the new thread stops here so the calling thread can complete pthread_create() call */
+		sem_wait(&m_sem_start);
+		try {
+			m_proc(m_param);
+		} catch (...) {
+			DEBUG(misc, 0, "Exception in thread %u!", Id());
+		}
+		m_thr = 0;
+		/* notify threads waiting for our completion */
+		sem_post(&m_sem_stop);
+		return NULL;
+	}
+};
+
+/** Used to construct new instance of thread object. */
+/*static*/ ThreadObject* ThreadObject::New()
+{
+	ThreadObject_pthread *thr = new ThreadObject_pthread();
+	return thr;
+}
+
+/** Returns thread object instance that belongs to the current (calling) thread. */
+/*static*/ ThreadObject* ThreadObject::Current()
+{
+	ThreadObject_pthread *thr = new ThreadObject_pthread();
+	thr->m_thr = pthread_self();
+	thr->m_attached = true;
+	return thr;
+}
+
+/** Returns thread id of the current (calling) thread. */
+/*static*/ uint ThreadObject::CurrentId()
+{
+	return (uint)pthread_self();
+}
+
+
+/**
+ * Event object that is signaled manually by Set() and reset automatically when thread passes Wait().
+ */
+struct AutoResetEvent_pthread : public AutoResetEvent {
+	sem_t m_sem;
+
+	AutoResetEvent_pthread()
+	{
+		sem_init(&m_sem, 0, 0);
+	}
+
+	/** Virtual destructor to allow 'delete' operator to work properly. */
+	/*virtual*/ ~AutoResetEvent_pthread()
+	{
+		sem_destroy(&m_sem);
+	}
+
+	/** Set the event state to signaled so that one thread can pass the Wait() method. */
+	/*virtual*/ void Set()
+	{
+		int val = 0;
+		if (sem_getvalue(&m_sem, &val) == 0 && val == 0) sem_post(&m_sem);
+	}
+
+	/** Wait until event is signaled by calling Set() */
+	/*virtual*/ void Wait()
+	{
+		sem_wait(&m_sem);
+	}
+
+};
+
+/** Used to construct new instance of event object. */
+/*static*/ AutoResetEvent* AutoResetEvent::New()
+{
+	AutoResetEvent_pthread *evt = new AutoResetEvent_pthread();
+	return evt;
+}
+
 #elif defined(WIN32)
 
 #include <windows.h>
+#include <process.h>
 
 struct OTTDThread {
 	HANDLE thread;
+	uint   thread_id;
 	OTTDThreadFunc func;
 	void* arg;
 	void* ret;
 };
 
-static DWORD WINAPI Proxy(LPVOID arg)
+static unsigned WINAPI Proxy(LPVOID arg)
 {
 	OTTDThread* t = (OTTDThread*)arg;
 	t->ret = t->func(t->arg);
@@ -122,13 +282,12 @@
 OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 {
 	OTTDThread* t = MallocT<OTTDThread>(1);
-	DWORD dwThreadId;
 
 	if (t == NULL) return NULL;
 
 	t->func = function;
 	t->arg  = arg;
-	t->thread = CreateThread(NULL, 0, Proxy, t, 0, &dwThreadId);
+	t->thread = (HANDLE)_beginthreadex(NULL, 0, Proxy, t, 0, &t->thread_id);
 
 	if (t->thread != NULL) {
 		return t;
@@ -153,6 +312,168 @@
 
 void OTTDExitThread()
 {
-	ExitThread(0);
+	_endthreadex(0);
 }
+
+/**
+ * C++ thread wrapper interface
+ */
+struct ThreadObject_Win32 : public ThreadObject {
+	uint     m_id_thr;
+	HANDLE   m_h_thr;
+	void (CDECL *m_proc)(void*);
+	void     *m_param;
+	bool     m_attached;
+
+	ThreadObject_Win32()
+		: m_id_thr(0)
+		, m_h_thr(NULL)
+		, m_proc(NULL)
+		, m_param(NULL)
+		, m_attached(false)
+	{}
+
+	/** Virtual destructor to allow 'delete' operator to work properly. */
+	/*virtual*/ ~ThreadObject_Win32()
+	{
+		if (m_h_thr != NULL) {
+			assert(m_attached || !IsRunning());
+			CloseHandle(m_h_thr);
+			m_h_thr = NULL;
+		}
+	}
+
+	/** Returns true if thread is running. */
+	/*virtual*/ bool IsRunning()
+	{
+		if (m_h_thr == NULL) return false;
+		DWORD exit_code = 0;
+		if (!GetExitCodeThread(m_h_thr, &exit_code)) return false;
+		return (exit_code == STILL_ACTIVE);
+	}
+
+	/** Waits for the thread exit. Returns true if thread exited. */
+	virtual bool WaitForStop()
+	{
+		assert(!IsCurrent());
+		if (!IsRunning()) return true;
+		DWORD res = WaitForSingleObject(m_h_thr, INFINITE);
+		return res == WAIT_OBJECT_0;
+	}
+
+	/** Returns true if this Thread Object belongs to the current (calling) thread. */
+	/*virtual*/ bool IsCurrent()
+	{
+		DWORD id_cur = GetCurrentThreadId();
+		return id_cur == m_id_thr;
+	}
+
+	/** Returns thread id of the thread associated to this Thread Object. */
+	/*virtual*/ uint Id()
+	{
+		return m_id_thr;
+	}
+
+	/** Begin thread execution by calling given procedure and passing given parameter to it. */
+	/*virtual*/ bool Begin(void (CDECL *proc)(void*), void *param)
+	{
+		assert(m_attached || !IsRunning());
+		m_attached = false;
+		m_proc = proc;
+		m_param = param;
+		m_h_thr = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &m_id_thr);
+		if (m_h_thr == NULL) return false;
+		ResumeThread(m_h_thr);
+		return true;
+	}
+
+	/** Thread procedure that is called by new thread. */
+	static uint CALLBACK stThreadProc(void *thr)
+	{
+		return ((ThreadObject_Win32*)thr)->ThreadProc();
+	}
+
+	/** Thread method that is called by new thread. */
+	uint ThreadProc()
+	{
+		uint id_thr = m_id_thr;
+		try {
+			m_proc(m_param);
+		} catch (...) {
+			DEBUG(misc, 0, "Exception in thread %d!", id_thr);
+		}
+		return 0;
+	}
+
+	/** Associates the current (caller) thread with this Thread Object. */
+	bool AttachCurrent()
+	{
+		assert(m_h_thr == NULL);
+		BOOL ret = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &m_h_thr, 0, FALSE, DUPLICATE_SAME_ACCESS);
+		if (!ret) return false;
+		m_id_thr = GetCurrentThreadId();
+		m_attached = true;
+		return true;
+	}
+};
+
+/** Used to construct new instance of thread object. */
+/*static*/ ThreadObject* ThreadObject::New()
+{
+	ThreadObject_Win32 *thr = new ThreadObject_Win32;
+	return thr;
+}
+
+/** Returns thread object instance that belongs to the current (calling) thread. */
+/*static*/ ThreadObject* ThreadObject::Current()
+{
+	ThreadObject_Win32 *thr = new ThreadObject_Win32;
+	if (!thr->AttachCurrent()) {
+		/* attach failed */
+		delete thr;
+		return NULL;
+	}
+	return thr;
+}
+
+/** Returns thread id of the current (calling) thread. */
+/*static*/ uint ThreadObject::CurrentId()
+{
+	return GetCurrentThreadId();
+}
+
+
+
+/**
+ * Event object that is signaled manually by Set() and reset automatically when thread passes Wait().
+ */
+struct AutoResetEvent_Win32 : public AutoResetEvent {
+	HANDLE m_handle;
+
+	AutoResetEvent_Win32()
+	{
+		m_handle = ::CreateEvent(NULL, FALSE, FALSE, NULL);
+	}
+
+	virtual ~AutoResetEvent_Win32()
+	{
+		::CloseHandle(m_handle);
+	}
+
+	virtual void Set()
+	{
+		::SetEvent(m_handle);
+	}
+
+	virtual void Wait()
+	{
+		::WaitForSingleObject(m_handle, INFINITE);
+	}
+};
+
+/*static*/ AutoResetEvent* AutoResetEvent::New()
+{
+	return new AutoResetEvent_Win32();
+}
+
 #endif
--- a/src/thread.h	Thu Mar 22 23:12:05 2007 +0000
+++ b/src/thread.h	Fri Mar 23 21:22:28 2007 +0000
@@ -11,4 +11,44 @@
 void*       OTTDJoinThread(OTTDThread*);
 void        OTTDExitThread();
 
+
+/**
+ * C++ thread wrapper interface
+ */
+struct ThreadObject {
+	/** Virtual destructor to allow 'delete' operator to work properly. */
+	virtual ~ThreadObject() {};
+	/** Returns true if thread is running. */
+	virtual bool IsRunning() = 0;
+	/** Waits for the thread exit. Returns true if thread exited. */
+	virtual bool WaitForStop() = 0;
+	/** Returns true if this Thread Object belongs to the current (calling) thread. */
+	virtual bool IsCurrent() = 0;
+	/** Returns thread id of the thread associated to this Thread Object. */
+	virtual uint Id() = 0;
+	/** Begin thread execution by calling given procedure and passing given parameter to it. */
+	virtual bool Begin(void (CDECL *proc)(void*), void *param) = 0;
+	/** Used to construct new instance of thread object. */
+	static ThreadObject* New();
+	/** Returns thread object instance that belongs to the current (calling) thread. */
+	static ThreadObject* Current();
+	/** Returns thread id of the current (calling) thread. */
+	static uint CurrentId();
+};
+
+/**
+ * Event object that is signaled manually by Set() and reset automatically when thread passes Wait().
+ */
+struct AutoResetEvent {
+	/** Virtual destructor to allow 'delete' operator to work properly. */
+	virtual ~AutoResetEvent() {};
+	/** Set the event state to signaled so that one thread can pass the Wait() method. */
+	virtual void Set() = 0;
+	/** Wait until event is signaled by calling Set() */
+	virtual void Wait() = 0;
+	/** Used to construct new instance of event object. */
+	static AutoResetEvent* New();
+};
+
+
 #endif /* THREAD_H */