diff --git a/src/thread_pthread.cpp b/src/thread_pthread.cpp new file mode 100644 --- /dev/null +++ b/src/thread_pthread.cpp @@ -0,0 +1,199 @@ +/* $Id$ */ + +/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */ + +#include "stdafx.h" +#include "thread.h" +#include "debug.h" +#include "core/alloc_func.hpp" +#include +#include +#include +#include + +/** + * 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 Exit() + { + /* You can only exit yourself */ + assert(IsCurrent()); + /* If the thread is not running, we are already closed */ + if (!IsRunning()) return false; + + /* For now we terminate by throwing an error, gives much cleaner cleanup */ + throw 0; + } + + /* virtual */ void *Join() + { + /* You cannot join yourself */ + assert(!IsCurrent()); + + void *ret; + pthread_join(m_thr, &ret); + m_thr = 0; + + return ret; + } + + /* 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 (...) { + } + + /* 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. + */ +class 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(); +}