204 lines
3.9 KiB
C++
204 lines
3.9 KiB
C++
// 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 <pthread.h>
|
|
|
|
/**
|
|
* 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<Thread*>(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<void*>(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 TContained>
|
|
class Lockable : public TContained, public Mutex {
|
|
FORBID_COPY(Lockable);
|
|
|
|
Lockable() {}
|
|
~Lockable() {}
|
|
};
|
|
|
|
#endif
|