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 
36 /*
37 Configuration macro:
38 
39   EMULATED_THREADS_USE_NATIVE_CALL_ONCE
40     Use native WindowsAPI one-time initialization function.
41     (requires WinVista or later)
42     Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
43 
44   EMULATED_THREADS_USE_NATIVE_CV
45     Use native WindowsAPI condition variable object.
46     (requires WinVista or later)
47     Otherwise use emulated implementation for WinXP.
48 
49   EMULATED_THREADS_TSS_DTOR_SLOTNUM
50     Max registerable TSS dtor number.
51 */
52 
53 // XXX: Retain XP compatability
54 #if 0
55 #if _WIN32_WINNT >= 0x0600
56 // Prefer native WindowsAPI on newer environment.
57 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
58 #define EMULATED_THREADS_USE_NATIVE_CV
59 #endif
60 #endif
61 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64  // see TLS_MINIMUM_AVAILABLE
62 
63 #include "threads.h"
64 
65 
66 /*
67 Implementation limits:
68   - Conditionally emulation for "Initialization functions"
69     (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
70   - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
71 */
72 static void impl_tss_dtor_invoke(void);  // forward decl.
73 
74 struct impl_thrd_param {
75     thrd_start_t func;
76     void *arg;
77 };
78 
impl_thrd_routine(void * p)79 static unsigned __stdcall impl_thrd_routine(void *p)
80 {
81     struct impl_thrd_param pack;
82     int code;
83     memcpy(&pack, p, sizeof(struct impl_thrd_param));
84     free(p);
85     code = pack.func(pack.arg);
86     impl_tss_dtor_invoke();
87     return (unsigned)code;
88 }
89 
impl_xtime2msec(const xtime * xt)90 static DWORD impl_xtime2msec(const xtime *xt)
91 {
92     return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L));
93 }
94 
95 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
96 struct impl_call_once_param { void (*func)(void); };
impl_call_once_callback(PINIT_ONCE InitOnce,PVOID Parameter,PVOID * Context)97 static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
98 {
99     struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
100     (param->func)();
101     ((void)InitOnce); ((void)Context);  // suppress warning
102     return TRUE;
103 }
104 #endif  // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
105 
106 #ifndef EMULATED_THREADS_USE_NATIVE_CV
107 /*
108 Note:
109   The implementation of condition variable is ported from Boost.Interprocess
110   See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
111 */
impl_cond_do_signal(cnd_t * cond,int broadcast)112 static void impl_cond_do_signal(cnd_t *cond, int broadcast)
113 {
114     int nsignal = 0;
115 
116     EnterCriticalSection(&cond->monitor);
117     if (cond->to_unblock != 0) {
118         if (cond->blocked == 0) {
119             LeaveCriticalSection(&cond->monitor);
120             return;
121         }
122         if (broadcast) {
123             cond->to_unblock += nsignal = cond->blocked;
124             cond->blocked = 0;
125         } else {
126             nsignal = 1;
127             cond->to_unblock++;
128             cond->blocked--;
129         }
130     } else if (cond->blocked > cond->gone) {
131         WaitForSingleObject(cond->sem_gate, INFINITE);
132         if (cond->gone != 0) {
133             cond->blocked -= cond->gone;
134             cond->gone = 0;
135         }
136         if (broadcast) {
137             nsignal = cond->to_unblock = cond->blocked;
138             cond->blocked = 0;
139         } else {
140             nsignal = cond->to_unblock = 1;
141             cond->blocked--;
142         }
143     }
144     LeaveCriticalSection(&cond->monitor);
145 
146     if (0 < nsignal)
147         ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
148 }
149 
impl_cond_do_wait(cnd_t * cond,mtx_t * mtx,const xtime * xt)150 static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
151 {
152     int nleft = 0;
153     int ngone = 0;
154     int timeout = 0;
155     DWORD w;
156 
157     WaitForSingleObject(cond->sem_gate, INFINITE);
158     cond->blocked++;
159     ReleaseSemaphore(cond->sem_gate, 1, NULL);
160 
161     mtx_unlock(mtx);
162 
163     w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE);
164     timeout = (w == WAIT_TIMEOUT);
165 
166     EnterCriticalSection(&cond->monitor);
167     if ((nleft = cond->to_unblock) != 0) {
168         if (timeout) {
169             if (cond->blocked != 0) {
170                 cond->blocked--;
171             } else {
172                 cond->gone++;
173             }
174         }
175         if (--cond->to_unblock == 0) {
176             if (cond->blocked != 0) {
177                 ReleaseSemaphore(cond->sem_gate, 1, NULL);
178                 nleft = 0;
179             }
180             else if ((ngone = cond->gone) != 0) {
181                 cond->gone = 0;
182             }
183         }
184     } else if (++cond->gone == INT_MAX/2) {
185         WaitForSingleObject(cond->sem_gate, INFINITE);
186         cond->blocked -= cond->gone;
187         ReleaseSemaphore(cond->sem_gate, 1, NULL);
188         cond->gone = 0;
189     }
190     LeaveCriticalSection(&cond->monitor);
191 
192     if (nleft == 1) {
193         while (ngone--)
194             WaitForSingleObject(cond->sem_queue, INFINITE);
195         ReleaseSemaphore(cond->sem_gate, 1, NULL);
196     }
197 
198     mtx_lock(mtx);
199     return timeout ? thrd_busy : thrd_success;
200 }
201 #endif  // ifndef EMULATED_THREADS_USE_NATIVE_CV
202 
203 static struct impl_tss_dtor_entry {
204     tss_t key;
205     tss_dtor_t dtor;
206 } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
207 
impl_tss_dtor_register(tss_t key,tss_dtor_t dtor)208 static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
209 {
210     int i;
211     for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
212         if (!impl_tss_dtor_tbl[i].dtor)
213             break;
214     }
215     if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
216         return 1;
217     impl_tss_dtor_tbl[i].key = key;
218     impl_tss_dtor_tbl[i].dtor = dtor;
219     return 0;
220 }
221 
impl_tss_dtor_invoke()222 static void impl_tss_dtor_invoke()
223 {
224     int i;
225     for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
226         if (impl_tss_dtor_tbl[i].dtor) {
227             void* val = tss_get(impl_tss_dtor_tbl[i].key);
228             if (val)
229                 (impl_tss_dtor_tbl[i].dtor)(val);
230         }
231     }
232 }
233 
234 
235 /*--------------- 7.25.2 Initialization functions ---------------*/
236 // 7.25.2.1
call_once(once_flag * flag,void (* func)(void))237 void call_once(once_flag *flag, void (*func)(void))
238 {
239     assert(flag && func);
240 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
241     {
242     struct impl_call_once_param param;
243     param.func = func;
244     InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)&param, NULL);
245     }
246 #else
247     if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
248         (func)();
249         InterlockedExchange(&flag->status, 2);
250     } else {
251         while (flag->status == 1) {
252             // busy loop!
253             thrd_yield();
254         }
255     }
256 #endif
257 }
258 
259 
260 /*------------- 7.25.3 Condition variable functions -------------*/
261 // 7.25.3.1
cnd_broadcast(cnd_t * cond)262 int cnd_broadcast(cnd_t *cond)
263 {
264     if (!cond) return thrd_error;
265 #ifdef EMULATED_THREADS_USE_NATIVE_CV
266     WakeAllConditionVariable(&cond->condvar);
267 #else
268     impl_cond_do_signal(cond, 1);
269 #endif
270     return thrd_success;
271 }
272 
273 // 7.25.3.2
cnd_destroy(cnd_t * cond)274 void cnd_destroy(cnd_t *cond)
275 {
276     assert(cond);
277 #ifdef EMULATED_THREADS_USE_NATIVE_CV
278     // do nothing
279 #else
280     CloseHandle(cond->sem_queue);
281     CloseHandle(cond->sem_gate);
282     DeleteCriticalSection(&cond->monitor);
283 #endif
284 }
285 
286 // 7.25.3.3
cnd_init(cnd_t * cond)287 int cnd_init(cnd_t *cond)
288 {
289     if (!cond) return thrd_error;
290 #ifdef EMULATED_THREADS_USE_NATIVE_CV
291     InitializeConditionVariable(&cond->condvar);
292 #else
293     cond->blocked = 0;
294     cond->gone = 0;
295     cond->to_unblock = 0;
296     cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
297     cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
298     InitializeCriticalSection(&cond->monitor);
299 #endif
300     return thrd_success;
301 }
302 
303 // 7.25.3.4
cnd_signal(cnd_t * cond)304 int cnd_signal(cnd_t *cond)
305 {
306     if (!cond) return thrd_error;
307 #ifdef EMULATED_THREADS_USE_NATIVE_CV
308     WakeConditionVariable(&cond->condvar);
309 #else
310     impl_cond_do_signal(cond, 0);
311 #endif
312     return thrd_success;
313 }
314 
315 // 7.25.3.5
cnd_timedwait(cnd_t * cond,mtx_t * mtx,const xtime * xt)316 int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
317 {
318     if (!cond || !mtx || !xt) return thrd_error;
319 #ifdef EMULATED_THREADS_USE_NATIVE_CV
320     if (SleepConditionVariableCS(&cond->condvar, &mtx->cs, impl_xtime2msec(xt)))
321         return thrd_success;
322     return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
323 #else
324     return impl_cond_do_wait(cond, mtx, xt);
325 #endif
326 }
327 
328 // 7.25.3.6
cnd_wait(cnd_t * cond,mtx_t * mtx)329 int cnd_wait(cnd_t *cond, mtx_t *mtx)
330 {
331     if (!cond || !mtx) return thrd_error;
332 #ifdef EMULATED_THREADS_USE_NATIVE_CV
333     SleepConditionVariableCS(&cond->condvar, &mtx->cs, INFINITE);
334 #else
335     impl_cond_do_wait(cond, mtx, NULL);
336 #endif
337     return thrd_success;
338 }
339 
340 
341 /*-------------------- 7.25.4 Mutex functions --------------------*/
342 // 7.25.4.1
mtx_destroy(mtx_t * mtx)343 void mtx_destroy(mtx_t *mtx)
344 {
345     assert(mtx);
346     DeleteCriticalSection(&mtx->cs);
347 }
348 
349 // 7.25.4.2
mtx_init(mtx_t * mtx,int type)350 int mtx_init(mtx_t *mtx, int type)
351 {
352     if (!mtx) return thrd_error;
353     if (type != mtx_plain && type != mtx_timed && type != mtx_try
354       && type != (mtx_plain|mtx_recursive)
355       && type != (mtx_timed|mtx_recursive)
356       && type != (mtx_try|mtx_recursive))
357         return thrd_error;
358     InitializeCriticalSection(&mtx->cs);
359     return thrd_success;
360 }
361 
362 // 7.25.4.3
mtx_lock(mtx_t * mtx)363 int mtx_lock(mtx_t *mtx)
364 {
365     if (!mtx) return thrd_error;
366     EnterCriticalSection(&mtx->cs);
367     return thrd_success;
368 }
369 
370 // 7.25.4.4
mtx_timedlock(mtx_t * mtx,const xtime * xt)371 int mtx_timedlock(mtx_t *mtx, const xtime *xt)
372 {
373     time_t expire, now;
374     if (!mtx || !xt) return thrd_error;
375     expire = time(NULL);
376     expire += xt->sec;
377     while (mtx_trylock(mtx) != thrd_success) {
378         now = time(NULL);
379         if (expire < now)
380             return thrd_busy;
381         // busy loop!
382         thrd_yield();
383     }
384     return thrd_success;
385 }
386 
387 // 7.25.4.5
mtx_trylock(mtx_t * mtx)388 int mtx_trylock(mtx_t *mtx)
389 {
390     if (!mtx) return thrd_error;
391     return TryEnterCriticalSection(&mtx->cs) ? thrd_success : thrd_busy;
392 }
393 
394 // 7.25.4.6
mtx_unlock(mtx_t * mtx)395 int mtx_unlock(mtx_t *mtx)
396 {
397     if (!mtx) return thrd_error;
398     LeaveCriticalSection(&mtx->cs);
399     return thrd_success;
400 }
401 
402 
403 /*------------------- 7.25.5 Thread functions -------------------*/
404 // 7.25.5.1
thrd_create(thrd_t * thr,thrd_start_t func,void * arg)405 int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
406 {
407     struct impl_thrd_param *pack;
408     uintptr_t handle;
409     if (!thr) return thrd_error;
410     pack = malloc(sizeof(struct impl_thrd_param));
411     if (!pack) return thrd_nomem;
412     pack->func = func;
413     pack->arg = arg;
414     handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
415     if (handle == 0) {
416         if (errno == EAGAIN || errno == EACCES)
417             return thrd_nomem;
418         return thrd_error;
419     }
420     *thr = (thrd_t)handle;
421     return thrd_success;
422 }
423 
424 // 7.25.5.2
thrd_current(void)425 thrd_t thrd_current(void)
426 {
427     return GetCurrentThread();
428 }
429 
430 // 7.25.5.3
thrd_detach(thrd_t thr)431 int thrd_detach(thrd_t thr)
432 {
433     CloseHandle(thr);
434     return thrd_success;
435 }
436 
437 // 7.25.5.4
thrd_equal(thrd_t thr0,thrd_t thr1)438 int thrd_equal(thrd_t thr0, thrd_t thr1)
439 {
440     return (thr0 == thr1);
441 }
442 
443 // 7.25.5.5
thrd_exit(int res)444 void thrd_exit(int res)
445 {
446     impl_tss_dtor_invoke();
447     _endthreadex((unsigned)res);
448 }
449 
450 // 7.25.5.6
thrd_join(thrd_t thr,int * res)451 int thrd_join(thrd_t thr, int *res)
452 {
453     DWORD w, code;
454     w = WaitForSingleObject(thr, INFINITE);
455     if (w != WAIT_OBJECT_0)
456         return thrd_error;
457     if (res) {
458         if (!GetExitCodeThread(thr, &code)) {
459             CloseHandle(thr);
460             return thrd_error;
461         }
462         *res = (int)code;
463     }
464     CloseHandle(thr);
465     return thrd_success;
466 }
467 
468 // 7.25.5.7
thrd_sleep(const xtime * xt)469 void thrd_sleep(const xtime *xt)
470 {
471     assert(xt);
472     Sleep(impl_xtime2msec(xt));
473 }
474 
475 // 7.25.5.8
thrd_yield(void)476 void thrd_yield(void)
477 {
478     SwitchToThread();
479 }
480 
481 
482 /*----------- 7.25.6 Thread-specific storage functions -----------*/
483 // 7.25.6.1
tss_create(tss_t * key,tss_dtor_t dtor)484 int tss_create(tss_t *key, tss_dtor_t dtor)
485 {
486     if (!key) return thrd_error;
487     *key = TlsAlloc();
488     if (dtor) {
489         if (impl_tss_dtor_register(*key, dtor)) {
490             TlsFree(*key);
491             return thrd_error;
492         }
493     }
494     return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
495 }
496 
497 // 7.25.6.2
tss_delete(tss_t key)498 void tss_delete(tss_t key)
499 {
500     TlsFree(key);
501 }
502 
503 // 7.25.6.3
tss_get(tss_t key)504 void *tss_get(tss_t key)
505 {
506     return TlsGetValue(key);
507 }
508 
509 // 7.25.6.4
tss_set(tss_t key,void * val)510 int tss_set(tss_t key, void *val)
511 {
512     return TlsSetValue(key, val) ? thrd_success : thrd_error;
513 }
514 
515 
516 /*-------------------- 7.25.7 Time functions --------------------*/
517 // 7.25.6.1
xtime_get(xtime * xt,int base)518 int xtime_get(xtime *xt, int base)
519 {
520     if (!xt) return 0;
521     if (base == TIME_UTC) {
522         xt->sec = time(NULL);
523         xt->nsec = 0;
524         return base;
525     }
526     return 0;
527 }
528