richk@10211: /* $Id$ */ richk@10211: richk@10211: /** @file thread_pthread.cpp POSIX pthread implementation of Threads. */ richk@10211: richk@10211: #include "stdafx.h" richk@10211: #include "thread.h" richk@10211: #include "debug.h" richk@10211: #include "core/alloc_func.hpp" richk@10211: #include richk@10211: #include richk@10211: #include richk@10211: #include richk@10211: richk@10211: /** richk@10211: * POSIX pthread version for ThreadObject. richk@10211: */ richk@10211: class ThreadObject_pthread : public ThreadObject { richk@10211: private: richk@10211: pthread_t m_thr; ///< System thread identifier. richk@10211: OTTDThreadFunc m_proc; ///< External thread procedure. richk@10211: void *m_param; ///< Parameter for the external thread procedure. richk@10211: bool m_attached; ///< True if the ThreadObject was attached to an existing thread. richk@10211: sem_t m_sem_start; ///< Here the new thread waits before it starts. richk@10211: sem_t m_sem_stop; ///< Here the other thread can wait for this thread to end. richk@10211: richk@10211: public: richk@10211: /** richk@10211: * Create a pthread and start it, calling proc(param). richk@10211: */ richk@10211: ThreadObject_pthread(OTTDThreadFunc proc, void *param) : richk@10211: m_thr(0), richk@10211: m_proc(proc), richk@10211: m_param(param), richk@10211: m_attached(false) richk@10211: { richk@10211: sem_init(&m_sem_start, 0, 0); richk@10211: sem_init(&m_sem_stop, 0, 0); richk@10211: richk@10211: pthread_create(&m_thr, NULL, &stThreadProc, this); richk@10211: sem_post(&m_sem_start); richk@10211: } richk@10211: richk@10211: /** richk@10211: * Create a pthread and attach current thread to it. richk@10211: */ richk@10211: ThreadObject_pthread() : richk@10211: m_thr(0), richk@10211: m_proc(NULL), richk@10211: m_param(0), richk@10211: m_attached(true) richk@10211: { richk@10211: sem_init(&m_sem_start, 0, 0); richk@10211: sem_init(&m_sem_stop, 0, 0); richk@10211: richk@10211: m_thr = pthread_self(); richk@10211: } richk@10211: richk@10211: /* virtual */ ~ThreadObject_pthread() richk@10211: { richk@10211: sem_destroy(&m_sem_stop); richk@10211: sem_destroy(&m_sem_start); richk@10211: }; richk@10211: richk@10211: /* virtual */ bool IsRunning() richk@10211: { richk@10724: int sval; richk@10724: sem_getvalue(&m_sem_stop, &sval); richk@10724: return sval == 0; richk@10211: } richk@10211: richk@10211: /* virtual */ bool WaitForStop() richk@10211: { richk@10211: /* You can't wait on yourself */ richk@10211: assert(!IsCurrent()); richk@10211: /* If the thread is not running, waiting is over */ richk@10211: if (!IsRunning()) return true; richk@10211: richk@10211: int ret = sem_wait(&m_sem_stop); richk@10211: if (ret == 0) { richk@10211: /* We have passed semaphore so increment it again */ richk@10211: sem_post(&m_sem_stop); richk@10211: return true; richk@10211: } richk@10211: return false; richk@10211: } richk@10211: richk@10211: /* virtual */ bool Exit() richk@10211: { richk@10211: /* You can only exit yourself */ richk@10211: assert(IsCurrent()); richk@10211: /* If the thread is not running, we are already closed */ richk@10211: if (!IsRunning()) return false; richk@10211: richk@10211: /* For now we terminate by throwing an error, gives much cleaner cleanup */ richk@10211: throw 0; richk@10211: } richk@10211: richk@10991: /* virtual */ void Join() richk@10211: { richk@10211: /* You cannot join yourself */ richk@10211: assert(!IsCurrent()); richk@10211: richk@10991: pthread_join(m_thr, NULL); richk@10211: m_thr = 0; richk@10211: } richk@10211: richk@10211: /* virtual */ bool IsCurrent() richk@10211: { richk@10211: return pthread_self() == m_thr; richk@10211: } richk@10211: richk@10211: /* virtual */ uint GetId() richk@10211: { richk@10211: return (uint)m_thr; richk@10211: } richk@10211: richk@10211: private: richk@10211: /** richk@10211: * On thread creation, this function is called, which calls the real startup richk@10211: * function. This to get back into the correct instance again. richk@10211: */ richk@10211: static void *stThreadProc(void *thr) richk@10211: { richk@10991: ((ThreadObject_pthread *)thr)->ThreadProc(); richk@10991: pthread_exit(NULL); richk@10211: } richk@10211: richk@10211: /** richk@10211: * A new thread is created, and this function is called. Call the custom richk@10211: * function of the creator of the thread. richk@10211: */ richk@10991: void ThreadProc() richk@10211: { richk@10211: /* The new thread stops here so the calling thread can complete pthread_create() call */ richk@10211: sem_wait(&m_sem_start); richk@10211: richk@10211: /* Call the proc of the creator to continue this thread */ richk@10211: try { richk@10211: m_proc(m_param); richk@10211: } catch (...) { richk@10211: } richk@10211: richk@10211: /* Notify threads waiting for our completion */ richk@10211: sem_post(&m_sem_stop); richk@10211: } richk@10211: }; richk@10211: richk@10211: /* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param) richk@10211: { richk@10211: return new ThreadObject_pthread(proc, param); richk@10211: } richk@10211: richk@10211: /* static */ ThreadObject *ThreadObject::AttachCurrent() richk@10211: { richk@10211: return new ThreadObject_pthread(); richk@10211: } richk@10211: richk@10211: /* static */ uint ThreadObject::CurrentId() richk@10211: { richk@10211: return (uint)pthread_self(); richk@10211: } richk@10211: richk@10211: richk@10211: /** richk@10211: * POSIX pthread version of ThreadSemaphore. richk@10211: */ richk@10211: class ThreadSemaphore_pthread : public ThreadSemaphore { richk@10211: private: richk@10211: sem_t m_sem; richk@10211: richk@10211: public: richk@10211: ThreadSemaphore_pthread() richk@10211: { richk@10211: sem_init(&m_sem, 0, 0); richk@10211: } richk@10211: richk@10211: /* virtual */ ~ThreadSemaphore_pthread() richk@10211: { richk@10211: sem_destroy(&m_sem); richk@10211: } richk@10211: richk@10211: /* virtual */ void Set() richk@10211: { richk@10211: int val = 0; richk@10211: if (sem_getvalue(&m_sem, &val) == 0 && val == 0) sem_post(&m_sem); richk@10211: } richk@10211: richk@10211: /* virtual */ void Wait() richk@10211: { richk@10211: sem_wait(&m_sem); richk@10211: } richk@10211: }; richk@10211: richk@10211: /* static */ ThreadSemaphore *ThreadSemaphore::New() richk@10211: { richk@10211: return new ThreadSemaphore_pthread(); richk@10211: }