(svn r12528) [NoAI] -Change: rewrote most of the internal ThreadObject to make it a bit more readable
[NoAI] -Change: when a ThreadObject is created, the thread is really created, and not delayed until some sub-function is called
[NoAI] -Change: renamed AutoResetEvent to ThreadSemaphore, as that is what is really is.. AutoResetEvent didn't really give a clue what it was ;)
/* $Id$ */
/** @file thread.cpp */
#include "stdafx.h"
#include "thread.h"
#include "debug.h"
#include "core/alloc_func.hpp"
#include <stdlib.h>
#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);
}
/**
* POSIX pthread version for ThreadObject.
*/
class ThreadObject_pthread : public ThreadObject {
private:
pthread_t m_thr; ///< System thread identifier.
OTTDThreadFunc m_proc; ///< 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.
public:
/**
* Create a pthread and start it, calling proc(param).
*/
ThreadObject_pthread(OTTDThreadFunc proc, void *param) :
m_thr(0),
m_proc(proc),
m_param(param),
m_attached(false)
{
sem_init(&m_sem_start, 0, 0);
sem_init(&m_sem_stop, 0, 0);
pthread_create(&m_thr, NULL, &stThreadProc, this);
sem_post(&m_sem_start);
}
/**
* Create a pthread and attach current thread to it.
*/
ThreadObject_pthread() :
m_thr(0),
m_proc(NULL),
m_param(0),
m_attached(true)
{
sem_init(&m_sem_start, 0, 0);
sem_init(&m_sem_stop, 0, 0);
m_thr = pthread_self();
}
/* virtual */ ~ThreadObject_pthread()
{
sem_destroy(&m_sem_stop);
sem_destroy(&m_sem_start);
};
/* virtual */ bool IsRunning()
{
return m_thr != 0;
}
/* virtual */ bool WaitForStop()
{
/* You can't wait on yourself */
assert(!IsCurrent());
/* If the thread is not running, waiting is over */
if (!IsRunning()) return true;
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;
}
/* virtual */ bool IsCurrent()
{
return pthread_self() == m_thr;
}
/* virtual */ uint GetId()
{
return (uint)m_thr;
}
private:
/**
* On thread creation, this function is called, which calls the real startup
* function. This to get back into the correct instance again.
*/
static void *stThreadProc(void *thr)
{
return ((ThreadObject_pthread*)thr)->ThreadProc();
}
/**
* A new thread is created, and this function is called. Call the custom
* function of the creator of the thread.
*/
void *ThreadProc()
{
/* The new thread stops here so the calling thread can complete pthread_create() call */
sem_wait(&m_sem_start);
/* Call the proc of the creator to continue this thread */
try {
m_proc(m_param);
} catch (...) {
DEBUG(misc, 0, "Exception in thread %u!", GetId());
}
/* The thread died, so we are no longer valid */
m_thr = 0;
/* Notify threads waiting for our completion */
sem_post(&m_sem_stop);
return NULL;
}
};
/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
{
return new ThreadObject_pthread(proc, param);
}
/* static */ ThreadObject *ThreadObject::AttachCurrent()
{
return new ThreadObject_pthread();
}
/* static */ uint ThreadObject::CurrentId()
{
return (uint)pthread_self();
}
/**
* POSIX pthread version of ThreadSemaphore.
*/
struct ThreadSemaphore_pthread : public ThreadSemaphore {
private:
sem_t m_sem;
public:
ThreadSemaphore_pthread()
{
sem_init(&m_sem, 0, 0);
}
/* virtual */ ~ThreadSemaphore_pthread()
{
sem_destroy(&m_sem);
}
/* virtual */ void Set()
{
int val = 0;
if (sem_getvalue(&m_sem, &val) == 0 && val == 0) sem_post(&m_sem);
}
/* virtual */ void Wait()
{
sem_wait(&m_sem);
}
};
/* static */ ThreadSemaphore *ThreadSemaphore::New()
{
return new ThreadSemaphore_pthread();
}
#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);
}
/**
* Win32 thread version for ThreadObject.
*/
struct ThreadObject_Win32 : public ThreadObject {
uint m_id_thr;
HANDLE m_h_thr;
OTTDThreadFunc m_proc;
void *m_param;
bool m_attached;
/**
* Create a win32 thread and start it, calling proc(param).
*/
ThreadObject_Win32(OTTDThreadFunc proc, void *param) :
m_id_thr(0),
m_h_thr(NULL),
m_proc(proc),
m_param(param),
m_attached(false)
{
m_h_thr = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &m_id_thr);
if (m_h_thr == NULL) return;
ResumeThread(m_h_thr);
}
/**
* Create a win32 thread and attach current thread to it.
*/
ThreadObject_Win32() :
m_id_thr(0),
m_h_thr(NULL),
m_proc(NULL),
m_param(NULL),
m_attached(false)
{
BOOL ret = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &m_h_thr, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (!ret) return;
m_id_thr = GetCurrentThreadId();
}
/* virtual */ ~ThreadObject_Win32()
{
if (m_h_thr != NULL) {
CloseHandle(m_h_thr);
m_h_thr = NULL;
}
}
/* 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);
}
/* virtual */ bool WaitForStop()
{
/* You can't wait on yourself */
assert(!IsCurrent());
/* If the thread is not running, waiting is over */
if (!IsRunning()) return true;
DWORD res = WaitForSingleObject(m_h_thr, INFINITE);
return res == WAIT_OBJECT_0;
}
/* virtual */ bool IsCurrent()
{
DWORD id_cur = GetCurrentThreadId();
return id_cur == m_id_thr;
}
/* virtual */ uint GetId()
{
return m_id_thr;
}
private:
/**
* On thread creation, this function is called, which calls the real startup
* function. This to get back into the correct instance again.
*/
static uint CALLBACK stThreadProc(void *thr)
{
return ((ThreadObject_Win32*)thr)->ThreadProc();
}
/**
* A new thread is created, and this function is called. Call the custom
* function of the creator of the thread.
*/
uint ThreadProc()
{
try {
m_proc(m_param);
} catch (...) {
DEBUG(misc, 0, "Exception in thread %d!", m_id_thr);
}
return 0;
}
};
/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
{
return new ThreadObject_Win32(proc, param);
}
/* static */ ThreadObject* ThreadObject::AttachCurrent()
{
return new ThreadObject_Win32();
}
/* static */ uint ThreadObject::CurrentId()
{
return GetCurrentThreadId();
}
/**
* Win32 thread version of ThreadSemaphore.
*/
struct ThreadSemaphore_Win32 : public ThreadSemaphore {
private:
HANDLE m_handle;
public:
ThreadSemaphore_Win32()
{
m_handle = ::CreateEvent(NULL, FALSE, FALSE, NULL);
}
/* virtual */ ~ThreadSemaphore_Win32()
{
::CloseHandle(m_handle);
}
/* virtual */ void Set()
{
::SetEvent(m_handle);
}
/* virtual */ void Wait()
{
::WaitForSingleObject(m_handle, INFINITE);
}
};
/* static */ ThreadSemaphore *ThreadSemaphore::New()
{
return new ThreadSemaphore_Win32();
}
#endif