thread.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. * Copyright 2010-2017 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
  4. */
  5. #include "bx_p.h"
  6. #include <bx/thread.h>
  7. #if BX_PLATFORM_ANDROID \
  8. || BX_PLATFORM_LINUX \
  9. || BX_PLATFORM_IOS \
  10. || BX_PLATFORM_OSX \
  11. || BX_PLATFORM_PS4 \
  12. || BX_PLATFORM_RPI
  13. # include <pthread.h>
  14. # if defined(__FreeBSD__)
  15. # include <pthread_np.h>
  16. # endif
  17. # if BX_PLATFORM_LINUX && (BX_CRT_GLIBC < 21200)
  18. # include <sys/prctl.h>
  19. # endif // BX_PLATFORM_
  20. #elif BX_PLATFORM_WINDOWS \
  21. || BX_PLATFORM_WINRT \
  22. || BX_PLATFORM_XBOXONE
  23. # include <windows.h>
  24. # include <limits.h>
  25. # include <errno.h>
  26. # if BX_PLATFORM_WINRT
  27. using namespace Platform;
  28. using namespace Windows::Foundation;
  29. using namespace Windows::System::Threading;
  30. # endif // BX_PLATFORM_WINRT
  31. #endif // BX_PLATFORM_
  32. #if BX_CONFIG_SUPPORTS_THREADING
  33. namespace bx
  34. {
  35. static AllocatorI* getAllocator()
  36. {
  37. static DefaultAllocator s_allocator;
  38. return &s_allocator;
  39. }
  40. struct ThreadInternal
  41. {
  42. #if BX_PLATFORM_WINDOWS \
  43. || BX_PLATFORM_WINRT \
  44. || BX_PLATFORM_XBOXONE
  45. static DWORD WINAPI threadFunc(LPVOID _arg);
  46. HANDLE m_handle;
  47. DWORD m_threadId;
  48. #elif BX_PLATFORM_POSIX
  49. static void* threadFunc(void* _arg);
  50. pthread_t m_handle;
  51. #endif // BX_PLATFORM_
  52. };
  53. #if BX_PLATFORM_WINDOWS \
  54. || BX_PLATFORM_XBOXONE \
  55. || BX_PLATFORM_WINRT
  56. DWORD WINAPI ThreadInternal::threadFunc(LPVOID _arg)
  57. {
  58. Thread* thread = (Thread*)_arg;
  59. int32_t result = thread->entry();
  60. return result;
  61. }
  62. #else
  63. void* ThreadInternal::threadFunc(void* _arg)
  64. {
  65. Thread* thread = (Thread*)_arg;
  66. union
  67. {
  68. void* ptr;
  69. int32_t i;
  70. } cast;
  71. cast.i = thread->entry();
  72. return cast.ptr;
  73. }
  74. #endif // BX_PLATFORM_
  75. Thread::Thread()
  76. : m_fn(NULL)
  77. , m_userData(NULL)
  78. , m_queue(getAllocator() )
  79. , m_stackSize(0)
  80. , m_exitCode(kExitSuccess)
  81. , m_running(false)
  82. {
  83. BX_STATIC_ASSERT(sizeof(ThreadInternal) <= sizeof(m_internal) );
  84. ThreadInternal* ti = (ThreadInternal*)m_internal;
  85. #if BX_PLATFORM_WINDOWS \
  86. || BX_PLATFORM_WINRT \
  87. || BX_PLATFORM_XBOXONE
  88. ti->m_handle = INVALID_HANDLE_VALUE;
  89. ti->m_threadId = UINT32_MAX;
  90. #elif BX_PLATFORM_POSIX
  91. ti->m_handle = 0;
  92. #endif // BX_PLATFORM_
  93. }
  94. Thread::~Thread()
  95. {
  96. if (m_running)
  97. {
  98. shutdown();
  99. }
  100. }
  101. void Thread::init(ThreadFn _fn, void* _userData, uint32_t _stackSize, const char* _name)
  102. {
  103. BX_CHECK(!m_running, "Already running!");
  104. m_fn = _fn;
  105. m_userData = _userData;
  106. m_stackSize = _stackSize;
  107. m_running = true;
  108. ThreadInternal* ti = (ThreadInternal*)m_internal;
  109. #if BX_PLATFORM_WINDOWS \
  110. || BX_PLATFORM_XBOXONE
  111. ti->m_handle = ::CreateThread(NULL
  112. , m_stackSize
  113. , (LPTHREAD_START_ROUTINE)ti->threadFunc
  114. , this
  115. , 0
  116. , NULL
  117. );
  118. #elif BX_PLATFORM_WINRT
  119. ti->m_handle = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
  120. auto workItemHandler = ref new WorkItemHandler([=](IAsyncAction^)
  121. {
  122. m_exitCode = ti->threadFunc(this);
  123. SetEvent(ti->m_handle);
  124. }
  125. , CallbackContext::Any
  126. );
  127. ThreadPool::RunAsync(workItemHandler, WorkItemPriority::Normal, WorkItemOptions::TimeSliced);
  128. #elif BX_PLATFORM_POSIX
  129. int result;
  130. BX_UNUSED(result);
  131. pthread_attr_t attr;
  132. result = pthread_attr_init(&attr);
  133. BX_CHECK(0 == result, "pthread_attr_init failed! %d", result);
  134. if (0 != m_stackSize)
  135. {
  136. result = pthread_attr_setstacksize(&attr, m_stackSize);
  137. BX_CHECK(0 == result, "pthread_attr_setstacksize failed! %d", result);
  138. }
  139. result = pthread_create(&ti->m_handle, &attr, &ti->threadFunc, this);
  140. BX_CHECK(0 == result, "pthread_attr_setschedparam failed! %d", result);
  141. #else
  142. # error "Not implemented!"
  143. #endif // BX_PLATFORM_
  144. m_sem.wait();
  145. if (NULL != _name)
  146. {
  147. setThreadName(_name);
  148. }
  149. }
  150. void Thread::shutdown()
  151. {
  152. BX_CHECK(m_running, "Not running!");
  153. ThreadInternal* ti = (ThreadInternal*)m_internal;
  154. #if BX_PLATFORM_WINDOWS
  155. WaitForSingleObject(ti->m_handle, INFINITE);
  156. GetExitCodeThread(ti->m_handle, (DWORD*)&m_exitCode);
  157. CloseHandle(ti->m_handle);
  158. ti->m_handle = INVALID_HANDLE_VALUE;
  159. #elif BX_PLATFORM_WINRT || BX_PLATFORM_XBOXONE
  160. WaitForSingleObjectEx(ti->m_handle, INFINITE, FALSE);
  161. CloseHandle(ti->m_handle);
  162. ti->m_handle = INVALID_HANDLE_VALUE;
  163. #elif BX_PLATFORM_POSIX
  164. union
  165. {
  166. void* ptr;
  167. int32_t i;
  168. } cast;
  169. pthread_join(ti->m_handle, &cast.ptr);
  170. m_exitCode = cast.i;
  171. ti->m_handle = 0;
  172. #endif // BX_PLATFORM_
  173. m_running = false;
  174. }
  175. bool Thread::isRunning() const
  176. {
  177. return m_running;
  178. }
  179. int32_t Thread::getExitCode() const
  180. {
  181. return m_exitCode;
  182. }
  183. void Thread::setThreadName(const char* _name)
  184. {
  185. ThreadInternal* ti = (ThreadInternal*)m_internal;
  186. BX_UNUSED(ti);
  187. #if BX_PLATFORM_OSX || BX_PLATFORM_IOS
  188. pthread_setname_np(_name);
  189. #elif (BX_CRT_GLIBC >= 21200) && ! BX_PLATFORM_HURD
  190. pthread_setname_np(ti->m_handle, _name);
  191. #elif BX_PLATFORM_LINUX
  192. prctl(PR_SET_NAME,_name, 0, 0, 0);
  193. #elif BX_PLATFORM_BSD
  194. # ifdef __NetBSD__
  195. pthread_setname_np(ti->m_handle, "%s", (void*)_name);
  196. # else
  197. pthread_set_name_np(ti->m_handle, _name);
  198. # endif // __NetBSD__
  199. #elif BX_PLATFORM_WINDOWS && BX_COMPILER_MSVC
  200. # pragma pack(push, 8)
  201. struct ThreadName
  202. {
  203. DWORD type;
  204. LPCSTR name;
  205. DWORD id;
  206. DWORD flags;
  207. };
  208. # pragma pack(pop)
  209. ThreadName tn;
  210. tn.type = 0x1000;
  211. tn.name = _name;
  212. tn.id = ti->m_threadId;
  213. tn.flags = 0;
  214. __try
  215. {
  216. RaiseException(0x406d1388
  217. , 0
  218. , sizeof(tn)/4
  219. , reinterpret_cast<ULONG_PTR*>(&tn)
  220. );
  221. }
  222. __except(EXCEPTION_EXECUTE_HANDLER)
  223. {
  224. }
  225. #else
  226. BX_UNUSED(_name);
  227. #endif // BX_PLATFORM_
  228. }
  229. void Thread::push(void* _ptr)
  230. {
  231. m_queue.push(_ptr);
  232. }
  233. void* Thread::pop()
  234. {
  235. void* ptr = m_queue.pop();
  236. return ptr;
  237. }
  238. int32_t Thread::entry()
  239. {
  240. #if BX_PLATFORM_WINDOWS
  241. ThreadInternal* ti = (ThreadInternal*)m_internal;
  242. ti->m_threadId = ::GetCurrentThreadId();
  243. #endif // BX_PLATFORM_WINDOWS
  244. m_sem.post();
  245. int32_t result = m_fn(this, m_userData);
  246. return result;
  247. }
  248. struct TlsDataInternal
  249. {
  250. #if BX_PLATFORM_WINDOWS
  251. uint32_t m_id;
  252. #elif !(BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT)
  253. pthread_key_t m_id;
  254. #endif // BX_PLATFORM_*
  255. };
  256. #if BX_PLATFORM_WINDOWS
  257. TlsData::TlsData()
  258. {
  259. BX_STATIC_ASSERT(sizeof(TlsDataInternal) <= sizeof(m_internal) );
  260. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  261. ti->m_id = TlsAlloc();
  262. BX_CHECK(TLS_OUT_OF_INDEXES != ti->m_id, "Failed to allocated TLS index (err: 0x%08x).", GetLastError() );
  263. }
  264. TlsData::~TlsData()
  265. {
  266. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  267. BOOL result = TlsFree(ti->m_id);
  268. BX_CHECK(0 != result, "Failed to free TLS index (err: 0x%08x).", GetLastError() ); BX_UNUSED(result);
  269. }
  270. void* TlsData::get() const
  271. {
  272. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  273. return TlsGetValue(ti->m_id);
  274. }
  275. void TlsData::set(void* _ptr)
  276. {
  277. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  278. TlsSetValue(ti->m_id, _ptr);
  279. }
  280. #elif !(BX_PLATFORM_XBOXONE || BX_PLATFORM_WINRT)
  281. TlsData::TlsData()
  282. {
  283. BX_STATIC_ASSERT(sizeof(TlsDataInternal) <= sizeof(m_internal) );
  284. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  285. int result = pthread_key_create(&ti->m_id, NULL);
  286. BX_CHECK(0 == result, "pthread_key_create failed %d.", result); BX_UNUSED(result);
  287. }
  288. TlsData::~TlsData()
  289. {
  290. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  291. int result = pthread_key_delete(ti->m_id);
  292. BX_CHECK(0 == result, "pthread_key_delete failed %d.", result); BX_UNUSED(result);
  293. }
  294. void* TlsData::get() const
  295. {
  296. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  297. return pthread_getspecific(ti->m_id);
  298. }
  299. void TlsData::set(void* _ptr)
  300. {
  301. TlsDataInternal* ti = (TlsDataInternal*)m_internal;
  302. int result = pthread_setspecific(ti->m_id, _ptr);
  303. BX_CHECK(0 == result, "pthread_setspecific failed %d.", result); BX_UNUSED(result);
  304. }
  305. #endif // BX_PLATFORM_*
  306. } // namespace bx
  307. #endif // BX_CONFIG_SUPPORTS_THREADING