265 lines
5.7 KiB
C++
265 lines
5.7 KiB
C++
/**
|
|
@file GThread.cpp
|
|
|
|
GThread class.
|
|
|
|
@created 2005-09-24
|
|
@edited 2010-09-22
|
|
*/
|
|
|
|
#include "G3D/GThread.h"
|
|
#include "G3D/System.h"
|
|
#include "G3D/debugAssert.h"
|
|
#include "G3D/GMutex.h"
|
|
|
|
namespace G3D {
|
|
|
|
namespace _internal {
|
|
|
|
class BasicThread: public GThread {
|
|
public:
|
|
BasicThread(const std::string& name, void (*proc)(void*), void* param):
|
|
GThread(name), m_wrapperProc(proc), m_param(param) { }
|
|
protected:
|
|
virtual void threadMain() {
|
|
m_wrapperProc(m_param);
|
|
}
|
|
|
|
private:
|
|
void (*m_wrapperProc)(void*);
|
|
|
|
void* m_param;
|
|
};
|
|
|
|
} // namespace _internal
|
|
|
|
|
|
GThread::GThread(const std::string& name):
|
|
m_status(STATUS_CREATED),
|
|
m_name(name) {
|
|
|
|
#ifdef G3D_WINDOWS
|
|
m_event = NULL;
|
|
#endif
|
|
|
|
// system-independent clear of handle
|
|
System::memset(&m_handle, 0, sizeof(m_handle));
|
|
}
|
|
|
|
GThread::~GThread() {
|
|
#ifdef _MSC_VER
|
|
# pragma warning( push )
|
|
# pragma warning( disable : 4127 )
|
|
#endif
|
|
alwaysAssertM(m_status != STATUS_RUNNING, "Deleting thread while running.");
|
|
#ifdef _MSC_VER
|
|
# pragma warning( pop )
|
|
#endif
|
|
|
|
#ifdef G3D_WINDOWS
|
|
if (m_event) {
|
|
::CloseHandle(m_event);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) {
|
|
return shared_ptr<GThread>(new _internal::BasicThread(name, proc, param));
|
|
}
|
|
|
|
|
|
bool GThread::started() const {
|
|
return m_status != STATUS_CREATED;
|
|
}
|
|
|
|
|
|
int GThread::numCores() {
|
|
return System::numCores();
|
|
}
|
|
|
|
#ifdef G3D_WINDOWS
|
|
// From http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
|
const DWORD MS_VC_EXCEPTION=0x406D1388;
|
|
|
|
#pragma pack(push,8)
|
|
typedef struct tagTHREADNAME_INFO {
|
|
DWORD dwType; // Must be 0x1000.
|
|
LPCSTR szName; // Pointer to name (in user addr space).
|
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
|
} THREADNAME_INFO;
|
|
#pragma pack(pop)
|
|
|
|
static void SetThreadName(DWORD dwThreadID, const char* threadName) {
|
|
THREADNAME_INFO info;
|
|
info.dwType = 0x1000;
|
|
info.szName = threadName;
|
|
info.dwThreadID = dwThreadID;
|
|
info.dwFlags = 0;
|
|
|
|
__try {
|
|
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {}
|
|
}
|
|
#endif
|
|
|
|
|
|
bool GThread::start(SpawnBehavior behavior) {
|
|
|
|
debugAssertM(! started(), "Thread has already executed.");
|
|
if (started()) {
|
|
return false;
|
|
}
|
|
|
|
m_status = STATUS_STARTED;
|
|
|
|
if (behavior == USE_CURRENT_THREAD) {
|
|
// Run on this thread
|
|
m_status = STATUS_RUNNING;
|
|
threadMain();
|
|
m_status = STATUS_COMPLETED;
|
|
return true;
|
|
}
|
|
|
|
# ifdef G3D_WINDOWS
|
|
DWORD threadId;
|
|
|
|
m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
debugAssert(m_event);
|
|
|
|
m_handle = ::CreateThread(NULL, 0, &internalThreadProc, this, 0, &threadId);
|
|
|
|
if (m_handle == NULL) {
|
|
::CloseHandle(m_event);
|
|
m_event = NULL;
|
|
}
|
|
|
|
SetThreadName(threadId, m_name.c_str());
|
|
|
|
return (m_handle != NULL);
|
|
# else
|
|
if (!pthread_create(&m_handle, NULL, &internalThreadProc, this)) {
|
|
return true;
|
|
} else {
|
|
// system-independent clear of handle
|
|
System::memset(&m_handle, 0, sizeof(m_handle));
|
|
|
|
return false;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
void GThread::terminate() {
|
|
if (m_handle) {
|
|
# ifdef G3D_WINDOWS
|
|
::TerminateThread(m_handle, 0);
|
|
# else
|
|
pthread_kill(m_handle, SIGSTOP);
|
|
# endif
|
|
// system-independent clear of handle
|
|
System::memset(&m_handle, 0, sizeof(m_handle));
|
|
}
|
|
}
|
|
|
|
|
|
bool GThread::running() const{
|
|
return (m_status == STATUS_RUNNING);
|
|
}
|
|
|
|
|
|
bool GThread::completed() const {
|
|
return (m_status == STATUS_COMPLETED);
|
|
}
|
|
|
|
|
|
void GThread::waitForCompletion() {
|
|
if (m_status == STATUS_COMPLETED) {
|
|
// Must be done
|
|
return;
|
|
}
|
|
|
|
# ifdef G3D_WINDOWS
|
|
debugAssert(m_event);
|
|
::WaitForSingleObject(m_event, INFINITE);
|
|
# else
|
|
debugAssert(m_handle);
|
|
pthread_join(m_handle, NULL);
|
|
# endif
|
|
}
|
|
|
|
|
|
#ifdef G3D_WINDOWS
|
|
DWORD WINAPI GThread::internalThreadProc(LPVOID param) {
|
|
GThread* current = reinterpret_cast<GThread*>(param);
|
|
debugAssert(current->m_event);
|
|
current->m_status = STATUS_RUNNING;
|
|
current->threadMain();
|
|
current->m_status = STATUS_COMPLETED;
|
|
::SetEvent(current->m_event);
|
|
return 0;
|
|
}
|
|
#else
|
|
void* GThread::internalThreadProc(void* param) {
|
|
GThread* current = reinterpret_cast<GThread*>(param);
|
|
current->m_status = STATUS_RUNNING;
|
|
current->threadMain();
|
|
current->m_status = STATUS_COMPLETED;
|
|
return (void*)NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
//GMutex implementation
|
|
GMutex::GMutex() {
|
|
#ifdef G3D_WINDOWS
|
|
::InitializeCriticalSection(&m_handle);
|
|
#else
|
|
int ret = pthread_mutexattr_init(&m_attr);
|
|
debugAssert(ret == 0);
|
|
ret = pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);
|
|
debugAssert(ret == 0);
|
|
ret = pthread_mutex_init(&m_handle, &m_attr);
|
|
debugAssert(ret == 0);
|
|
#endif
|
|
}
|
|
|
|
GMutex::~GMutex() {
|
|
//TODO: Debug check for locked
|
|
#ifdef G3D_WINDOWS
|
|
::DeleteCriticalSection(&m_handle);
|
|
#else
|
|
int ret = pthread_mutex_destroy(&m_handle);
|
|
debugAssert(ret == 0);
|
|
ret = pthread_mutexattr_destroy(&m_attr);
|
|
debugAssert(ret == 0);
|
|
#endif
|
|
}
|
|
|
|
bool GMutex::tryLock() {
|
|
#ifdef G3D_WINDOWS
|
|
return (::TryEnterCriticalSection(&m_handle) != 0);
|
|
#else
|
|
return (pthread_mutex_trylock(&m_handle) == 0);
|
|
#endif
|
|
}
|
|
|
|
void GMutex::lock() {
|
|
#ifdef G3D_WINDOWS
|
|
::EnterCriticalSection(&m_handle);
|
|
#else
|
|
pthread_mutex_lock(&m_handle);
|
|
#endif
|
|
}
|
|
|
|
void GMutex::unlock() {
|
|
#ifdef G3D_WINDOWS
|
|
::LeaveCriticalSection(&m_handle);
|
|
#else
|
|
pthread_mutex_unlock(&m_handle);
|
|
#endif
|
|
}
|
|
|
|
} // namespace G3D
|