// Copyright 2007 Georgia Institute of Technology. All rights reserved. // ABSOLUTELY NOT FOR DISTRIBUTION /** * @file thread.h * * Abstractions for helping you write threaded programs. */ #ifndef PAR_THREAD_H #define PAR_THREAD_H #include "task.h" #include "base/common.h" #include /** * Thread convenience wrapper. * * Usage: Create a Thread, give it a Task object, tell the thread to run, * and eventually wait for the thread to finish. */ class Thread { FORBID_COPY(Thread); private: #ifdef DEBUG enum {UNINIT, READY, ATTACHED, DETACHED, DONE} status; #endif pthread_t thread_; Task *task_; static void *ThreadMain_(void *self) { Thread* thread = reinterpret_cast(self); thread->task_->Run(); return NULL; } void Exit_() { pthread_exit(NULL); } public: Thread() { DEBUG_ONLY(status = UNINIT); } ~Thread() { DEBUG_ASSERT(status == DETACHED || status == READY || status == DONE); DEBUG_ONLY(status = UNINIT); } /** * Initializes, given a task to run. */ void Init(Task* task_in) { DEBUG_ASSERT(status == UNINIT); task_ = task_in; DEBUG_ONLY(status = READY); } /** * Starts the thread running. */ void Start() { DEBUG_ASSERT(status == READY); pthread_create(&thread_, NULL, ThreadMain_, reinterpret_cast(this)); DEBUG_ONLY(status = ATTACHED); } /** * Detaches a thread -- the thread will cease to exist once the task * completes. You may not call WaitStop on this thread afterwards. */ void Detach() { DEBUG_ASSERT(status == ATTACHED); pthread_detach(thread_); DEBUG_ONLY(status = DETACHED); } /** * Wait for a thread to stop. * * Failure to do this may cause your program to hang when it is done. */ void WaitStop() { DEBUG_ASSERT(status == ATTACHED); pthread_join(thread_, NULL); DEBUG_ONLY(status = DONE); } /** * Gets the contained task. */ Task* task() const { return task_; } }; /** * Mutual exclusion lock to prevent threads from clobbering results. */ class Mutex { FORBID_COPY(Mutex); friend class WaitCondition; private: pthread_mutex_t mutex_; public: static Mutex global; public: Mutex() { pthread_mutex_init(&mutex_, NULL); } ~Mutex() { pthread_mutex_destroy(&mutex_); } /** Obtains the lock. */ void Lock() { pthread_mutex_lock(&mutex_); } /** Tries to lock, returns false if doing so would require waiting. */ bool TryLock() { return likely(!pthread_mutex_trylock(&mutex_)); } /** Releases the lock. */ void Unlock() { pthread_mutex_unlock(&mutex_); } }; /** * Wait condition for alerting other threads of an action. */ class WaitCondition { FORBID_COPY(WaitCondition); private: pthread_cond_t cond_; public: WaitCondition() { pthread_cond_init(&cond_, NULL); } ~WaitCondition() { pthread_cond_destroy(&cond_); } void Signal() { pthread_cond_signal(&cond_); } void Broadcast() { pthread_cond_broadcast(&cond_); } void Wait(Mutex* mutex_to_unlock) { pthread_cond_wait(&cond_, &mutex_to_unlock->mutex_); } void WaitMillis(Mutex& mutex_to_unlock, unsigned millis) { struct timespec ts; ts.tv_sec = millis / 1000; ts.tv_nsec = (millis % 1000) * 1000000; pthread_cond_timedwait(&cond_, &mutex_to_unlock.mutex_, &ts); } void WaitSec(Mutex& mutex_to_unlock, unsigned sec) { struct timespec ts; ts.tv_sec = sec; ts.tv_nsec = 0; pthread_cond_timedwait(&cond_, &mutex_to_unlock.mutex_, &ts); } }; /** * Mix-in to make a version of an existing object that can be locked. * * Your object must have default constructors and use Init methods. * The resulting object will have Lock, Unlock, and TryLock methods. */ template class Lockable : public TContained, public Mutex { FORBID_COPY(Lockable); Lockable() {} ~Lockable() {} }; #endif