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