//-----------------------------------------------------------------------------
// Torque Game Engine Advanced
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/types.h"
#include "core/util/tVector.h"
#include "platform/threads/mutex.h"
#ifndef _PLATFORM_THREADS_THREAD_H_
#define _PLATFORM_THREADS_THREAD_H_
// Forward ref used by platform code
class PlatformThreadData;
// Typedefs
typedef void (*ThreadRunFunction)(void *data);
class Thread
{
protected:
PlatformThreadData* mData;
/// Used to signal threads need to stop.
/// Threads set this flag to false in start()
bool shouldStop;
public:
/// If set, the thread will delete itself once it has finished running.
bool autoDelete;
/// Create a thread.
/// @param func The starting function for the thread.
/// @param arg Data to be passed to func, when the thread starts.
/// @param start_thread Whether to start the Thread immediately.
Thread(ThreadRunFunction func = 0, void *arg = 0, bool start_thread = true, bool autodelete = false);
/// Destroy a thread.
/// The thread MUST be allowed to exit before it is destroyed.
virtual ~Thread();
/// Start a thread.
/// Sets shouldStop to false and calls run() in a new thread of execution.
void start();
/// Ask a thread to stop running.
void stop() { shouldStop = true; }
/// Block until the thread stops running.
bool join();
/// Threads may call checkForStop() periodically to check if they've been
/// asked to stop. As soon as checkForStop() returns true, the thread should
/// clean up and return.
bool checkForStop() { return shouldStop; }
/// Run the Thread's entry point function.
/// Override this method in a subclass of Thread to create threaded code in
/// an object oriented way, and without passing a function ptr to Thread().
/// Also, you can call this method directly to execute the thread's
/// code in a non-threaded way.
virtual void run(void *arg = 0);
/// Returns true if the thread is running.
bool isAlive();
/// Returns the platform specific thread id for this thread.
U32 getId();
};
class ThreadManager
{
static ThreadManager* singleton()
{
static ThreadManager* man = NULL;
if(!man) man = new ThreadManager;
AssertISV(man, "Thread manager doesn't exist.");
return man;
}
Vector<Thread*> threadPool;
Mutex poolLock;
public:
/// Returns true if threadId is the same as the calling thread's id.
static bool isCurrentThread(U32 threadId);
/// Returns true if the 2 thread ids represent the same thread. Some thread
/// APIs return an opaque object as a thread id, so the == operator cannot
/// reliably compare thread ids.
// this comparator is needed by pthreads and ThreadManager.
static bool compare(U32 threadId_1, U32 threadId_2);
/// Returns the platform specific thread id of the calling thread. Some
/// platforms do not guarantee that this ID stays the same over the life of
/// the thread, so use ThreadManager::compare() to compare thread ids.
static U32 getCurrentThreadId();
/// Each thread should add itself to the thread pool the first time it runs.
static void addThread(Thread* thread)
{
ThreadManager &manager = *singleton();
manager.poolLock.lock();
Thread *alreadyAdded = getThreadById(thread->getId());
if(!alreadyAdded)
manager.threadPool.push_back(thread);
manager.poolLock.unlock();
}
static void removeThread(Thread* thread)
{
ThreadManager &manager = *singleton();
manager.poolLock.lock();
U32 threadID = thread->getId();
for(U32 i = 0;i < manager.threadPool.size();++i)
{
if(manager.threadPool[i]->getId() == threadID)
{
manager.threadPool.erase(i);
break;
}
}
manager.poolLock.unlock();
}
/// Searches the pool of known threads for a thread whose id is equivalent to
/// the given threadid. Compares thread ids with ThreadManager::compare().
static Thread* getThreadById(U32 threadid)
{
AssertFatal(threadid != 0, "ThreadManager::getThreadById() Searching for a bad thread id.");
Thread* ret = NULL;
singleton()->poolLock.lock();
Vector<Thread*> &pool = singleton()->threadPool;
for( S32 i = pool.size() - 1; i >= 0; i--)
{
Thread* p = pool[i];
if(compare(p->getId(), threadid))
{
ret = p;
break;
}
}
singleton()->poolLock.unlock();
return ret;
}
static Thread* getCurrentThread()
{
return getThreadById(ThreadManager::getCurrentThreadId());
}
};
inline bool ThreadManager::isCurrentThread(U32 threadId)
{
U32 current = getCurrentThreadId();
return compare(current, threadId);
}
#endif // _PLATFORM_THREADS_THREAD_H_
//-----------------------------------------------------------------------------
// Torque Game Engine Advanced
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/threads/thread.h"
#include "platform/threads/semaphore.h"
#include "platformWin32/platformWin32.h"
#include "core/util/safeDelete.h"
#include <process.h> // [tom, 4/20/2006] for _beginthread()
//-----------------------------------------------------------------------------
// Thread data
//-----------------------------------------------------------------------------
class PlatformThreadData
{
public:
ThreadRunFunction mRunFunc;
void* mRunArg;
Thread* mThread;
HANDLE mThreadHnd;
Semaphore mGateway;
U32 mThreadID;
PlatformThreadData()
{
mRunFunc = NULL;
mRunArg = 0;
mThread = 0;
mThreadHnd = 0;
};
};
//-----------------------------------------------------------------------------
// Static Functions/Methods
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Function: ThreadRunHandler
// Summary: Calls Thread::run() with the thread's specified run argument.
// Neccesary because Thread::run() is provided as a non-threaded
// way to execute the thread's run function. So we have to keep
// track of the thread's lock here.
static unsigned int __stdcall ThreadRunHandler(void * arg)
{
PlatformThreadData* mData = reinterpret_cast<PlatformThreadData*>(arg);
mData->mThreadID = ThreadManager::getCurrentThreadId();
ThreadManager::addThread(mData->mThread);
mData->mThread->run(mData->mRunArg);
ThreadManager::removeThread(mData->mThread);
// we could delete the Thread here, if it wants to be auto-deleted...
mData->mGateway.release();
// the end of this function is where the created win32 thread will die.
_endthreadex( 0 );
return 0;
}
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
Thread::Thread(ThreadRunFunction func /* = 0 */, void *arg /* = 0 */, bool start_thread /* = true */, bool autodelete /*= false*/)
{
mData = new PlatformThreadData;
mData->mRunFunc = func;
mData->mRunArg = arg;
mData->mThread = this;
if(start_thread)
start();
}
Thread::~Thread()
{
stop();
join();
SAFE_DELETE(mData);
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
void Thread::start()
{
if(isAlive())
return;
// cause start to block out other pthreads from using this Thread,
// at least until ThreadRunHandler exits.
mData->mGateway.acquire();
// reset the shouldStop flag, so we'll know when someone asks us to stop.
shouldStop = false;
mData->mThreadHnd = (HANDLE)_beginthreadex(0, 0, ThreadRunHandler, mData, 0, 0);
}
bool Thread::join()
{
if(!isAlive())
return true;
bool res = ( WaitForSingleObject(mData->mThreadHnd, INFINITE) != WAIT_FAILED );
CloseHandle( mData->mThreadHnd );
return res;
}
void Thread::run(void *arg /* = 0 */)
{
if(mData->mRunFunc)
mData->mRunFunc(arg);
}
bool Thread::isAlive()
{
if(mData->mThreadHnd == 0)
return false;
DWORD res = WaitForSingleObject(mData->mThreadHnd, 0);
return res != WAIT_OBJECT_0 && res != WAIT_FAILED;
}
U32 Thread::getId()
{
return mData->mThreadID;
}
U32 ThreadManager::getCurrentThreadId()
{
return GetCurrentThreadId();
}
bool ThreadManager::compare(U32 threadId_1, U32 threadId_2)
{
return (threadId_1 == threadId_2);
}