/*] Copyright (c) 2009-2011, Charles McGarvey [***************************** **] All rights reserved. * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * *****************************************************************************/ #ifndef _MOOF_THREAD_HH_ #define _MOOF_THREAD_HH_ #include #include #include #include #include #include /** * \file thread.hh * Light C++ wrapper around the SDL threads API. */ namespace moof { // forward declarations class runloop; typedef boost::shared_ptr runloop_ptr; /** * 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(); /** * 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); /** * Detach a new thread and run its runloop with an initial timer. * \param timer The timer to schedule on the thread. * \return The new thread, or an invalid thread if an error occurred. */ static thread detach(timer& timer); /** * 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() { if (thread_) { int i; SDL_WaitThread(thread_, &i); thread_ = 0; return i; } return 1; } /** * 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(); /** * 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(); } /** * Get the thread's runloop. * \return The thread's runloop. */ moof::runloop& runloop() const; /** * Get the runloop for the main thread. * \return The runloop. */ static moof::runloop& main_runloop(); private: static void spawn(thread* thread); static int run(void* arg); function function_; SDL_Thread* thread_; runloop_ptr runloop_; }; /** * 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, bool lock = true) : mutex_(mutex), is_locked_(false) { if (lock && !acquire()) throw "mutex lock not acquired"; } /** * 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; }; 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, bool lock = true) : semaphore_(semaphore), is_locked_(false) { if (lock && !acquire()) throw "semaphore lock not acquired"; } /** * 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_; }; private: SDL_sem* semaphore_; }; #if ENABLE_THREADS #define MOOF_DECLARE_MUTEX(M) moof::mutex M #define MOOF_DECLARE_STATIC_MUTEX(M) static moof::mutex M #define MOOF_MUTEX_LOCK(M) moof::mutex::lock lock_##M(M) #else #define MOOF_DECLARE_MUTEX(M) #define MOOF_DECLARE_STATIC_MUTEX(M) #define MOOF_MUTEX_LOCK(M) #endif } // namespace moof #endif // _MOOF_THREAD_HH_