thread.cpp 9.4 KB

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