diff --git a/include/bx/sem.h b/include/bx/sem.h index b33ab37..92a688a 100644 --- a/include/bx/sem.h +++ b/include/bx/sem.h @@ -25,9 +25,7 @@ namespace bx { -#if BX_PLATFORM_POSIX - -# if BX_CONFIG_SEMAPHORE_PTHREAD + /// class Semaphore { BX_CLASS(Semaphore @@ -36,228 +34,31 @@ namespace bx ); public: - Semaphore() - : m_count(0) - { - int result; - result = pthread_mutex_init(&m_mutex, NULL); - BX_CHECK(0 == result, "pthread_mutex_init %d", result); + /// + Semaphore(); - result = pthread_cond_init(&m_cond, NULL); - BX_CHECK(0 == result, "pthread_cond_init %d", result); + /// + ~Semaphore(); - BX_UNUSED(result); - } + /// + void post(uint32_t _count = 1); - ~Semaphore() - { - int result; - result = pthread_cond_destroy(&m_cond); - BX_CHECK(0 == result, "pthread_cond_destroy %d", result); - - result = pthread_mutex_destroy(&m_mutex); - BX_CHECK(0 == result, "pthread_mutex_destroy %d", result); - - BX_UNUSED(result); - } - - void post(uint32_t _count = 1) - { - int result = pthread_mutex_lock(&m_mutex); - BX_CHECK(0 == result, "pthread_mutex_lock %d", result); - - for (uint32_t ii = 0; ii < _count; ++ii) - { - result = pthread_cond_signal(&m_cond); - BX_CHECK(0 == result, "pthread_cond_signal %d", result); - } - - m_count += _count; - - result = pthread_mutex_unlock(&m_mutex); - BX_CHECK(0 == result, "pthread_mutex_unlock %d", result); - - BX_UNUSED(result); - } - - bool wait(int32_t _msecs = -1) - { - int result = pthread_mutex_lock(&m_mutex); - BX_CHECK(0 == result, "pthread_mutex_lock %d", result); - -# if BX_PLATFORM_NACL || BX_PLATFORM_OSX - BX_UNUSED(_msecs); - BX_CHECK(-1 == _msecs, "NaCl and OSX don't support pthread_cond_timedwait at this moment."); - while (0 == result - && 0 >= m_count) - { - result = pthread_cond_wait(&m_cond, &m_mutex); - } -# elif BX_PLATFORM_IOS - if (-1 == _msecs) - { - while (0 == result - && 0 >= m_count) - { - result = pthread_cond_wait(&m_cond, &m_mutex); - } - } - else - { - timespec ts; - ts.tv_sec = _msecs/1000; - ts.tv_nsec = (_msecs%1000)*1000; - - while (0 == result - && 0 >= m_count) - { - result = pthread_cond_timedwait_relative_np(&m_cond, &m_mutex, &ts); - } - } -# else - timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += _msecs/1000; - ts.tv_nsec += (_msecs%1000)*1000; - - while (0 == result - && 0 >= m_count) - { - result = pthread_cond_timedwait(&m_cond, &m_mutex, &ts); - } -# endif // BX_PLATFORM_NACL || BX_PLATFORM_OSX - bool ok = 0 == result; - - if (ok) - { - --m_count; - } - - result = pthread_mutex_unlock(&m_mutex); - BX_CHECK(0 == result, "pthread_mutex_unlock %d", result); - - BX_UNUSED(result); - - return ok; - } + /// + bool wait(int32_t _msecs = -1); private: +#if BX_PLATFORM_POSIX +# if BX_CONFIG_SEMAPHORE_PTHREAD pthread_mutex_t m_mutex; pthread_cond_t m_cond; int32_t m_count; - }; - -# else - - class Semaphore - { - BX_CLASS(Semaphore - , NO_COPY - , NO_ASSIGNMENT - ); - - public: - Semaphore() - { - int32_t result = sem_init(&m_handle, 0, 0); - BX_CHECK(0 == result, "sem_init failed. errno %d", errno); - BX_UNUSED(result); - } - - ~Semaphore() - { - int32_t result = sem_destroy(&m_handle); - BX_CHECK(0 == result, "sem_destroy failed. errno %d", errno); - BX_UNUSED(result); - } - - void post(uint32_t _count = 1) - { - int32_t result; - for (uint32_t ii = 0; ii < _count; ++ii) - { - result = sem_post(&m_handle); - BX_CHECK(0 == result, "sem_post failed. errno %d", errno); - } - BX_UNUSED(result); - } - - bool wait(int32_t _msecs = -1) - { -# if BX_PLATFORM_NACL || BX_PLATFORM_OSX - BX_CHECK(-1 == _msecs, "NaCl and OSX don't support sem_timedwait at this moment."); BX_UNUSED(_msecs); - return 0 == sem_wait(&m_handle); -# else - if (0 > _msecs) - { - int32_t result; - do - { - result = sem_wait(&m_handle); - } // keep waiting when interrupted by a signal handler... - while (-1 == result && EINTR == errno); - BX_CHECK(0 == result, "sem_wait failed. errno %d", errno); - return 0 == result; - } - - timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += _msecs/1000; - ts.tv_nsec += (_msecs%1000)*1000; - return 0 == sem_timedwait(&m_handle, &ts); -# endif // BX_PLATFORM_ - } - - private: + #else sem_t m_handle; - }; # endif // BX_CONFIG_SEMAPHORE_PTHREAD - #elif BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE || BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT - - class Semaphore - { - BX_CLASS(Semaphore - , NO_COPY - , NO_ASSIGNMENT - ); - - public: - Semaphore() - { -#if BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT - m_handle = CreateSemaphoreExW(NULL, 0, LONG_MAX, NULL, 0, SEMAPHORE_ALL_ACCESS); -#else - m_handle = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL); -#endif - BX_CHECK(NULL != m_handle, "Failed to create Semaphore!"); - } - - ~Semaphore() - { - CloseHandle(m_handle); - } - - void post(uint32_t _count = 1) const - { - ReleaseSemaphore(m_handle, _count, NULL); - } - - bool wait(int32_t _msecs = -1) const - { - DWORD milliseconds = (0 > _msecs) ? INFINITE : _msecs; -#if BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT - return WAIT_OBJECT_0 == WaitForSingleObjectEx(m_handle, milliseconds, FALSE); -#else - return WAIT_OBJECT_0 == WaitForSingleObject(m_handle, milliseconds); -#endif - } - - private: HANDLE m_handle; - }; - #endif // BX_PLATFORM_ + }; } // namespace bx diff --git a/include/bx/thread.h b/include/bx/thread.h index 8514a70..9515236 100644 --- a/include/bx/thread.h +++ b/include/bx/thread.h @@ -6,6 +6,8 @@ #ifndef BX_THREAD_H_HEADER_GUARD #define BX_THREAD_H_HEADER_GUARD +#include "bx.h" + #if BX_PLATFORM_POSIX # include # if defined(__FreeBSD__) @@ -26,8 +28,10 @@ using namespace Windows::System::Threading; namespace bx { + /// typedef int32_t (*ThreadFn)(void* _userData); + /// class Thread { BX_CLASS(Thread @@ -36,205 +40,36 @@ namespace bx ); public: - Thread() -#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT - : m_handle(INVALID_HANDLE_VALUE) - , m_threadId(UINT32_MAX) -#elif BX_PLATFORM_POSIX - : m_handle(0) -#endif // BX_PLATFORM_ - , m_fn(NULL) - , m_userData(NULL) - , m_stackSize(0) - , m_exitCode(0 /*EXIT_SUCCESS*/) - , m_running(false) - { - } + /// + Thread(); - virtual ~Thread() - { - if (m_running) - { - shutdown(); - } - } + /// + virtual ~Thread(); - void init(ThreadFn _fn, void* _userData = NULL, uint32_t _stackSize = 0, const char* _name = NULL) - { - BX_CHECK(!m_running, "Already running!"); + /// + void init(ThreadFn _fn, void* _userData = NULL, uint32_t _stackSize = 0, const char* _name = NULL); - m_fn = _fn; - m_userData = _userData; - m_stackSize = _stackSize; - m_running = true; + /// + void shutdown(); -#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE - m_handle = ::CreateThread(NULL - , m_stackSize - , (LPTHREAD_START_ROUTINE)threadFunc - , this - , 0 - , NULL - ); -#elif BX_PLATFORM_WINRT - m_handle = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); - auto workItemHandler = ref new WorkItemHandler([=](IAsyncAction^) - { - m_exitCode = threadFunc(this); - SetEvent(m_handle); - }, CallbackContext::Any); + /// + bool isRunning() const; - ThreadPool::RunAsync(workItemHandler, WorkItemPriority::Normal, WorkItemOptions::TimeSliced); -#elif BX_PLATFORM_POSIX - int result; - BX_UNUSED(result); + /// + int32_t getExitCode() const; - pthread_attr_t attr; - result = pthread_attr_init(&attr); - BX_CHECK(0 == result, "pthread_attr_init failed! %d", result); - - if (0 != m_stackSize) - { - result = pthread_attr_setstacksize(&attr, m_stackSize); - BX_CHECK(0 == result, "pthread_attr_setstacksize failed! %d", result); - } - -// sched_param sched; -// sched.sched_priority = 0; -// result = pthread_attr_setschedparam(&attr, &sched); -// BX_CHECK(0 == result, "pthread_attr_setschedparam failed! %d", result); - - result = pthread_create(&m_handle, &attr, &threadFunc, this); - BX_CHECK(0 == result, "pthread_attr_setschedparam failed! %d", result); -#else -# error "Not implemented!" -#endif // BX_PLATFORM_ - - m_sem.wait(); - - if (NULL != _name) - { - setThreadName(_name); - } - } - - void shutdown() - { - BX_CHECK(m_running, "Not running!"); -#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 - WaitForSingleObject(m_handle, INFINITE); - GetExitCodeThread(m_handle, (DWORD*)&m_exitCode); - CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; -#elif BX_PLATFORM_WINRT - WaitForSingleObjectEx(m_handle, INFINITE, FALSE); - CloseHandle(m_handle); - m_handle = INVALID_HANDLE_VALUE; -#elif BX_PLATFORM_POSIX - union - { - void* ptr; - int32_t i; - } cast; - pthread_join(m_handle, &cast.ptr); - m_exitCode = cast.i; - m_handle = 0; -#endif // BX_PLATFORM_ - m_running = false; - } - - bool isRunning() const - { - return m_running; - } - - int32_t getExitCode() const - { - return m_exitCode; - } - - void setThreadName(const char* _name) - { -#if BX_PLATFORM_OSX || BX_PLATFORM_IOS - pthread_setname_np(_name); -#elif (BX_CRT_GLIBC >= 21200) && ! BX_PLATFORM_HURD - pthread_setname_np(m_handle, _name); -#elif BX_PLATFORM_LINUX - prctl(PR_SET_NAME,_name, 0, 0, 0); -#elif BX_PLATFORM_BSD -# ifdef __NetBSD__ - pthread_setname_np(m_handle, "%s", (void*)_name); -# else - pthread_set_name_np(m_handle, _name); -# endif // __NetBSD__ -#elif BX_PLATFORM_WINDOWS && BX_COMPILER_MSVC -# pragma pack(push, 8) - struct ThreadName - { - DWORD type; - LPCSTR name; - DWORD id; - DWORD flags; - }; -# pragma pack(pop) - ThreadName tn; - tn.type = 0x1000; - tn.name = _name; - tn.id = m_threadId; - tn.flags = 0; - - __try - { - RaiseException(0x406d1388 - , 0 - , sizeof(tn)/4 - , reinterpret_cast(&tn) - ); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } -#else - BX_UNUSED(_name); -#endif // BX_PLATFORM_ - } + /// + void setThreadName(const char* _name); private: - int32_t entry() - { -#if BX_PLATFORM_WINDOWS - m_threadId = ::GetCurrentThreadId(); -#endif // BX_PLATFORM_WINDOWS - - m_sem.post(); - return m_fn(m_userData); - } - -#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_WINRT - static DWORD WINAPI threadFunc(LPVOID _arg) - { - Thread* thread = (Thread*)_arg; - int32_t result = thread->entry(); - return result; - } -#else - static void* threadFunc(void* _arg) - { - Thread* thread = (Thread*)_arg; - union - { - void* ptr; - int32_t i; - } cast; - cast.i = thread->entry(); - return cast.ptr; - } -#endif // BX_PLATFORM_ + int32_t entry(); #if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT + static DWORD WINAPI threadFunc(LPVOID _arg); HANDLE m_handle; DWORD m_threadId; #elif BX_PLATFORM_POSIX + static void* threadFunc(void* _arg); pthread_t m_handle; #endif // BX_PLATFORM_ @@ -246,71 +81,34 @@ namespace bx bool m_running; }; + /// + class TlsData + { + public: + /// + TlsData(); + + /// + ~TlsData(); + + /// + void* get() const; + + /// + void set(void* _ptr); + + private: #if BX_PLATFORM_WINDOWS - class TlsData - { - public: - TlsData() - { - m_id = TlsAlloc(); - BX_CHECK(TLS_OUT_OF_INDEXES != m_id, "Failed to allocated TLS index (err: 0x%08x).", GetLastError() ); - } - - ~TlsData() - { - BOOL result = TlsFree(m_id); - BX_CHECK(0 != result, "Failed to free TLS index (err: 0x%08x).", GetLastError() ); BX_UNUSED(result); - } - - void* get() const - { - return TlsGetValue(m_id); - } - - void set(void* _ptr) - { - TlsSetValue(m_id, _ptr); - } - - private: uint32_t m_id; - }; - #elif !(BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT) - - class TlsData - { - public: - TlsData() - { - int result = pthread_key_create(&m_id, NULL); - BX_CHECK(0 == result, "pthread_key_create failed %d.", result); BX_UNUSED(result); - } - - ~TlsData() - { - int result = pthread_key_delete(m_id); - BX_CHECK(0 == result, "pthread_key_delete failed %d.", result); BX_UNUSED(result); - } - - void* get() const - { - return pthread_getspecific(m_id); - } - - void set(void* _ptr) - { - int result = pthread_setspecific(m_id, _ptr); - BX_CHECK(0 == result, "pthread_setspecific failed %d.", result); BX_UNUSED(result); - } - - private: pthread_key_t m_id; - }; #endif // BX_PLATFORM_* + }; } // namespace bx #endif // BX_CONFIG_SUPPORTS_THREADING +#include "thread.inl" + #endif // BX_THREAD_H_HEADER_GUARD diff --git a/include/bx/thread.inl b/include/bx/thread.inl new file mode 100644 index 0000000..38fc49d --- /dev/null +++ b/include/bx/thread.inl @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#ifndef BX_THREAD_H_HEADER_GUARD +# error "Must be included from bx/thread.h!" +#endif // BX_THREAD_H_HEADER_GUARD + +#if BX_CONFIG_SUPPORTS_THREADING + +namespace bx +{ +#if BX_PLATFORM_WINDOWS + inline TlsData::TlsData() + { + m_id = TlsAlloc(); + BX_CHECK(TLS_OUT_OF_INDEXES != m_id, "Failed to allocated TLS index (err: 0x%08x).", GetLastError() ); + } + + inline TlsData::~TlsData() + { + BOOL result = TlsFree(m_id); + BX_CHECK(0 != result, "Failed to free TLS index (err: 0x%08x).", GetLastError() ); BX_UNUSED(result); + } + + inline void* TlsData::get() const + { + return TlsGetValue(m_id); + } + + inline void TlsData::set(void* _ptr) + { + TlsSetValue(m_id, _ptr); + } + +#elif !(BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT) + + inline TlsData::TlsData() + { + int result = pthread_key_create(&m_id, NULL); + BX_CHECK(0 == result, "pthread_key_create failed %d.", result); BX_UNUSED(result); + } + + inline TlsData::~TlsData() + { + int result = pthread_key_delete(m_id); + BX_CHECK(0 == result, "pthread_key_delete failed %d.", result); BX_UNUSED(result); + } + + inline void* TlsData::get() const + { + return pthread_getspecific(m_id); + } + + inline void TlsData::set(void* _ptr) + { + int result = pthread_setspecific(m_id, _ptr); + BX_CHECK(0 == result, "pthread_setspecific failed %d.", result); BX_UNUSED(result); + } +#endif // BX_PLATFORM_* + +} // namespace bx + +#endif // BX_CONFIG_SUPPORTS_THREADING diff --git a/src/sem.cpp b/src/sem.cpp new file mode 100644 index 0000000..2d3ca15 --- /dev/null +++ b/src/sem.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2010-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include + +#if BX_CONFIG_SUPPORTS_THREADING + +#if BX_PLATFORM_POSIX +# include +# include +# include +# include +#elif BX_PLATFORM_XBOXONE +# include +#elif BX_PLATFORM_XBOX360 || BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT +# include +# include +#endif // BX_PLATFORM_ + +namespace bx +{ +#if BX_PLATFORM_POSIX + +# if BX_CONFIG_SEMAPHORE_PTHREAD + Semaphore::Semaphore() + : m_count(0) + { + int result; + result = pthread_mutex_init(&m_mutex, NULL); + BX_CHECK(0 == result, "pthread_mutex_init %d", result); + + result = pthread_cond_init(&m_cond, NULL); + BX_CHECK(0 == result, "pthread_cond_init %d", result); + + BX_UNUSED(result); + } + + Semaphore::~Semaphore() + { + int result; + result = pthread_cond_destroy(&m_cond); + BX_CHECK(0 == result, "pthread_cond_destroy %d", result); + + result = pthread_mutex_destroy(&m_mutex); + BX_CHECK(0 == result, "pthread_mutex_destroy %d", result); + + BX_UNUSED(result); + } + + void Semaphore::post(uint32_t _count) + { + int result = pthread_mutex_lock(&m_mutex); + BX_CHECK(0 == result, "pthread_mutex_lock %d", result); + + for (uint32_t ii = 0; ii < _count; ++ii) + { + result = pthread_cond_signal(&m_cond); + BX_CHECK(0 == result, "pthread_cond_signal %d", result); + } + + m_count += _count; + + result = pthread_mutex_unlock(&m_mutex); + BX_CHECK(0 == result, "pthread_mutex_unlock %d", result); + + BX_UNUSED(result); + } + + bool Semaphore::wait(int32_t _msecs) + { + int result = pthread_mutex_lock(&m_mutex); + BX_CHECK(0 == result, "pthread_mutex_lock %d", result); + +# if BX_PLATFORM_NACL || BX_PLATFORM_OSX + BX_UNUSED(_msecs); + BX_CHECK(-1 == _msecs, "NaCl and OSX don't support pthread_cond_timedwait at this moment."); + while (0 == result + && 0 >= m_count) + { + result = pthread_cond_wait(&m_cond, &m_mutex); + } +# elif BX_PLATFORM_IOS + if (-1 == _msecs) + { + while (0 == result + && 0 >= m_count) + { + result = pthread_cond_wait(&m_cond, &m_mutex); + } + } + else + { + timespec ts; + ts.tv_sec = _msecs/1000; + ts.tv_nsec = (_msecs%1000)*1000; + + while (0 == result + && 0 >= m_count) + { + result = pthread_cond_timedwait_relative_np(&m_cond, &m_mutex, &ts); + } + } +# else + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += _msecs/1000; + ts.tv_nsec += (_msecs%1000)*1000; + + while (0 == result + && 0 >= m_count) + { + result = pthread_cond_timedwait(&m_cond, &m_mutex, &ts); + } +# endif // BX_PLATFORM_NACL || BX_PLATFORM_OSX + bool ok = 0 == result; + + if (ok) + { + --m_count; + } + + result = pthread_mutex_unlock(&m_mutex); + BX_CHECK(0 == result, "pthread_mutex_unlock %d", result); + + BX_UNUSED(result); + + return ok; + } + +# else + + Semaphore::Semaphore() + { + int32_t result = sem_init(&m_handle, 0, 0); + BX_CHECK(0 == result, "sem_init failed. errno %d", errno); + BX_UNUSED(result); + } + + Semaphore::~Semaphore() + { + int32_t result = sem_destroy(&m_handle); + BX_CHECK(0 == result, "sem_destroy failed. errno %d", errno); + BX_UNUSED(result); + } + + void Semaphore::post(uint32_t _count) + { + int32_t result; + for (uint32_t ii = 0; ii < _count; ++ii) + { + result = sem_post(&m_handle); + BX_CHECK(0 == result, "sem_post failed. errno %d", errno); + } + BX_UNUSED(result); + } + + bool Semaphore::wait(int32_t _msecs) + { +# if BX_PLATFORM_NACL || BX_PLATFORM_OSX + BX_CHECK(-1 == _msecs, "NaCl and OSX don't support sem_timedwait at this moment."); BX_UNUSED(_msecs); + return 0 == sem_wait(&m_handle); +# else + if (0 > _msecs) + { + int32_t result; + do + { + result = sem_wait(&m_handle); + } // keep waiting when interrupted by a signal handler... + while (-1 == result && EINTR == errno); + BX_CHECK(0 == result, "sem_wait failed. errno %d", errno); + return 0 == result; + } + + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += _msecs/1000; + ts.tv_nsec += (_msecs%1000)*1000; + return 0 == sem_timedwait(&m_handle, &ts); +# endif // BX_PLATFORM_ + } +# endif // BX_CONFIG_SEMAPHORE_PTHREAD + +#elif BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE || BX_PLATFORM_WINDOWS || BX_PLATFORM_WINRT + + Semaphore::Semaphore() + { +#if BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT + m_handle = CreateSemaphoreExW(NULL, 0, LONG_MAX, NULL, 0, SEMAPHORE_ALL_ACCESS); +#else + m_handle = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL); +#endif + BX_CHECK(NULL != m_handle, "Failed to create Semaphore!"); + } + + Semaphore::~Semaphore() + { + CloseHandle(m_handle); + } + + void Semaphore::post(uint32_t _count) + { + ReleaseSemaphore(m_handle, _count, NULL); + } + + bool Semaphore::wait(int32_t _msecs) + { + DWORD milliseconds = (0 > _msecs) ? INFINITE : _msecs; +#if BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT + return WAIT_OBJECT_0 == WaitForSingleObjectEx(m_handle, milliseconds, FALSE); +#else + return WAIT_OBJECT_0 == WaitForSingleObject(m_handle, milliseconds); +#endif + } +#endif // BX_PLATFORM_ + +} // namespace bx + +#endif // BX_CONFIG_SUPPORTS_THREADING diff --git a/src/thread.cpp b/src/thread.cpp new file mode 100644 index 0000000..92045bf --- /dev/null +++ b/src/thread.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2010-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include + +#if BX_CONFIG_SUPPORTS_THREADING + +namespace bx +{ + Thread::Thread() +#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT + : m_handle(INVALID_HANDLE_VALUE) + , m_threadId(UINT32_MAX) +#elif BX_PLATFORM_POSIX + : m_handle(0) +#endif // BX_PLATFORM_ + , m_fn(NULL) + , m_userData(NULL) + , m_stackSize(0) + , m_exitCode(0 /*EXIT_SUCCESS*/) + , m_running(false) + { + } + + Thread::~Thread() + { + if (m_running) + { + shutdown(); + } + } + + void Thread::init(ThreadFn _fn, void* _userData, uint32_t _stackSize, const char* _name) + { + BX_CHECK(!m_running, "Already running!"); + + m_fn = _fn; + m_userData = _userData; + m_stackSize = _stackSize; + m_running = true; + +#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_XBOXONE + m_handle = ::CreateThread(NULL + , m_stackSize + , (LPTHREAD_START_ROUTINE)threadFunc + , this + , 0 + , NULL + ); +#elif BX_PLATFORM_WINRT + m_handle = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); + auto workItemHandler = ref new WorkItemHandler([=](IAsyncAction^) + { + m_exitCode = threadFunc(this); + SetEvent(m_handle); + }, CallbackContext::Any); + + ThreadPool::RunAsync(workItemHandler, WorkItemPriority::Normal, WorkItemOptions::TimeSliced); +#elif BX_PLATFORM_POSIX + int result; + BX_UNUSED(result); + + pthread_attr_t attr; + result = pthread_attr_init(&attr); + BX_CHECK(0 == result, "pthread_attr_init failed! %d", result); + + if (0 != m_stackSize) + { + result = pthread_attr_setstacksize(&attr, m_stackSize); + BX_CHECK(0 == result, "pthread_attr_setstacksize failed! %d", result); + } + + // sched_param sched; + // sched.sched_priority = 0; + // result = pthread_attr_setschedparam(&attr, &sched); + // BX_CHECK(0 == result, "pthread_attr_setschedparam failed! %d", result); + + result = pthread_create(&m_handle, &attr, &threadFunc, this); + BX_CHECK(0 == result, "pthread_attr_setschedparam failed! %d", result); +#else +# error "Not implemented!" +#endif // BX_PLATFORM_ + + m_sem.wait(); + + if (NULL != _name) + { + setThreadName(_name); + } + } + + void Thread::shutdown() + { + BX_CHECK(m_running, "Not running!"); +#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 + WaitForSingleObject(m_handle, INFINITE); + GetExitCodeThread(m_handle, (DWORD*)&m_exitCode); + CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +#elif BX_PLATFORM_WINRT + WaitForSingleObjectEx(m_handle, INFINITE, FALSE); + CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +#elif BX_PLATFORM_POSIX + union + { + void* ptr; + int32_t i; + } cast; + pthread_join(m_handle, &cast.ptr); + m_exitCode = cast.i; + m_handle = 0; +#endif // BX_PLATFORM_ + m_running = false; + } + + bool Thread::isRunning() const + { + return m_running; + } + + int32_t Thread::getExitCode() const + { + return m_exitCode; + } + + void Thread::setThreadName(const char* _name) + { +#if BX_PLATFORM_OSX || BX_PLATFORM_IOS + pthread_setname_np(_name); +#elif (BX_CRT_GLIBC >= 21200) && ! BX_PLATFORM_HURD + pthread_setname_np(m_handle, _name); +#elif BX_PLATFORM_LINUX + prctl(PR_SET_NAME,_name, 0, 0, 0); +#elif BX_PLATFORM_BSD +# ifdef __NetBSD__ + pthread_setname_np(m_handle, "%s", (void*)_name); +# else + pthread_set_name_np(m_handle, _name); +# endif // __NetBSD__ +#elif BX_PLATFORM_WINDOWS && BX_COMPILER_MSVC +# pragma pack(push, 8) + struct ThreadName + { + DWORD type; + LPCSTR name; + DWORD id; + DWORD flags; + }; +# pragma pack(pop) + ThreadName tn; + tn.type = 0x1000; + tn.name = _name; + tn.id = m_threadId; + tn.flags = 0; + + __try + { + RaiseException(0x406d1388 + , 0 + , sizeof(tn)/4 + , reinterpret_cast(&tn) + ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#else + BX_UNUSED(_name); +#endif // BX_PLATFORM_ + } + + int32_t Thread::entry() + { +#if BX_PLATFORM_WINDOWS + m_threadId = ::GetCurrentThreadId(); +#endif // BX_PLATFORM_WINDOWS + + m_sem.post(); + return m_fn(m_userData); + } + +#if BX_PLATFORM_WINDOWS || BX_PLATFORM_XBOX360 || BX_PLATFORM_WINRT + DWORD WINAPI Thread::threadFunc(LPVOID _arg) + { + Thread* thread = (Thread*)_arg; + int32_t result = thread->entry(); + return result; + } +#else + void* Thread::threadFunc(void* _arg) + { + Thread* thread = (Thread*)_arg; + union + { + void* ptr; + int32_t i; + } cast; + cast.i = thread->entry(); + return cast.ptr; + } +#endif // BX_PLATFORM_ + +} // namespace bx + +#endif // BX_CONFIG_SUPPORTS_THREADING