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_USE_NATIVE_CV
46 Use native WindowsAPI condition variable object.
47 (requires WinVista or later)
48 Otherwise use emulated implementation for WinXP.
50 EMULATED_THREADS_TSS_DTOR_SLOTNUM
51 Max registerable TSS dtor number.
54 // XXX: Retain XP compatability
56 #if _WIN32_WINNT >= 0x0600
57 // Prefer native WindowsAPI on newer environment.
58 #if !defined(__MINGW32__)
59 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
61 #define EMULATED_THREADS_USE_NATIVE_CV
64 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
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
74 #if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600)
75 #error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600
79 /*---------------------------- macros ----------------------------*/
80 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
81 #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
83 #define ONCE_FLAG_INIT {0}
85 #define TSS_DTOR_ITERATIONS 1
87 // FIXME: temporary non-standard hack to ease transition
88 #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
90 /*---------------------------- types ----------------------------*/
91 typedef struct cnd_t {
92 #ifdef EMULATED_THREADS_USE_NATIVE_CV
93 CONDITION_VARIABLE condvar;
100 CRITICAL_SECTION monitor;
104 typedef HANDLE thrd_t;
108 typedef CRITICAL_SECTION mtx_t;
110 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
111 typedef INIT_ONCE once_flag;
113 typedef struct once_flag_t {
114 volatile LONG status;
119 static inline void * tss_get(tss_t key);
120 static inline void thrd_yield(void);
121 static inline int mtx_trylock(mtx_t *mtx);
122 static inline int mtx_lock(mtx_t *mtx);
123 static inline int mtx_unlock(mtx_t *mtx);
126 Implementation 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*
131 static void impl_tss_dtor_invoke(void); // forward decl.
133 struct impl_thrd_param {
138 static unsigned __stdcall impl_thrd_routine(void *p)
140 struct impl_thrd_param pack;
142 memcpy(&pack, p, sizeof(struct impl_thrd_param));
144 code = pack.func(pack.arg);
145 impl_tss_dtor_invoke();
146 return (unsigned)code;
149 static DWORD impl_xtime2msec(const xtime *xt)
151 return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L));
154 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
155 struct impl_call_once_param { void (*func)(void); };
156 static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
158 struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
160 ((void)InitOnce); ((void)Context); // suppress warning
163 #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
165 #ifndef EMULATED_THREADS_USE_NATIVE_CV
168 The implementation of condition variable is ported from Boost.Interprocess
169 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
171 static void impl_cond_do_signal(cnd_t *cond, int broadcast)
175 EnterCriticalSection(&cond->monitor);
176 if (cond->to_unblock != 0) {
177 if (cond->blocked == 0) {
178 LeaveCriticalSection(&cond->monitor);
182 cond->to_unblock += nsignal = cond->blocked;
189 } else if (cond->blocked > cond->gone) {
190 WaitForSingleObject(cond->sem_gate, INFINITE);
191 if (cond->gone != 0) {
192 cond->blocked -= cond->gone;
196 nsignal = cond->to_unblock = cond->blocked;
199 nsignal = cond->to_unblock = 1;
203 LeaveCriticalSection(&cond->monitor);
206 ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
209 static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
216 WaitForSingleObject(cond->sem_gate, INFINITE);
218 ReleaseSemaphore(cond->sem_gate, 1, NULL);
222 w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE);
223 timeout = (w == WAIT_TIMEOUT);
225 EnterCriticalSection(&cond->monitor);
226 if ((nleft = cond->to_unblock) != 0) {
228 if (cond->blocked != 0) {
234 if (--cond->to_unblock == 0) {
235 if (cond->blocked != 0) {
236 ReleaseSemaphore(cond->sem_gate, 1, NULL);
239 else if ((ngone = cond->gone) != 0) {
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);
249 LeaveCriticalSection(&cond->monitor);
253 WaitForSingleObject(cond->sem_queue, INFINITE);
254 ReleaseSemaphore(cond->sem_gate, 1, NULL);
258 return timeout ? thrd_busy : thrd_success;
260 #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
262 static struct impl_tss_dtor_entry {
265 } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
267 static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
270 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
271 if (!impl_tss_dtor_tbl[i].dtor)
274 if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
276 impl_tss_dtor_tbl[i].key = key;
277 impl_tss_dtor_tbl[i].dtor = dtor;
281 static void impl_tss_dtor_invoke()
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);
288 (impl_tss_dtor_tbl[i].dtor)(val);
294 /*--------------- 7.25.2 Initialization functions ---------------*/
297 call_once(once_flag *flag, void (*func)(void))
299 assert(flag && func);
300 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
302 struct impl_call_once_param param;
304 InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL);
307 if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
309 InterlockedExchange(&flag->status, 2);
311 while (flag->status == 1) {
320 /*------------- 7.25.3 Condition variable functions -------------*/
323 cnd_broadcast(cnd_t *cond)
325 if (!cond) return thrd_error;
326 #ifdef EMULATED_THREADS_USE_NATIVE_CV
327 WakeAllConditionVariable(&cond->condvar);
329 impl_cond_do_signal(cond, 1);
336 cnd_destroy(cnd_t *cond)
339 #ifdef EMULATED_THREADS_USE_NATIVE_CV
342 CloseHandle(cond->sem_queue);
343 CloseHandle(cond->sem_gate);
344 DeleteCriticalSection(&cond->monitor);
350 cnd_init(cnd_t *cond)
352 if (!cond) return thrd_error;
353 #ifdef EMULATED_THREADS_USE_NATIVE_CV
354 InitializeConditionVariable(&cond->condvar);
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);
368 cnd_signal(cnd_t *cond)
370 if (!cond) return thrd_error;
371 #ifdef EMULATED_THREADS_USE_NATIVE_CV
372 WakeConditionVariable(&cond->condvar);
374 impl_cond_do_signal(cond, 0);
381 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
383 if (!cond || !mtx || !xt) return thrd_error;
384 #ifdef EMULATED_THREADS_USE_NATIVE_CV
385 if (SleepConditionVariableCS(&cond->condvar, mtx, impl_xtime2msec(xt)))
387 return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
389 return impl_cond_do_wait(cond, mtx, xt);
395 cnd_wait(cnd_t *cond, mtx_t *mtx)
397 if (!cond || !mtx) return thrd_error;
398 #ifdef EMULATED_THREADS_USE_NATIVE_CV
399 SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
401 impl_cond_do_wait(cond, mtx, NULL);
407 /*-------------------- 7.25.4 Mutex functions --------------------*/
410 mtx_destroy(mtx_t *mtx)
413 DeleteCriticalSection(mtx);
418 mtx_init(mtx_t *mtx, int type)
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))
426 InitializeCriticalSection(mtx);
434 if (!mtx) return thrd_error;
435 EnterCriticalSection(mtx);
441 mtx_timedlock(mtx_t *mtx, const xtime *xt)
444 if (!mtx || !xt) return thrd_error;
447 while (mtx_trylock(mtx) != thrd_success) {
459 mtx_trylock(mtx_t *mtx)
461 if (!mtx) return thrd_error;
462 return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
467 mtx_unlock(mtx_t *mtx)
469 if (!mtx) return thrd_error;
470 LeaveCriticalSection(mtx);
475 /*------------------- 7.25.5 Thread functions -------------------*/
478 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
480 struct impl_thrd_param *pack;
482 if (!thr) return thrd_error;
483 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
484 if (!pack) return thrd_nomem;
487 handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
489 if (errno == EAGAIN || errno == EACCES)
493 *thr = (thrd_t)handle;
502 HANDLE hCurrentThread;
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.
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.
513 * Neither is particularly nice.
515 * Life would be much easier if C11 threads had different abstractions for
516 * threads and thread IDs, just like C++11 threads does...
519 bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
520 GetCurrentThread(), // source (pseudo) handle
521 GetCurrentProcess(), // target process
522 &hCurrentThread, // target handle
525 DUPLICATE_SAME_ACCESS);
528 hCurrentThread = GetCurrentThread();
530 return hCurrentThread;
536 thrd_detach(thrd_t thr)
544 thrd_equal(thrd_t thr0, thrd_t thr1)
546 return GetThreadId(thr0) == GetThreadId(thr1);
553 impl_tss_dtor_invoke();
554 _endthreadex((unsigned)res);
559 thrd_join(thrd_t thr, int *res)
562 w = WaitForSingleObject(thr, INFINITE);
563 if (w != WAIT_OBJECT_0)
566 if (!GetExitCodeThread(thr, &code)) {
578 thrd_sleep(const xtime *xt)
581 Sleep(impl_xtime2msec(xt));
592 /*----------- 7.25.6 Thread-specific storage functions -----------*/
595 tss_create(tss_t *key, tss_dtor_t dtor)
597 if (!key) return thrd_error;
600 if (impl_tss_dtor_register(*key, dtor)) {
605 return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
610 tss_delete(tss_t key)
619 return TlsGetValue(key);
624 tss_set(tss_t key, void *val)
626 return TlsSetValue(key, val) ? thrd_success : thrd_error;
630 /*-------------------- 7.25.7 Time functions --------------------*/
633 xtime_get(xtime *xt, int base)
636 if (base == TIME_UTC) {
637 xt->sec = time(NULL);