• 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 
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)&param, 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