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