src/ai/ai_threads.cpp
author KUDr
Fri, 23 Mar 2007 21:22:28 +0000
branchnoai
changeset 9514 e31710af1ca0
parent 9496 05ebee9884b3
child 9544 54705d209c44
permissions -rw-r--r--
(svn r9419) [NoAI] -Codechange: support AI threads also on Win32 (using threads on Win95 and fibers on other Win32 platforms)
/* $Id$ */

/** @file ai_threads.cpp handles the threading of AIs */

#include "../stdafx.h"
#include "../openttd.h"
#include "../variables.h"
#include "../player.h"
#include "../debug.h"
#include "../thread.h"
#include "ai.h"
#include "ai_threads.h"
#include "api/ai_controller.hpp"
#include "api/ai_object.hpp"
#include "../misc/autoptr.hpp"
#include "../misc/countedptr.hpp"
#include <map>

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
		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:
	bool           last_command_res; ///< The result of the last command

	/**
	 * 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).
	 */
protected:
	AIThread(PlayerID player, AIController *controller)
	{
		/* register new thread */
		s_threads[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;
		this->last_command_res = false;
	}

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);
	}

	/**
	 * 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.
	 */
protected:
	void ThreadProc()
	{
		assert(this->state == STARTING);
		this->state = RUNNING;

		this->controller->Start();

		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;
			AI_PlayerDied((PlayerID)this->fiber_id);
		} else {
			this->state = STOPPED;
		}
	}

	/**
	 * Suspend this AI.
	 * @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->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->ticks_to_sleep = timeout;

		/* 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 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 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
		 *  of the MP. Therefor we keep adding value to ticks_to_sleep till it
		 *  reaches -1, and we 'hang' it there infinitely, until the MP commands
		 *  comes in. In the case it happens sooner, the Resume() codes handles it
		 *  nicely and makes the value positive with the remaining ticks to wait. */
		if (this->ticks_to_sleep < -1) this->ticks_to_sleep++;
		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->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;
	}

	/**
	 * 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();
	}
};

/**
 * 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();
		}
	}

	/**
	 * 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*/


/**
 * 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!
 */
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();
	assert(thr != NULL);
	thr->Suspend(timeout);
}

/**
 * Run the thread 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);
	if (thr == NULL) {
		DEBUG(ai, 0, "AI_RunTick() called for dead AI player #%d", player);
		return;
	}
	thr->RunTick();
}

/**
 * Run an AI player for the first time.
 * @param player     the (AI) player to start
 * @param controller the actual AI
 */
void AI_StartPlayer(PlayerID player, AIController *controller)
{
	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();
}

/**
 * Stops the given player
 * @param player the (AI) player to stop
 */
void AI_StopPlayer(PlayerID player)
{
	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);
}

/**
 * Callback for when a network command was executed.
 *  As commands are always ordered, we don't have to worry about right command.
 */
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 */
	thr->last_command_res = success;

	/* Store some values inside the AIObject static memory */
	AIObject::SetNewVehicleID(_new_vehicle_id);

	thr->Resume();
}

/**
 * Returns the result of the last command which was received by the
 *  MultiPlayer Command Callback.
 * @param player the player to the callback information about
 * @return true if and only if the last sent command succeeded
 */
bool AI_GetCallbackResult(PlayerID player)
{
	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;
}