thread.cpp 9.4 KB

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