src/thread.cpp
author truebrain
Tue, 01 Apr 2008 13:28:55 +0000
branchnoai
changeset 9856 6a0dcee9f8e4
parent 9723 eee46cb39750
permissions -rw-r--r--
(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