/** @file GThread.cpp GThread class. @created 2005-09-24 @edited 2005-10-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_WIN32 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_WIN32 if (m_event) { ::CloseHandle(m_event); } #endif } GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) { return new _internal::BasicThread(name, proc, param); } bool GThread::started() const { return m_status != STATUS_CREATED; } 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_WIN32 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; } 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_WIN32 ::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_WIN32 debugAssert(m_event); ::WaitForSingleObject(m_event, INFINITE); # else debugAssert(m_handle); pthread_join(m_handle, NULL); # endif } #ifdef G3D_WIN32 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_WIN32 ::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_WIN32 ::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_WIN32 return (::TryEnterCriticalSection(&m_handle) != 0); #else return (pthread_mutex_trylock(&m_handle) == 0); #endif } void GMutex::lock() { #ifdef G3D_WIN32 ::EnterCriticalSection(&m_handle); #else pthread_mutex_lock(&m_handle); #endif } void GMutex::unlock() { #ifdef G3D_WIN32 ::LeaveCriticalSection(&m_handle); #else pthread_mutex_unlock(&m_handle); #endif } } // namespace G3D