X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fmoof%2Fthread.hh;fp=src%2Fmoof%2Fthread.hh;h=09caefd3048800a0cab77380bbceb62743700e96;hb=831f04d4bc19a390415ac0bbac4331c7a65509bc;hp=0000000000000000000000000000000000000000;hpb=299af4f2047e767e5d79501c26444473bda64c64;p=chaz%2Fyoink diff --git a/src/moof/thread.hh b/src/moof/thread.hh new file mode 100644 index 0000000..09caefd --- /dev/null +++ b/src/moof/thread.hh @@ -0,0 +1,622 @@ + +/*] Copyright (c) 2009-2010, Charles McGarvey [************************** +**] All rights reserved. +* +* vi:ts=4 sw=4 tw=75 +* +* Distributable under the terms and conditions of the 2-clause BSD license; +* see the file COPYING for a complete text of the license. +* +**************************************************************************/ + +/** + * \file thread.hh + * Light C++ wrapper around the SDL threads API. + */ + +#ifndef _MOOF_THREAD_HH_ +#define _MOOF_THREAD_HH_ + +#include +#include +#include + +#include + + +namespace moof { + + +/** + * 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 boost::function function; + + + /** + * Construct an invalid thread object which has no association with any + * real thread. + */ + thread() : + thread_(0) {} + + /** + * 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) + { + thread::function* fcopy = new thread::function(function); + SDL_Thread* thread = SDL_CreateThread(&thread::run, (void*)fcopy); + if (thread == 0) delete fcopy; + return moof::thread(thread); + } + + + /** + * 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(thread_, &i); + thread_ = 0; + return i; + } + + /** + * 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(thread_); + thread_ = 0; + } + + /** + * Get whether or not the thread object is associated with a real + * thread. + * \return True if the thread is valid, false otherwise. + */ + bool is_valid() const + { + return thread_ != 0; + } + + + /** + * Get a unique identifier for this thread, if it is valid. + * \return The identifier. + */ + uint32_t identifier() const + { + return SDL_GetThreadID(thread_); + } + + /** + * Get the unique identifier of the calling thread. + * \return The identifier. + */ + static uint32_t current_identifier() + { + return SDL_ThreadID(); + } + + +private: + + thread(SDL_Thread* thread) : + thread_(thread) {} + + static int run(void* arg) + { + int code = (*(function*)arg)(); + delete (function*)arg; + return code; + } + + + SDL_Thread* thread_; +}; + + +/** + * An abstract class representing some task that is to be run + * asynchronously. + */ +class async_task +{ +public: + + /** + * Deconstruct the task. + */ + virtual ~async_task() {} + + /** + * Get whether or not the task is done. + * \return True if the task is done, false otherwise. + */ + virtual bool is_done() 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 threaded_task +{ +public: + + /** + * Get the thread object the task is executing in. + * \return The thread. + */ + const class thread& thread() const { return thread_; } + + /** + * Block the current thread until the task thread is finished. + * \return The integer value returned by the task function. + */ + int wait() + { + return thread_.wait(); + } + + +protected: + + class thread thread_; +}; + + +/** + * A mutex to protect sensitive sections of code from threads which might + * otherwise cause unpredictable results. + */ +class mutex +{ +public: + + /** + * Construct a mutex. + */ + mutex() : + mutex_(SDL_CreateMutex()) {} + + /** + * Deconstruct a mutex. + */ + ~mutex() + { + SDL_DestroyMutex(mutex_); + } + + + /** + * 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 acquire_lock() + { + return (SDL_LockMutex(mutex_) == 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 release_lock() + { + return (SDL_UnlockMutex(mutex_) == 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 + { + public: + + /** + * Construct a lock. + * \param mutex The mutex. + */ + explicit lock(mutex& mutex) : + mutex_(mutex), + is_locked_(false) {} + + /** + * Deconstruct a lock. The lock is automagically released if it is + * still locked. + */ + ~lock() + { + if (is_locked_) release(); + } + + + /** + * Try to acquire a lock on the mutex. + * \return True if the mutex was locked, false otherwise. + */ + bool acquire() + { + return (is_locked_ = mutex_.acquire_lock()); + } + + /** + * Release the lock. + * \return True if the mutex was unlocked, false otherwise. + */ + bool release() + { + bool result = mutex_.release_lock(); + is_locked_ = false; + return result; + } + + + /** + * Get whether or not the mutex is locked. + * \return True if the mutex is locked, false otherwise. + */ + bool is_locked() const + { + return is_locked_; + } + + + protected: + + mutex& mutex_; + bool is_locked_; + + friend class condition; + }; + + /** + * This type of lock tries to acquire a lock on the mutex during + * construction and releases the lock on deconstruction. + */ + class scoped_lock : private lock + { + public: + + /** + * Construct a lock. + * \param mutex The mutex. + */ + explicit scoped_lock(mutex& mutex) : + lock(mutex) + { + acquire(); + } + + /** + * Get whether or not the mutex is locked. + * \return True if the mutex is locked, false otherwise. + */ + bool is_locked() const + { + return lock::is_locked(); + } + }; + + +private: + + SDL_mutex* mutex_; + + friend class condition; +}; + + +/** + * A class representing a condition variable. + */ +class condition +{ +public: + + /** + * Construct a condition. + */ + condition() + { + condition_ = SDL_CreateCond(); + } + + /** + * Deconstruct a condition. + */ + ~condition() + { + SDL_DestroyCond(condition_); + } + + + /** + * 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(condition_, mutex.mutex_) == 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.mutex_.mutex_) == 0); + } + + /** + * 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) + { + Uint32 ms = timeout * SCALAR(1000.0); + return (SDL_CondWaitTimeout(condition_, mutex.mutex_, 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(condition_, + lock.mutex_.mutex_, 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); + } + + /** + * Notify all other threads that are waiting on the condition. + * \return True on success, false otherwise. + */ + bool notify_all() + { + return (SDL_CondBroadcast(condition_) == 0); + } + + +private: + + SDL_cond* condition_; +}; + + +/** + * A semaphore class. + */ +class semaphore +{ +public: + + /** + * Construct a semaphore. + * \param value The initial value of the semaphore. + */ + explicit semaphore(uint32_t value) + { + semaphore_ = SDL_CreateSemaphore(value); + } + + /** + * Deconstruct a semaphore. + */ + ~semaphore() + { + SDL_DestroySemaphore(semaphore_); + } + + + /** + * 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 acquire_lock() + { + return (SDL_SemWait(semaphore_) == 0); + } + + /** + * 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 acquire_lock(scalar timeout) + { + Uint32 ms = timeout * SCALAR(1000.0); + return (SDL_SemWaitTimeout(semaphore_, ms) == 0); + } + + /** + * 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 release_lock() + { + return (SDL_SemPost(semaphore_) == 0); + } + + /** + * 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 try_lock() + { + return (SDL_SemTryWait(semaphore_) == 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: + + /** + * Construct a lock. + * \param semaphore The semaphore. + */ + explicit lock(semaphore& semaphore) : + semaphore_(semaphore), + is_locked_(false) {} + + /** + * Deconstruct a lock. The lock is automagically released if it is + * still locked. + */ + ~lock() + { + if (is_locked_) release(); + } + + + /** + * Try to acquire a lock on the semaphore. + * \return True if the semaphore was locked, false otherwise. + */ + bool acquire() + { + return (is_locked_ = semaphore_.acquire_lock()); + } + + /** + * Release the lock. + * \return True if the semaphore was unlocked, false otherwise. + */ + bool release() + { + bool result = semaphore_.release_lock(); + is_locked_ = false; + return result; + } + + /** + * Get whether or not the semaphore is locked. + * \return True if the semaphore is locked, false otherwise. + */ + bool is_locked() const + { + return is_locked_; + } + + + protected: + + semaphore& semaphore_; + bool is_locked_; + }; + + /** + * This type of lock tries to acquire a lock on the semaphore during + * construction and releases the lock on deconstruction. + */ + class scoped_lock : private lock + { + public: + + /** + * Construct a lock. + * \param semaphore The semaphore. + */ + explicit scoped_lock(semaphore& semaphore) : + lock(semaphore) + { + acquire(); + } + + /** + * Get whether or not the semaphore is locked. + * \return True if the semaphore is locked, false otherwise. + */ + bool is_locked() const + { + return lock::is_locked(); + } + }; + + +private: + + SDL_sem* semaphore_; +}; + + +} // namespace moof + +#endif // _MOOF_THREAD_HH_ +