*
**************************************************************************/
-#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();
}
};
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
+/**
+ * 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.
+ */
+ 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:
+ /**
+ * Construct a lock.
+ * \param mutex The mutex.
+ */
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.
+ */
+ 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.
+ */
+ 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:
+ /**
+ * Construct a lock.
+ * \param semaphore The semaphore.
+ */
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;
};