thread.cpp 7.6 KB

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