]> Dogcows Code - chaz/yoink/blobdiff - src/moof/thread.hh
the massive refactoring effort
[chaz/yoink] / src / moof / thread.hh
diff --git a/src/moof/thread.hh b/src/moof/thread.hh
new file mode 100644 (file)
index 0000000..09caefd
--- /dev/null
@@ -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 <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <SDL/SDL.h>
+
+#include <moof/math.hh>
+
+
+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<int(void)> 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_
+
This page took 0.025829 seconds and 4 git commands to generate.