]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Thread.hh
constructors should be explicit
[chaz/yoink] / src / Moof / Thread.hh
index 3e0bd829a8affabd059f5b7b376779e7971e4fbf..2987a59b968716b7649c1eb5d180072f3c308e8a 100644 (file)
 *
 **************************************************************************/
 
-#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 <boost/bind.hpp>
 #include <boost/function.hpp>
 #include <SDL/SDL.h>
 
+#include <Moof/Math.hh>
+
 
 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<int(void)> Function;
 
-typedef boost::function<int(void)> 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;
 };
 
 
This page took 0.030984 seconds and 4 git commands to generate.