/** @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(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(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(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