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