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