2
* C11 <threads.h> emulation library
4
* (C) Copyright yohhoy 2012.
5
* Distributed under the Boost Software License, Version 1.0.
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:
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.
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.
34
#include <process.h> // MSVCRT
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.
45
EMULATED_THREADS_TSS_DTOR_SLOTNUM
46
Max registerable TSS dtor number.
49
#if _WIN32_WINNT >= 0x0600
50
// Prefer native WindowsAPI on newer environment.
51
#if !defined(__MINGW32__)
52
#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
55
#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
57
#ifndef WIN32_LEAN_AND_MEAN
58
#define WIN32_LEAN_AND_MEAN 1
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
67
/* Visual Studio 2015 and later */
69
#define HAVE_TIMESPEC_GET
72
/*---------------------------- macros ----------------------------*/
73
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
74
#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
76
#define ONCE_FLAG_INIT {0}
78
#define TSS_DTOR_ITERATIONS 1
80
// FIXME: temporary non-standard hack to ease transition
81
#define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
83
/*---------------------------- types ----------------------------*/
84
typedef CONDITION_VARIABLE cnd_t;
86
typedef HANDLE thrd_t;
90
typedef CRITICAL_SECTION mtx_t;
92
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
93
typedef INIT_ONCE once_flag;
95
typedef struct once_flag_t {
101
static inline void * tss_get(tss_t key);
102
static inline void thrd_yield(void);
103
static inline int mtx_trylock(mtx_t *mtx);
104
static inline int mtx_lock(mtx_t *mtx);
105
static inline int mtx_unlock(mtx_t *mtx);
108
Implementation limits:
109
- Conditionally emulation for "Initialization functions"
110
(see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
111
- Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
113
static void impl_tss_dtor_invoke(void); // forward decl.
115
struct impl_thrd_param {
120
static unsigned __stdcall impl_thrd_routine(void *p)
122
struct impl_thrd_param pack;
124
memcpy(&pack, p, sizeof(struct impl_thrd_param));
126
code = pack.func(pack.arg);
127
impl_tss_dtor_invoke();
128
return (unsigned)code;
131
static time_t impl_timespec2msec(const struct timespec *ts)
133
return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);
136
#ifdef HAVE_TIMESPEC_GET
137
static DWORD impl_abs2relmsec(const struct timespec *abs_time)
139
const time_t abs_ms = impl_timespec2msec(abs_time);
141
timespec_get(&now, TIME_UTC);
142
const time_t now_ms = impl_timespec2msec(&now);
143
const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD)(abs_ms - now_ms) : 0;
148
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
149
struct impl_call_once_param { void (*func)(void); };
150
static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
152
struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
154
((void)InitOnce); ((void)Context); // suppress warning
157
#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
159
static struct impl_tss_dtor_entry {
162
} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
164
static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
167
for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
168
if (!impl_tss_dtor_tbl[i].dtor)
171
if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
173
impl_tss_dtor_tbl[i].key = key;
174
impl_tss_dtor_tbl[i].dtor = dtor;
178
static void impl_tss_dtor_invoke()
181
for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
182
if (impl_tss_dtor_tbl[i].dtor) {
183
void* val = tss_get(impl_tss_dtor_tbl[i].key);
185
(impl_tss_dtor_tbl[i].dtor)(val);
191
/*--------------- 7.25.2 Initialization functions ---------------*/
194
call_once(once_flag *flag, void (*func)(void))
196
assert(flag && func);
197
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
199
struct impl_call_once_param param;
201
InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL);
204
if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
206
InterlockedExchange(&flag->status, 2);
208
while (flag->status == 1) {
217
/*------------- 7.25.3 Condition variable functions -------------*/
220
cnd_broadcast(cnd_t *cond)
222
assert(cond != NULL);
223
WakeAllConditionVariable(cond);
229
cnd_destroy(cnd_t *cond)
232
assert(cond != NULL);
238
cnd_init(cnd_t *cond)
240
assert(cond != NULL);
241
InitializeConditionVariable(cond);
247
cnd_signal(cnd_t *cond)
249
assert(cond != NULL);
250
WakeConditionVariable(cond);
256
cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
258
assert(cond != NULL);
260
assert(abs_time != NULL);
261
#ifdef HAVE_TIMESPEC_GET
262
const DWORD timeout = impl_abs2relmsec(abs_time);
263
if (SleepConditionVariableCS(cond, mtx, timeout))
265
return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout : thrd_error;
273
cnd_wait(cnd_t *cond, mtx_t *mtx)
275
assert(cond != NULL);
277
SleepConditionVariableCS(cond, mtx, INFINITE);
282
/*-------------------- 7.25.4 Mutex functions --------------------*/
285
mtx_destroy(mtx_t *mtx)
288
DeleteCriticalSection(mtx);
293
mtx_init(mtx_t *mtx, int type)
296
if (type != mtx_plain && type != mtx_timed && type != mtx_try
297
&& type != (mtx_plain|mtx_recursive)
298
&& type != (mtx_timed|mtx_recursive)
299
&& type != (mtx_try|mtx_recursive))
301
InitializeCriticalSection(mtx);
310
EnterCriticalSection(mtx);
316
mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
320
#ifdef HAVE_TIMESPEC_GET
321
while (mtx_trylock(mtx) != thrd_success) {
322
if (impl_abs2relmsec(ts) == 0)
323
return thrd_timedout;
335
mtx_trylock(mtx_t *mtx)
338
return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
343
mtx_unlock(mtx_t *mtx)
346
LeaveCriticalSection(mtx);
351
/*------------------- 7.25.5 Thread functions -------------------*/
354
thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
356
struct impl_thrd_param *pack;
359
pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
360
if (!pack) return thrd_nomem;
363
handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
365
if (errno == EAGAIN || errno == EACCES)
369
*thr = (thrd_t)handle;
378
HANDLE hCurrentThread;
381
/* GetCurrentThread() returns a pseudo-handle, which we need
382
* to pass to DuplicateHandle(). Only the resulting handle can be used
383
* from other threads.
385
* Note that neither handle can be compared to the one by thread_create.
386
* Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
387
* can be compared directly.
389
* Other potential solutions would be:
390
* - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
391
* - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
393
* Neither is particularly nice.
395
* Life would be much easier if C11 threads had different abstractions for
396
* threads and thread IDs, just like C++11 threads does...
399
bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
400
GetCurrentThread(), // source (pseudo) handle
401
GetCurrentProcess(), // target process
402
&hCurrentThread, // target handle
405
DUPLICATE_SAME_ACCESS);
408
hCurrentThread = GetCurrentThread();
410
return hCurrentThread;
416
thrd_detach(thrd_t thr)
424
thrd_equal(thrd_t thr0, thrd_t thr1)
426
return GetThreadId(thr0) == GetThreadId(thr1);
433
impl_tss_dtor_invoke();
434
_endthreadex((unsigned)res);
439
thrd_join(thrd_t thr, int *res)
442
w = WaitForSingleObject(thr, INFINITE);
443
if (w != WAIT_OBJECT_0)
446
if (!GetExitCodeThread(thr, &code)) {
458
thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
462
assert(!remaining); /* not implemented */
463
Sleep((DWORD)impl_timespec2msec(time_point));
474
/*----------- 7.25.6 Thread-specific storage functions -----------*/
477
tss_create(tss_t *key, tss_dtor_t dtor)
482
if (impl_tss_dtor_register(*key, dtor)) {
487
return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
492
tss_delete(tss_t key)
501
return TlsGetValue(key);
506
tss_set(tss_t key, void *val)
508
return TlsSetValue(key, val) ? thrd_success : thrd_error;
512
/*-------------------- 7.25.7 Time functions --------------------*/
514
#ifndef HAVE_TIMESPEC_GET
516
timespec_get(struct timespec *ts, int base)
519
if (base == TIME_UTC) {
520
ts->tv_sec = time(NULL);