src/thread.cpp
author KUDr
Sun, 25 Mar 2007 23:39:05 +0000
branchnoai
changeset 9544 54705d209c44
parent 9514 e31710af1ca0
child 9574 698395509d12
permissions -rw-r--r--
(svn r9466) [NoAI] -Fix: main AI thread waking up triggered too early when AI stopped (TrueBrain, Rubidium)
/* $Id$ */

#include "stdafx.h"
#include "thread.h"
#include "debug.h"
#include <stdlib.h>
#include "helpers.hpp"

#if defined(__AMIGA__) || defined(__MORPHOS__) || defined(PSP) || defined(NO_THREADS)
OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg) { return NULL; }
void *OTTDJoinThread(OTTDThread *t) { return NULL; }
void OTTDExitThread() { NOT_REACHED(); };

#elif defined(__OS2__)

#define INCL_DOS
#include <os2.h>
#include <process.h>

struct OTTDThread {
	TID thread;
	OTTDThreadFunc func;
	void* arg;
	void* ret;
};

static void Proxy(void* arg)
{
	OTTDThread* t = (OTTDThread*)arg;
	t->ret = t->func(t->arg);
}

OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
{
	OTTDThread* t = MallocT<OTTDThread>(1);

	if (t == NULL) return NULL;

	t->func = function;
	t->arg  = arg;
	t->thread = _beginthread(Proxy, NULL, 32768, t);
	if (t->thread != (TID)-1) {
		return t;
	} else {
		free(t);
		return NULL;
	}
}

void* OTTDJoinThread(OTTDThread* t)
{
	void* ret;

	if (t == NULL) return NULL;

	DosWaitThread(&t->thread, DCWW_WAIT);
	ret = t->ret;
	free(t);
	return ret;
}

void OTTDExitThread()
{
	_endthread();
}

#elif defined(UNIX)

#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

struct OTTDThread {
	pthread_t thread;
};

OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
{
	OTTDThread* t = MallocT<OTTDThread>(1);

	if (t == NULL) return NULL;

	if (pthread_create(&t->thread, NULL, function, arg) == 0) {
		return t;
	} else {
		free(t);
		return NULL;
	}
}

void* OTTDJoinThread(OTTDThread* t)
{
	void* ret;

	if (t == NULL) return NULL;

	pthread_join(t->thread, &ret);
	free(t);
	return ret;
}

void OTTDExitThread()
{
	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 unsigned WINAPI Proxy(LPVOID arg)
{
	OTTDThread* t = (OTTDThread*)arg;
	t->ret = t->func(t->arg);
	return 0;
}

OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
{
	OTTDThread* t = MallocT<OTTDThread>(1);

	if (t == NULL) return NULL;

	t->func = function;
	t->arg  = arg;
	t->thread = (HANDLE)_beginthreadex(NULL, 0, Proxy, t, 0, &t->thread_id);

	if (t->thread != NULL) {
		return t;
	} else {
		free(t);
		return NULL;
	}
}

void* OTTDJoinThread(OTTDThread* t)
{
	void* ret;

	if (t == NULL) return NULL;

	WaitForSingleObject(t->thread, INFINITE);
	CloseHandle(t->thread);
	ret = t->ret;
	free(t);
	return ret;
}

void OTTDExitThread()
{
	_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) {
			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