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#include <assert.h>
30#include <limits.h>
31#include <errno.h>
32#include <process.h>  // MSVCRT
33#include <stdlib.h>
34
35#include "c11/threads.h"
36
37#ifndef WIN32_LEAN_AND_MEAN
38#define WIN32_LEAN_AND_MEAN 1
39#endif
40#include <windows.h>
41
42/*
43Configuration macro:
44
45  EMULATED_THREADS_USE_NATIVE_CALL_ONCE
46    Use native WindowsAPI one-time initialization function.
47    (requires WinVista or later)
48    Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
49
50  EMULATED_THREADS_TSS_DTOR_SLOTNUM
51    Max registerable TSS dtor number.
52*/
53
54#if _WIN32_WINNT >= 0x0600
55// Prefer native WindowsAPI on newer environment.
56#if !defined(__MINGW32__)
57#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
58#endif
59#endif
60#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64  // see TLS_MINIMUM_AVAILABLE
61
62// check configuration
63#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)
64#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
65#endif
66
67
68static_assert(sizeof(cnd_t) == sizeof(CONDITION_VARIABLE), "The size of cnd_t must equal to CONDITION_VARIABLE");
69static_assert(sizeof(thrd_t) == sizeof(HANDLE), "The size of thrd_t must equal to HANDLE");
70static_assert(sizeof(tss_t) == sizeof(DWORD), "The size of tss_t must equal to DWORD");
71static_assert(sizeof(mtx_t) == sizeof(CRITICAL_SECTION), "The size of mtx_t must equal to CRITICAL_SECTION");
72static_assert(sizeof(once_flag) == sizeof(INIT_ONCE), "The size of once_flag must equal to INIT_ONCE");
73
74/*
75Implementation limits:
76  - Conditionally emulation for "Initialization functions"
77    (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
78  - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
79*/
80static void impl_tss_dtor_invoke(void);  // forward decl.
81
82struct impl_thrd_param {
83    thrd_start_t func;
84    void *arg;
85};
86
87static unsigned __stdcall impl_thrd_routine(void *p)
88{
89    struct impl_thrd_param pack;
90    int code;
91    memcpy(&pack, p, sizeof(struct impl_thrd_param));
92    free(p);
93    code = pack.func(pack.arg);
94    impl_tss_dtor_invoke();
95    return (unsigned)code;
96}
97
98static time_t impl_timespec2msec(const struct timespec *ts)
99{
100    return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);
101}
102
103static DWORD impl_abs2relmsec(const struct timespec *abs_time)
104{
105    const time_t abs_ms = impl_timespec2msec(abs_time);
106    struct timespec now;
107    timespec_get(&now, TIME_UTC);
108    const time_t now_ms = impl_timespec2msec(&now);
109    const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD)(abs_ms - now_ms) : 0;
110    return rel_ms;
111}
112
113#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
114struct impl_call_once_param { void (*func)(void); };
115static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
116{
117    struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
118    (param->func)();
119    ((void)InitOnce); ((void)Context);  // suppress warning
120    return TRUE;
121}
122#endif  // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
123
124static struct impl_tss_dtor_entry {
125    tss_t key;
126    tss_dtor_t dtor;
127} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
128
129static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
130{
131    int i;
132    for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
133        if (!impl_tss_dtor_tbl[i].dtor)
134            break;
135    }
136    if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
137        return 1;
138    impl_tss_dtor_tbl[i].key = key;
139    impl_tss_dtor_tbl[i].dtor = dtor;
140    return 0;
141}
142
143static void impl_tss_dtor_invoke(void)
144{
145    int i;
146    for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
147        if (impl_tss_dtor_tbl[i].dtor) {
148            void* val = tss_get(impl_tss_dtor_tbl[i].key);
149            if (val)
150                (impl_tss_dtor_tbl[i].dtor)(val);
151        }
152    }
153}
154
155
156/*--------------- 7.25.2 Initialization functions ---------------*/
157// 7.25.2.1
158void
159call_once(once_flag *flag, void (*func)(void))
160{
161    assert(flag && func);
162#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
163    {
164    struct impl_call_once_param param;
165    param.func = func;
166    InitOnceExecuteOnce((PINIT_ONCE)flag, impl_call_once_callback, (PVOID)&param, NULL);
167    }
168#else
169    if (InterlockedCompareExchangePointer((PVOID volatile *)&flag->status, (PVOID)1, (PVOID)0) == 0) {
170        (func)();
171        InterlockedExchangePointer((PVOID volatile *)&flag->status, (PVOID)2);
172    } else {
173        while (flag->status == 1) {
174            // busy loop!
175            thrd_yield();
176        }
177    }
178#endif
179}
180
181
182/*------------- 7.25.3 Condition variable functions -------------*/
183// 7.25.3.1
184int
185cnd_broadcast(cnd_t *cond)
186{
187    assert(cond != NULL);
188    WakeAllConditionVariable((PCONDITION_VARIABLE)cond);
189    return thrd_success;
190}
191
192// 7.25.3.2
193void
194cnd_destroy(cnd_t *cond)
195{
196    (void)cond;
197    assert(cond != NULL);
198    // do nothing
199}
200
201// 7.25.3.3
202int
203cnd_init(cnd_t *cond)
204{
205    assert(cond != NULL);
206    InitializeConditionVariable((PCONDITION_VARIABLE)cond);
207    return thrd_success;
208}
209
210// 7.25.3.4
211int
212cnd_signal(cnd_t *cond)
213{
214    assert(cond != NULL);
215    WakeConditionVariable((PCONDITION_VARIABLE)cond);
216    return thrd_success;
217}
218
219// 7.25.3.5
220int
221cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
222{
223    assert(cond != NULL);
224    assert(mtx != NULL);
225    assert(abs_time != NULL);
226    const DWORD timeout = impl_abs2relmsec(abs_time);
227    if (SleepConditionVariableCS((PCONDITION_VARIABLE)cond, (PCRITICAL_SECTION)mtx, timeout))
228        return thrd_success;
229    return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout : thrd_error;
230}
231
232// 7.25.3.6
233int
234cnd_wait(cnd_t *cond, mtx_t *mtx)
235{
236    assert(cond != NULL);
237    assert(mtx != NULL);
238    SleepConditionVariableCS((PCONDITION_VARIABLE)cond, (PCRITICAL_SECTION)mtx, INFINITE);
239    return thrd_success;
240}
241
242
243/*-------------------- 7.25.4 Mutex functions --------------------*/
244// 7.25.4.1
245void
246mtx_destroy(mtx_t *mtx)
247{
248    assert(mtx);
249    DeleteCriticalSection((PCRITICAL_SECTION)mtx);
250}
251
252// 7.25.4.2
253int
254mtx_init(mtx_t *mtx, int type)
255{
256    assert(mtx != NULL);
257    if (type != mtx_plain && type != mtx_timed && type != mtx_try
258      && type != (mtx_plain|mtx_recursive)
259      && type != (mtx_timed|mtx_recursive)
260      && type != (mtx_try|mtx_recursive))
261        return thrd_error;
262    InitializeCriticalSection((PCRITICAL_SECTION)mtx);
263    return thrd_success;
264}
265
266// 7.25.4.3
267int
268mtx_lock(mtx_t *mtx)
269{
270    assert(mtx != NULL);
271    EnterCriticalSection((PCRITICAL_SECTION)mtx);
272    return thrd_success;
273}
274
275// 7.25.4.4
276int
277mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
278{
279    assert(mtx != NULL);
280    assert(ts != NULL);
281    while (mtx_trylock(mtx) != thrd_success) {
282        if (impl_abs2relmsec(ts) == 0)
283            return thrd_timedout;
284        // busy loop!
285        thrd_yield();
286    }
287    return thrd_success;
288}
289
290// 7.25.4.5
291int
292mtx_trylock(mtx_t *mtx)
293{
294    assert(mtx != NULL);
295    return TryEnterCriticalSection((PCRITICAL_SECTION)mtx) ? thrd_success : thrd_busy;
296}
297
298// 7.25.4.6
299int
300mtx_unlock(mtx_t *mtx)
301{
302    assert(mtx != NULL);
303    LeaveCriticalSection((PCRITICAL_SECTION)mtx);
304    return thrd_success;
305}
306
307
308/*------------------- 7.25.5 Thread functions -------------------*/
309// 7.25.5.1
310int
311thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
312{
313    struct impl_thrd_param *pack;
314    uintptr_t handle;
315    assert(thr != NULL);
316    pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
317    if (!pack) return thrd_nomem;
318    pack->func = func;
319    pack->arg = arg;
320    handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
321    if (handle == 0) {
322        free(pack);
323        if (errno == EAGAIN || errno == EACCES)
324            return thrd_nomem;
325        return thrd_error;
326    }
327    *thr = (thrd_t)handle;
328    return thrd_success;
329}
330
331#if 0
332// 7.25.5.2
333static inline thrd_t
334thrd_current(void)
335{
336    HANDLE hCurrentThread;
337    BOOL bRet;
338
339    /* GetCurrentThread() returns a pseudo-handle, which we need
340     * to pass to DuplicateHandle(). Only the resulting handle can be used
341     * from other threads.
342     *
343     * Note that neither handle can be compared to the one by thread_create.
344     * Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
345     * can be compared directly.
346     *
347     * Other potential solutions would be:
348     * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
349     * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
350     *
351     * Neither is particularly nice.
352     *
353     * Life would be much easier if C11 threads had different abstractions for
354     * threads and thread IDs, just like C++11 threads does...
355     */
356
357    bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
358                           GetCurrentThread(), // source (pseudo) handle
359                           GetCurrentProcess(), // target process
360                           &hCurrentThread, // target handle
361                           0,
362                           FALSE,
363                           DUPLICATE_SAME_ACCESS);
364    assert(bRet);
365    if (!bRet) {
366	hCurrentThread = GetCurrentThread();
367    }
368    return hCurrentThread;
369}
370#endif
371
372// 7.25.5.3
373int
374thrd_detach(thrd_t thr)
375{
376    CloseHandle(thr);
377    return thrd_success;
378}
379
380// 7.25.5.4
381int
382thrd_equal(thrd_t thr0, thrd_t thr1)
383{
384    return GetThreadId(thr0) == GetThreadId(thr1);
385}
386
387// 7.25.5.5
388_Noreturn
389void
390thrd_exit(int res)
391{
392    impl_tss_dtor_invoke();
393    _endthreadex((unsigned)res);
394}
395
396// 7.25.5.6
397int
398thrd_join(thrd_t thr, int *res)
399{
400    DWORD w, code;
401    w = WaitForSingleObject(thr, INFINITE);
402    if (w != WAIT_OBJECT_0)
403        return thrd_error;
404    if (res) {
405        if (!GetExitCodeThread(thr, &code)) {
406            CloseHandle(thr);
407            return thrd_error;
408        }
409        *res = (int)code;
410    }
411    CloseHandle(thr);
412    return thrd_success;
413}
414
415// 7.25.5.7
416int
417thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
418{
419    (void)remaining;
420    assert(time_point);
421    assert(!remaining); /* not implemented */
422    Sleep((DWORD)impl_timespec2msec(time_point));
423    return 0;
424}
425
426// 7.25.5.8
427void
428thrd_yield(void)
429{
430    SwitchToThread();
431}
432
433
434/*----------- 7.25.6 Thread-specific storage functions -----------*/
435// 7.25.6.1
436int
437tss_create(tss_t *key, tss_dtor_t dtor)
438{
439    assert(key != NULL);
440    *key = TlsAlloc();
441    if (dtor) {
442        if (impl_tss_dtor_register(*key, dtor)) {
443            TlsFree(*key);
444            return thrd_error;
445        }
446    }
447    return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
448}
449
450// 7.25.6.2
451void
452tss_delete(tss_t key)
453{
454    TlsFree(key);
455}
456
457// 7.25.6.3
458void *
459tss_get(tss_t key)
460{
461    return TlsGetValue(key);
462}
463
464// 7.25.6.4
465int
466tss_set(tss_t key, void *val)
467{
468    return TlsSetValue(key, val) ? thrd_success : thrd_error;
469}
470