thread.cpp 7.4 KB

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