X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FThread.hh;h=2987a59b968716b7649c1eb5d180072f3c308e8a;hp=3e0bd829a8affabd059f5b7b376779e7971e4fbf;hb=de6942ee1401fea16a171610de779ef0a8c57e38;hpb=41f8dd670e963aad94527ce2be0486268993a477 diff --git a/src/Moof/Thread.hh b/src/Moof/Thread.hh index 3e0bd82..2987a59 100644 --- a/src/Moof/Thread.hh +++ b/src/Moof/Thread.hh @@ -9,104 +9,180 @@ * **************************************************************************/ -#ifndef _MOOF_THREAD_HH_ -#define _MOOF_THREAD_HH_ - /** - * @file Thread.hh + * \file Thread.hh * Light C++ wrapper around the SDL threads API. */ +#ifndef _MOOF_THREAD_HH_ +#define _MOOF_THREAD_HH_ + +#include #include #include +#include + namespace Mf { -// -// The detach function detaches a separate thread by calling 'func' with -// the 'arg' parameter. -// +/** + * Represents a thread which may be running. You cannot instantiate a + * thread object directly; new threads are created by detaching functions + * using the detach() method. Once a thread is detached, it will continue + * running until the function returns. You don't need to keep the thread + * object you want to wait() or kill() the thread later. + */ +class Thread +{ +public: -typedef SDL_Thread* Thread; + typedef boost::function Function; -typedef boost::function Function; + /** + * Construct an invalid thread object which has no association with any + * real thread. + */ + Thread() : + mThread(0) {} -inline int detach_(void* arg) -{ - //Function function = *(Function*)arg; - int code = (*(Function*)arg)(); + /** + * Execute a function in a new thread. + * \param function The function to execute. + * \return The new thread, or an invalid thread if an error occurred. + */ + static Thread detach(const Function& function) + { + Function* fcopy = new Function(function); + SDL_Thread* thread = SDL_CreateThread(&Thread::run, (void*)fcopy); + if (thread == 0) delete fcopy; + return Thread(thread); + } - delete (Function*)arg; - return code; -} -inline Thread detachFunction(const Function& function) -{ - Function* fcopy = new Function(function); - Thread thread = SDL_CreateThread(detach_, (void*)fcopy); + /** + * Wait for the thread to terminate, getting its return value. The + * thread will be invalidated. + * \return The integer value returned by the detached function. + */ + int wait() + { + int i; + SDL_WaitThread(mThread, &i); + mThread = 0; + return i; + } - if (thread == 0) delete fcopy; - return thread; -} + /** + * Forcefully kill the thread without giving it a chance to clean up + * after itself. The thread will be invalidated. Don't use this. + */ + void kill() + { + SDL_KillThread(mThread); + mThread = 0; + } + /** + * Get whether or not the thread object is associated with a real + * thread. + * \return True if the thread is valid, false otherwise. + */ + bool isValid() const + { + return mThread != 0; + } -inline int waitOnThread(Thread thread) -{ - int i; - SDL_WaitThread(thread, &i); - return i; -} -inline void killThread(Thread thread) -{ - SDL_KillThread(thread); -} + /** + * Get a unique identifier for this thread, if it is valid. + * \return The identifier. + */ + uint32_t identifier() const + { + return SDL_GetThreadID(mThread); + } + /** + * Get the unique identifier of the calling thread. + * \return The identifier. + */ + static uint32_t currentIdentifier() + { + return SDL_ThreadID(); + } -// -// The identifier function returns a unique integer for the calling thread. -// -inline unsigned getThreadIdentifier() -{ - return SDL_ThreadID(); -} +private: -inline unsigned getThreadIdentifier(Thread thread) -{ - return SDL_GetThreadID(thread); -} + Thread(SDL_Thread* thread) : + mThread(thread) {} + static int run(void* arg) + { + int code = (*(Function*)arg)(); + delete (Function*)arg; + return code; + } + + SDL_Thread* mThread; +}; + + +/** + * An abstract class representing some task that is to be run + * asynchronously. + */ class AsyncTask { public: + /** + * Deconstruct the task. + */ virtual ~AsyncTask() {} + /** + * Get whether or not the task is done. + * \return True if the task is done, false otherwise. + */ virtual bool isDone() const = 0; + /** + * Begin the task. + */ virtual void run() = 0; + + /** + * Block the current thread until the task is finished. + * \return A value representing the state of the finished task. + */ virtual int wait() = 0; }; +/** + * An asynchronous task that is run to be executed in a separated thread. + */ class ThreadedTask { public: - ThreadedTask() : - mThread(0) {} - - Thread thread() const { return mThread; } + /** + * Get the thread object the task is executing in. + * \return The thread. + */ + const Thread& thread() const { return mThread; } + /** + * Block the current thread until the task thread is finished. + * \return The integer value returned by the task function. + */ int wait() { - int code = waitOnThread(mThread); - mThread = 0; - return code; + return mThread.wait(); } @@ -116,205 +192,427 @@ protected: }; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +/** + * A mutex to protect sensitive sections of code from threads which might + * otherwise cause unpredictable results. + */ class Mutex { - friend class Condition; - public: - Mutex() - { - mMutex = SDL_CreateMutex(); - } + /** + * Construct a mutex. + */ + Mutex() : + mMutex(SDL_CreateMutex()) {} + + /** + * Deconstruct a mutex. + */ ~Mutex() { SDL_DestroyMutex(mMutex); } + + /** + * Block until the calling thread can secure exclusive access to the + * code protected by the mutex. + * \return True if the lock was acquired, false otherwise. + * \see Lock + */ bool acquireLock() { return (SDL_LockMutex(mMutex) == 0); } + + /** + * Unlock the mutex. Call this after the sensitive block of code to + * allow another thread to acquire the lock. + * \return True if the mutex was unlocked, false otherwise. + * \see Lock + */ bool releaseLock() { return (SDL_UnlockMutex(mMutex) == 0); } + + /** + * As an alternative method for locking, objects of this class will + * automagically release the lock if it is still locked at + * deconstruction. Therefore, it's generally safer to use this method + * since it makes it much more difficult to forget to unlock a mutex. + */ class Lock { - friend class Condition; - public: - Lock(Mutex& mutex) - { - mMutex = &mutex; - mIsLocked = false; - } + /** + * Construct a lock. + * \param mutex The mutex. + */ + explicit Lock(Mutex& mutex) : + mMutex(mutex), + mIsLocked(false) {} + + /** + * Deconstruct a lock. The lock is automagically released if it is + * still locked. + */ ~Lock() { if (mIsLocked) release(); } + + /** + * Try to acquire a lock on the mutex. + * \return True if the mutex was locked, false otherwise. + */ bool acquire() { - return (mIsLocked = mMutex->acquireLock()); + return (mIsLocked = mMutex.acquireLock()); } + + /** + * Release the lock. + * \return True if the mutex was unlocked, false otherwise. + */ bool release() { - return mMutex->releaseLock(); + bool result = mMutex.releaseLock(); mIsLocked = false; + return result; } + + /** + * Get whether or not the mutex is locked. + * \return True if the mutex is locked, false otherwise. + */ bool isLocked() const { return mIsLocked; } + protected: - Mutex* mMutex; + Mutex& mMutex; bool mIsLocked; + + friend class Condition; }; - class ScopedLock : public Lock + /** + * This type of lock tries to acquire a lock on the mutex during + * construction and releases the lock on deconstruction. + */ + class ScopedLock : private Lock { public: - ScopedLock(Mutex& mutex) : + /** + * Construct a lock. + * \param mutex The mutex. + */ + explicit ScopedLock(Mutex& mutex) : Lock(mutex) { acquire(); } + + /** + * Get whether or not the mutex is locked. + * \return True if the mutex is locked, false otherwise. + */ + bool isLocked() const + { + return Lock::isLocked(); + } }; + private: SDL_mutex* mMutex; + + friend class Condition; }; +/** + * A class representing a condition variable. + */ class Condition { public: + /** + * Construct a condition. + */ Condition() { - condition_ = SDL_CreateCond(); + mCondition = SDL_CreateCond(); } + + /** + * Deconstruct a condition. + */ ~Condition() { - SDL_DestroyCond(condition_); + SDL_DestroyCond(mCondition); } + + /** + * Unlock the mutex and wait for another thread to notify the thread, + * at which point the mutex will be re-locked and the method will + * return. + * \param mutex The mutex. + * \return True if the thread was notified, false otherwise. + */ + bool wait(Mutex& mutex) + { + return (SDL_CondWait(mCondition, mutex.mMutex) == 0); + } + + /** + * Unlock the mutex associated with a lock and wait for another thread + * to notify the thread, at which point the lock will be re-locked and + * the method will return. + * \param lock The lock. + * \return True if the thread was notified, false otherwise. + */ bool wait(Mutex::Lock& lock) { - return (SDL_CondWait(condition_, lock.mMutex->mMutex) == 0); + return (SDL_CondWait(mCondition, lock.mMutex.mMutex) == 0); } - bool wait(Mutex::Lock& lock, unsigned ms) + + /** + * Unlock the mutex and wait for another thread to notify the thread, + * at which point the mutex will be re-locked and the method will + * return. If the thread was not notified before a certain number of + * seconds, the method will return anyway. + * \param mutex The mutex. + * \param timeout Number of seconds to wait. + * \return True if the thread was notified, false otherwise. + */ + bool wait(Mutex& mutex, Scalar timeout) { - // TODO for consistency, this function should take seconds - return (SDL_CondWaitTimeout(condition_, - lock.mMutex->mMutex, ms) == 0); + Uint32 ms = timeout * SCALAR(1000.0); + return (SDL_CondWaitTimeout(mCondition, mutex.mMutex, ms) == 0); } + /** + * Unlock the mutex associated with a lock and wait for another thread + * to notify the thread, at which point the lock will be re-locked and + * the method will return. If the thread was not notified before a + * certain number of seconds, the method will return anyway. + * \param lock The lock. + * \param timeout Number of seconds to wait. + * \return True if the thread was notified, false otherwise. + */ + bool wait(Mutex::Lock& lock, Scalar timeout) + { + Uint32 ms = timeout * SCALAR(1000.0); + return (SDL_CondWaitTimeout(mCondition, + lock.mMutex.mMutex, ms) == 0); + } + + + /** + * Notify one other thread that is waiting on the condition. + * \return True on success, false otherwise. + */ bool notify() { - return (SDL_CondSignal(condition_) == 0); + return (SDL_CondSignal(mCondition) == 0); } + + /** + * Notify all other threads that are waiting on the condition. + * \return True on success, false otherwise. + */ bool notifyAll() { - return (SDL_CondBroadcast(condition_) == 0); + return (SDL_CondBroadcast(mCondition) == 0); } + private: - SDL_cond* condition_; + SDL_cond* mCondition; }; +/** + * A semaphore class. + */ class Semaphore { public: - Semaphore(unsigned int value) + /** + * Construct a semaphore. + * \param value The initial value of the semaphore. + */ + explicit Semaphore(uint32_t value) { - semaphore_ = SDL_CreateSemaphore(value); + mSemaphore = SDL_CreateSemaphore(value); } + + /** + * Deconstruct a semaphore. + */ ~Semaphore() { - SDL_DestroySemaphore(semaphore_); + SDL_DestroySemaphore(mSemaphore); } + + /** + * Block until the calling thread can secure exclusive access to the + * code protected by the semaphore. + * \return True if the lock was acquired, false otherwise. + * \see Lock + */ bool acquireLock() { - return (SDL_SemWait(semaphore_) == 0); + return (SDL_SemWait(mSemaphore) == 0); } - bool releaseLock() + + /** + * Block until the calling thread can secure exclusive access to the + * code protected by the semaphore, or until the timeout expires. + * \param timeout Number of seconds to try. + * \return True if the lock was acquired, false otherwise. + */ + bool acquireLock(Scalar timeout) { - return (SDL_SemPost(semaphore_) == 0); + Uint32 ms = timeout * SCALAR(1000.0); + return (SDL_SemWaitTimeout(mSemaphore, ms) == 0); } - bool tryLock() + /** + * Unlock the semaphore. Call this after the sensitive block of code + * to allow another thread to acquire the lock. + * \return True if the semaphore was unlocked, false otherwise. + * \see Lock + */ + bool releaseLock() { - return (SDL_SemTryWait(semaphore_) == 0); + return (SDL_SemPost(mSemaphore) == 0); } - bool tryLock(unsigned ms) + + /** + * Try to lock the semaphore, but don't block if the lock is not + * immediately available. + * \return True if the semaphore was locked, false otherwise. + */ + bool tryLock() { - // TODO for consistency, this function should take seconds - return (SDL_SemWaitTimeout(semaphore_, ms) == 0); + return (SDL_SemTryWait(mSemaphore) == 0); } - + + /** + * As an alternative method for locking, objects of this class will + * automagically release the lock if it is still locked at + * deconstruction. Therefore, it's generally safer to use this method + * since it makes it much more difficult to forget to unlock a + * semaphore. + */ class Lock { public: - Lock(Semaphore& semaphore) - { - semaphore_ = &semaphore; - mIsLocked = false; - } + /** + * Construct a lock. + * \param semaphore The semaphore. + */ + explicit Lock(Semaphore& semaphore) : + mSemaphore(semaphore), + mIsLocked(false) {} + + /** + * Deconstruct a lock. The lock is automagically released if it is + * still locked. + */ ~Lock() { if (mIsLocked) release(); } + + /** + * Try to acquire a lock on the semaphore. + * \return True if the semaphore was locked, false otherwise. + */ bool acquire() { - return (mIsLocked = semaphore_->acquireLock()); + return (mIsLocked = mSemaphore.acquireLock()); } + + /** + * Release the lock. + * \return True if the semaphore was unlocked, false otherwise. + */ bool release() { - return semaphore_->releaseLock(); mIsLocked = false; + bool result = mSemaphore.releaseLock(); + mIsLocked = false; + return result; } + /** + * Get whether or not the semaphore is locked. + * \return True if the semaphore is locked, false otherwise. + */ bool isLocked() const { return mIsLocked; } + protected: - Semaphore* semaphore_; + Semaphore& mSemaphore; bool mIsLocked; }; - class ScopedLock : public Lock + /** + * This type of lock tries to acquire a lock on the semaphore during + * construction and releases the lock on deconstruction. + */ + class ScopedLock : private Lock { public: - ScopedLock(Semaphore& semaphore) : + /** + * Construct a lock. + * \param semaphore The semaphore. + */ + explicit ScopedLock(Semaphore& semaphore) : Lock(semaphore) { acquire(); } + + /** + * Get whether or not the semaphore is locked. + * \return True if the semaphore is locked, false otherwise. + */ + bool isLocked() const + { + return Lock::isLocked(); + } }; + private: - SDL_sem* semaphore_; + SDL_sem* mSemaphore; };