threads_win32.h revision 01e04c3f
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/*
38Configuration 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 ----------------------------*/
95typedef 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
108typedef HANDLE thrd_t;
109
110typedef DWORD tss_t;
111
112typedef CRITICAL_SECTION mtx_t;
113
114#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
115typedef INIT_ONCE once_flag;
116#else
117typedef struct once_flag_t {
118    volatile LONG status;
119} once_flag;
120#endif
121
122
123static inline void * tss_get(tss_t key);
124static inline void thrd_yield(void);
125static inline int mtx_trylock(mtx_t *mtx);
126static inline int mtx_lock(mtx_t *mtx);
127static inline int mtx_unlock(mtx_t *mtx);
128
129/*
130Implementation 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*/
135static void impl_tss_dtor_invoke(void);  // forward decl.
136
137struct impl_thrd_param {
138    thrd_start_t func;
139    void *arg;
140};
141
142static 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
153static 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
159struct impl_call_once_param { void (*func)(void); };
160static 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/*
171Note:
172  The implementation of condition variable is ported from Boost.Interprocess
173  See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
174*/
175static 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
213static 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
266static 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
271static 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
285static 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
300static inline void
301call_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
326static inline int
327cnd_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
339static inline void
340cnd_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
353static inline int
354cnd_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
371static inline int
372cnd_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
384static inline int
385cnd_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
398static inline int
399cnd_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
413static inline void
414mtx_destroy(mtx_t *mtx)
415{
416    assert(mtx);
417    DeleteCriticalSection(mtx);
418}
419
420// 7.25.4.2
421static inline int
422mtx_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
435static inline int
436mtx_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
444static inline int
445mtx_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
462static inline int
463mtx_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
470static inline int
471mtx_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
481static inline int
482thrd_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
503static inline thrd_t
504thrd_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
543static inline int
544thrd_detach(thrd_t thr)
545{
546    CloseHandle(thr);
547    return thrd_success;
548}
549
550// 7.25.5.4
551static inline int
552thrd_equal(thrd_t thr0, thrd_t thr1)
553{
554    return GetThreadId(thr0) == GetThreadId(thr1);
555}
556
557// 7.25.5.5
558static inline void
559thrd_exit(int res)
560{
561    impl_tss_dtor_invoke();
562    _endthreadex((unsigned)res);
563}
564
565// 7.25.5.6
566static inline int
567thrd_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
585static inline void
586thrd_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
594static inline void
595thrd_yield(void)
596{
597    SwitchToThread();
598}
599
600
601/*----------- 7.25.6 Thread-specific storage functions -----------*/
602// 7.25.6.1
603static inline int
604tss_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
618static inline void
619tss_delete(tss_t key)
620{
621    TlsFree(key);
622}
623
624// 7.25.6.3
625static inline void *
626tss_get(tss_t key)
627{
628    return TlsGetValue(key);
629}
630
631// 7.25.6.4
632static inline int
633tss_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
642static inline int
643timespec_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