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