threads_win32.h revision eaf9affa5ec9c5fd919e4207ab80b4677650ac67
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
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 ----------------------------*/
91typedef 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
104typedef HANDLE thrd_t;
105
106typedef DWORD tss_t;
107
108typedef CRITICAL_SECTION mtx_t;
109
110#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
111typedef INIT_ONCE once_flag;
112#else
113typedef struct once_flag_t {
114    volatile LONG status;
115} once_flag;
116#endif
117
118
119static inline void * tss_get(tss_t key);
120static inline void thrd_yield(void);
121static inline int mtx_trylock(mtx_t *mtx);
122static inline int mtx_lock(mtx_t *mtx);
123static inline int mtx_unlock(mtx_t *mtx);
124
125/*
126Implementation 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*/
131static void impl_tss_dtor_invoke(void);  // forward decl.
132
133struct impl_thrd_param {
134    thrd_start_t func;
135    void *arg;
136};
137
138static 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
149static 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
155struct impl_call_once_param { void (*func)(void); };
156static 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/*
167Note:
168  The implementation of condition variable is ported from Boost.Interprocess
169  See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
170*/
171static 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
209static 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
262static 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
267static 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
281static 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
296static inline void
297call_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
322static inline int
323cnd_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
335static inline void
336cnd_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
349static inline int
350cnd_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
367static inline int
368cnd_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
380static inline int
381cnd_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
394static inline int
395cnd_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
409static inline void
410mtx_destroy(mtx_t *mtx)
411{
412    assert(mtx);
413    DeleteCriticalSection(mtx);
414}
415
416// 7.25.4.2
417static inline int
418mtx_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
431static inline int
432mtx_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
440static inline int
441mtx_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
458static inline int
459mtx_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
466static inline int
467mtx_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
477static inline int
478thrd_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
499static inline thrd_t
500thrd_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
535static inline int
536thrd_detach(thrd_t thr)
537{
538    CloseHandle(thr);
539    return thrd_success;
540}
541
542// 7.25.5.4
543static inline int
544thrd_equal(thrd_t thr0, thrd_t thr1)
545{
546    return GetThreadId(thr0) == GetThreadId(thr1);
547}
548
549// 7.25.5.5
550static inline void
551thrd_exit(int res)
552{
553    impl_tss_dtor_invoke();
554    _endthreadex((unsigned)res);
555}
556
557// 7.25.5.6
558static inline int
559thrd_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
577static inline void
578thrd_sleep(const xtime *xt)
579{
580    assert(xt);
581    Sleep(impl_xtime2msec(xt));
582}
583
584// 7.25.5.8
585static inline void
586thrd_yield(void)
587{
588    SwitchToThread();
589}
590
591
592/*----------- 7.25.6 Thread-specific storage functions -----------*/
593// 7.25.6.1
594static inline int
595tss_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
609static inline void
610tss_delete(tss_t key)
611{
612    TlsFree(key);
613}
614
615// 7.25.6.3
616static inline void *
617tss_get(tss_t key)
618{
619    return TlsGetValue(key);
620}
621
622// 7.25.6.4
623static inline int
624tss_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
632static inline int
633xtime_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