(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