Clone of mesa.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

threads_win32.h 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /*
  2. * C11 <threads.h> emulation library
  3. *
  4. * (C) Copyright yohhoy 2012.
  5. * Distributed under the Boost Software License, Version 1.0.
  6. *
  7. * Permission is hereby granted, free of charge, to any person or organization
  8. * obtaining a copy of the software and accompanying documentation covered by
  9. * this license (the "Software") to use, reproduce, display, distribute,
  10. * execute, and transmit the Software, and to prepare [[derivative work]]s of the
  11. * Software, and to permit third-parties to whom the Software is furnished to
  12. * do so, all subject to the following:
  13. *
  14. * The copyright notices in the Software and this entire statement, including
  15. * the above license grant, this restriction and the following disclaimer,
  16. * must be included in all copies of the Software, in whole or in part, and
  17. * all derivative works of the Software, unless such copies or derivative
  18. * works are solely in the form of machine-executable object code generated by
  19. * a source language processor.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  24. * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  25. * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  26. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  27. * DEALINGS IN THE SOFTWARE.
  28. */
  29. #ifndef assert
  30. #include <assert.h>
  31. #endif
  32. #include <limits.h>
  33. #include <errno.h>
  34. #include <process.h> // MSVCRT
  35. #include <stdlib.h>
  36. /*
  37. Configuration macro:
  38. EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  39. Use native WindowsAPI one-time initialization function.
  40. (requires WinVista or later)
  41. Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
  42. EMULATED_THREADS_USE_NATIVE_CV
  43. Use native WindowsAPI condition variable object.
  44. (requires WinVista or later)
  45. Otherwise use emulated implementation for WinXP.
  46. EMULATED_THREADS_TSS_DTOR_SLOTNUM
  47. Max registerable TSS dtor number.
  48. */
  49. // XXX: Retain XP compatability
  50. #if 0
  51. #if _WIN32_WINNT >= 0x0600
  52. // Prefer native WindowsAPI on newer environment.
  53. #if !defined(__MINGW32__)
  54. #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  55. #endif
  56. #define EMULATED_THREADS_USE_NATIVE_CV
  57. #endif
  58. #endif
  59. #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
  60. #include <windows.h>
  61. // check configuration
  62. #if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)
  63. #error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
  64. #endif
  65. #if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600)
  66. #error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600
  67. #endif
  68. /*---------------------------- macros ----------------------------*/
  69. #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  70. #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
  71. #else
  72. #define ONCE_FLAG_INIT {0}
  73. #endif
  74. #define TSS_DTOR_ITERATIONS 1
  75. // FIXME: temporary non-standard hack to ease transition
  76. #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
  77. /*---------------------------- types ----------------------------*/
  78. typedef struct cnd_t {
  79. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  80. CONDITION_VARIABLE condvar;
  81. #else
  82. int blocked;
  83. int gone;
  84. int to_unblock;
  85. HANDLE sem_queue;
  86. HANDLE sem_gate;
  87. CRITICAL_SECTION monitor;
  88. #endif
  89. } cnd_t;
  90. typedef HANDLE thrd_t;
  91. typedef DWORD tss_t;
  92. typedef CRITICAL_SECTION mtx_t;
  93. #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  94. typedef INIT_ONCE once_flag;
  95. #else
  96. typedef struct once_flag_t {
  97. volatile LONG status;
  98. } once_flag;
  99. #endif
  100. static inline void * tss_get(tss_t key);
  101. static inline void thrd_yield(void);
  102. static inline int mtx_trylock(mtx_t *mtx);
  103. static inline int mtx_lock(mtx_t *mtx);
  104. static inline int mtx_unlock(mtx_t *mtx);
  105. /*
  106. Implementation limits:
  107. - Conditionally emulation for "Initialization functions"
  108. (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
  109. - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
  110. */
  111. static void impl_tss_dtor_invoke(void); // forward decl.
  112. struct impl_thrd_param {
  113. thrd_start_t func;
  114. void *arg;
  115. };
  116. static unsigned __stdcall impl_thrd_routine(void *p)
  117. {
  118. struct impl_thrd_param pack;
  119. int code;
  120. memcpy(&pack, p, sizeof(struct impl_thrd_param));
  121. free(p);
  122. code = pack.func(pack.arg);
  123. impl_tss_dtor_invoke();
  124. return (unsigned)code;
  125. }
  126. static DWORD impl_xtime2msec(const xtime *xt)
  127. {
  128. return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L));
  129. }
  130. #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  131. struct impl_call_once_param { void (*func)(void); };
  132. static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
  133. {
  134. struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
  135. (param->func)();
  136. ((void)InitOnce); ((void)Context); // suppress warning
  137. return TRUE;
  138. }
  139. #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  140. #ifndef EMULATED_THREADS_USE_NATIVE_CV
  141. /*
  142. Note:
  143. The implementation of condition variable is ported from Boost.Interprocess
  144. See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
  145. */
  146. static void impl_cond_do_signal(cnd_t *cond, int broadcast)
  147. {
  148. int nsignal = 0;
  149. EnterCriticalSection(&cond->monitor);
  150. if (cond->to_unblock != 0) {
  151. if (cond->blocked == 0) {
  152. LeaveCriticalSection(&cond->monitor);
  153. return;
  154. }
  155. if (broadcast) {
  156. cond->to_unblock += nsignal = cond->blocked;
  157. cond->blocked = 0;
  158. } else {
  159. nsignal = 1;
  160. cond->to_unblock++;
  161. cond->blocked--;
  162. }
  163. } else if (cond->blocked > cond->gone) {
  164. WaitForSingleObject(cond->sem_gate, INFINITE);
  165. if (cond->gone != 0) {
  166. cond->blocked -= cond->gone;
  167. cond->gone = 0;
  168. }
  169. if (broadcast) {
  170. nsignal = cond->to_unblock = cond->blocked;
  171. cond->blocked = 0;
  172. } else {
  173. nsignal = cond->to_unblock = 1;
  174. cond->blocked--;
  175. }
  176. }
  177. LeaveCriticalSection(&cond->monitor);
  178. if (0 < nsignal)
  179. ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
  180. }
  181. static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
  182. {
  183. int nleft = 0;
  184. int ngone = 0;
  185. int timeout = 0;
  186. DWORD w;
  187. WaitForSingleObject(cond->sem_gate, INFINITE);
  188. cond->blocked++;
  189. ReleaseSemaphore(cond->sem_gate, 1, NULL);
  190. mtx_unlock(mtx);
  191. w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE);
  192. timeout = (w == WAIT_TIMEOUT);
  193. EnterCriticalSection(&cond->monitor);
  194. if ((nleft = cond->to_unblock) != 0) {
  195. if (timeout) {
  196. if (cond->blocked != 0) {
  197. cond->blocked--;
  198. } else {
  199. cond->gone++;
  200. }
  201. }
  202. if (--cond->to_unblock == 0) {
  203. if (cond->blocked != 0) {
  204. ReleaseSemaphore(cond->sem_gate, 1, NULL);
  205. nleft = 0;
  206. }
  207. else if ((ngone = cond->gone) != 0) {
  208. cond->gone = 0;
  209. }
  210. }
  211. } else if (++cond->gone == INT_MAX/2) {
  212. WaitForSingleObject(cond->sem_gate, INFINITE);
  213. cond->blocked -= cond->gone;
  214. ReleaseSemaphore(cond->sem_gate, 1, NULL);
  215. cond->gone = 0;
  216. }
  217. LeaveCriticalSection(&cond->monitor);
  218. if (nleft == 1) {
  219. while (ngone--)
  220. WaitForSingleObject(cond->sem_queue, INFINITE);
  221. ReleaseSemaphore(cond->sem_gate, 1, NULL);
  222. }
  223. mtx_lock(mtx);
  224. return timeout ? thrd_busy : thrd_success;
  225. }
  226. #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
  227. static struct impl_tss_dtor_entry {
  228. tss_t key;
  229. tss_dtor_t dtor;
  230. } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
  231. static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
  232. {
  233. int i;
  234. for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
  235. if (!impl_tss_dtor_tbl[i].dtor)
  236. break;
  237. }
  238. if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
  239. return 1;
  240. impl_tss_dtor_tbl[i].key = key;
  241. impl_tss_dtor_tbl[i].dtor = dtor;
  242. return 0;
  243. }
  244. static void impl_tss_dtor_invoke()
  245. {
  246. int i;
  247. for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
  248. if (impl_tss_dtor_tbl[i].dtor) {
  249. void* val = tss_get(impl_tss_dtor_tbl[i].key);
  250. if (val)
  251. (impl_tss_dtor_tbl[i].dtor)(val);
  252. }
  253. }
  254. }
  255. /*--------------- 7.25.2 Initialization functions ---------------*/
  256. // 7.25.2.1
  257. static inline void
  258. call_once(once_flag *flag, void (*func)(void))
  259. {
  260. assert(flag && func);
  261. #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
  262. {
  263. struct impl_call_once_param param;
  264. param.func = func;
  265. InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)&param, NULL);
  266. }
  267. #else
  268. if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
  269. (func)();
  270. InterlockedExchange(&flag->status, 2);
  271. } else {
  272. while (flag->status == 1) {
  273. // busy loop!
  274. thrd_yield();
  275. }
  276. }
  277. #endif
  278. }
  279. /*------------- 7.25.3 Condition variable functions -------------*/
  280. // 7.25.3.1
  281. static inline int
  282. cnd_broadcast(cnd_t *cond)
  283. {
  284. if (!cond) return thrd_error;
  285. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  286. WakeAllConditionVariable(&cond->condvar);
  287. #else
  288. impl_cond_do_signal(cond, 1);
  289. #endif
  290. return thrd_success;
  291. }
  292. // 7.25.3.2
  293. static inline void
  294. cnd_destroy(cnd_t *cond)
  295. {
  296. assert(cond);
  297. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  298. // do nothing
  299. #else
  300. CloseHandle(cond->sem_queue);
  301. CloseHandle(cond->sem_gate);
  302. DeleteCriticalSection(&cond->monitor);
  303. #endif
  304. }
  305. // 7.25.3.3
  306. static inline int
  307. cnd_init(cnd_t *cond)
  308. {
  309. if (!cond) return thrd_error;
  310. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  311. InitializeConditionVariable(&cond->condvar);
  312. #else
  313. cond->blocked = 0;
  314. cond->gone = 0;
  315. cond->to_unblock = 0;
  316. cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
  317. cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
  318. InitializeCriticalSection(&cond->monitor);
  319. #endif
  320. return thrd_success;
  321. }
  322. // 7.25.3.4
  323. static inline int
  324. cnd_signal(cnd_t *cond)
  325. {
  326. if (!cond) return thrd_error;
  327. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  328. WakeConditionVariable(&cond->condvar);
  329. #else
  330. impl_cond_do_signal(cond, 0);
  331. #endif
  332. return thrd_success;
  333. }
  334. // 7.25.3.5
  335. static inline int
  336. cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
  337. {
  338. if (!cond || !mtx || !xt) return thrd_error;
  339. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  340. if (SleepConditionVariableCS(&cond->condvar, mtx, impl_xtime2msec(xt)))
  341. return thrd_success;
  342. return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
  343. #else
  344. return impl_cond_do_wait(cond, mtx, xt);
  345. #endif
  346. }
  347. // 7.25.3.6
  348. static inline int
  349. cnd_wait(cnd_t *cond, mtx_t *mtx)
  350. {
  351. if (!cond || !mtx) return thrd_error;
  352. #ifdef EMULATED_THREADS_USE_NATIVE_CV
  353. SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
  354. #else
  355. impl_cond_do_wait(cond, mtx, NULL);
  356. #endif
  357. return thrd_success;
  358. }
  359. /*-------------------- 7.25.4 Mutex functions --------------------*/
  360. // 7.25.4.1
  361. static inline void
  362. mtx_destroy(mtx_t *mtx)
  363. {
  364. assert(mtx);
  365. DeleteCriticalSection(mtx);
  366. }
  367. // 7.25.4.2
  368. static inline int
  369. mtx_init(mtx_t *mtx, int type)
  370. {
  371. if (!mtx) return thrd_error;
  372. if (type != mtx_plain && type != mtx_timed && type != mtx_try
  373. && type != (mtx_plain|mtx_recursive)
  374. && type != (mtx_timed|mtx_recursive)
  375. && type != (mtx_try|mtx_recursive))
  376. return thrd_error;
  377. InitializeCriticalSection(mtx);
  378. return thrd_success;
  379. }
  380. // 7.25.4.3
  381. static inline int
  382. mtx_lock(mtx_t *mtx)
  383. {
  384. if (!mtx) return thrd_error;
  385. EnterCriticalSection(mtx);
  386. return thrd_success;
  387. }
  388. // 7.25.4.4
  389. static inline int
  390. mtx_timedlock(mtx_t *mtx, const xtime *xt)
  391. {
  392. time_t expire, now;
  393. if (!mtx || !xt) return thrd_error;
  394. expire = time(NULL);
  395. expire += xt->sec;
  396. while (mtx_trylock(mtx) != thrd_success) {
  397. now = time(NULL);
  398. if (expire < now)
  399. return thrd_busy;
  400. // busy loop!
  401. thrd_yield();
  402. }
  403. return thrd_success;
  404. }
  405. // 7.25.4.5
  406. static inline int
  407. mtx_trylock(mtx_t *mtx)
  408. {
  409. if (!mtx) return thrd_error;
  410. return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
  411. }
  412. // 7.25.4.6
  413. static inline int
  414. mtx_unlock(mtx_t *mtx)
  415. {
  416. if (!mtx) return thrd_error;
  417. LeaveCriticalSection(mtx);
  418. return thrd_success;
  419. }
  420. /*------------------- 7.25.5 Thread functions -------------------*/
  421. // 7.25.5.1
  422. static inline int
  423. thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
  424. {
  425. struct impl_thrd_param *pack;
  426. uintptr_t handle;
  427. if (!thr) return thrd_error;
  428. pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
  429. if (!pack) return thrd_nomem;
  430. pack->func = func;
  431. pack->arg = arg;
  432. handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
  433. if (handle == 0) {
  434. if (errno == EAGAIN || errno == EACCES)
  435. return thrd_nomem;
  436. return thrd_error;
  437. }
  438. *thr = (thrd_t)handle;
  439. return thrd_success;
  440. }
  441. #if 0
  442. // 7.25.5.2
  443. static inline thrd_t
  444. thrd_current(void)
  445. {
  446. HANDLE hCurrentThread;
  447. BOOL bRet;
  448. /* GetCurrentThread() returns a pseudo-handle, which is useless. We need
  449. * to call DuplicateHandle to get a real handle. However the handle value
  450. * will not match the one returned by thread_create.
  451. *
  452. * Other potential solutions would be:
  453. * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
  454. * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
  455. *
  456. * Neither is particularly nice.
  457. *
  458. * Life would be much easier if C11 threads had different abstractions for
  459. * threads and thread IDs, just like C++11 threads does...
  460. */
  461. bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
  462. GetCurrentThread(), // source (pseudo) handle
  463. GetCurrentProcess(), // target process
  464. &hCurrentThread, // target handle
  465. 0,
  466. FALSE,
  467. DUPLICATE_SAME_ACCESS);
  468. assert(bRet);
  469. if (!bRet) {
  470. hCurrentThread = GetCurrentThread();
  471. }
  472. return hCurrentThread;
  473. }
  474. #endif
  475. // 7.25.5.3
  476. static inline int
  477. thrd_detach(thrd_t thr)
  478. {
  479. CloseHandle(thr);
  480. return thrd_success;
  481. }
  482. // 7.25.5.4
  483. static inline int
  484. thrd_equal(thrd_t thr0, thrd_t thr1)
  485. {
  486. return GetThreadId(thr0) == GetThreadId(thr1);
  487. }
  488. // 7.25.5.5
  489. static inline void
  490. thrd_exit(int res)
  491. {
  492. impl_tss_dtor_invoke();
  493. _endthreadex((unsigned)res);
  494. }
  495. // 7.25.5.6
  496. static inline int
  497. thrd_join(thrd_t thr, int *res)
  498. {
  499. DWORD w, code;
  500. w = WaitForSingleObject(thr, INFINITE);
  501. if (w != WAIT_OBJECT_0)
  502. return thrd_error;
  503. if (res) {
  504. if (!GetExitCodeThread(thr, &code)) {
  505. CloseHandle(thr);
  506. return thrd_error;
  507. }
  508. *res = (int)code;
  509. }
  510. CloseHandle(thr);
  511. return thrd_success;
  512. }
  513. // 7.25.5.7
  514. static inline void
  515. thrd_sleep(const xtime *xt)
  516. {
  517. assert(xt);
  518. Sleep(impl_xtime2msec(xt));
  519. }
  520. // 7.25.5.8
  521. static inline void
  522. thrd_yield(void)
  523. {
  524. SwitchToThread();
  525. }
  526. /*----------- 7.25.6 Thread-specific storage functions -----------*/
  527. // 7.25.6.1
  528. static inline int
  529. tss_create(tss_t *key, tss_dtor_t dtor)
  530. {
  531. if (!key) return thrd_error;
  532. *key = TlsAlloc();
  533. if (dtor) {
  534. if (impl_tss_dtor_register(*key, dtor)) {
  535. TlsFree(*key);
  536. return thrd_error;
  537. }
  538. }
  539. return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
  540. }
  541. // 7.25.6.2
  542. static inline void
  543. tss_delete(tss_t key)
  544. {
  545. TlsFree(key);
  546. }
  547. // 7.25.6.3
  548. static inline void *
  549. tss_get(tss_t key)
  550. {
  551. return TlsGetValue(key);
  552. }
  553. // 7.25.6.4
  554. static inline int
  555. tss_set(tss_t key, void *val)
  556. {
  557. return TlsSetValue(key, val) ? thrd_success : thrd_error;
  558. }
  559. /*-------------------- 7.25.7 Time functions --------------------*/
  560. // 7.25.6.1
  561. static inline int
  562. xtime_get(xtime *xt, int base)
  563. {
  564. if (!xt) return 0;
  565. if (base == TIME_UTC) {
  566. xt->sec = time(NULL);
  567. xt->nsec = 0;
  568. return base;
  569. }
  570. return 0;
  571. }