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
39 EMULATED_THREADS_USE_NATIVE_CALL_ONCE
40 Use native WindowsAPI one-time initialization function.
41 (requires WinVista or later)
42 Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
44 EMULATED_THREADS_USE_NATIVE_CV
45 Use native WindowsAPI condition variable object.
46 (requires WinVista or later)
47 Otherwise use emulated implementation for WinXP.
49 EMULATED_THREADS_TSS_DTOR_SLOTNUM
50 Max registerable TSS dtor number.
53 // XXX: Retain XP compatability
55 #if _WIN32_WINNT >= 0x0600
56 // Prefer native WindowsAPI on newer environment.
57 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
58 #define EMULATED_THREADS_USE_NATIVE_CV
61 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
67 Implementation limits:
68 - Conditionally emulation for "Initialization functions"
69 (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
70 - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
72 static void impl_tss_dtor_invoke(void); // forward decl.
74 struct impl_thrd_param
{
79 static unsigned __stdcall
impl_thrd_routine(void *p
)
81 struct impl_thrd_param pack
;
83 memcpy(&pack
, p
, sizeof(struct impl_thrd_param
));
85 code
= pack
.func(pack
.arg
);
86 impl_tss_dtor_invoke();
87 return (unsigned)code
;
90 static DWORD
impl_xtime2msec(const xtime
*xt
)
92 return (DWORD
)((xt
->sec
* 1000U) + (xt
->nsec
/ 1000000L));
95 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
96 struct impl_call_once_param
{ void (*func
)(void); };
97 static BOOL CALLBACK
impl_call_once_callback(PINIT_ONCE InitOnce
, PVOID Parameter
, PVOID
*Context
)
99 struct impl_call_once_param
*param
= (struct impl_call_once_param
*)Parameter
;
101 ((void)InitOnce
); ((void)Context
); // suppress warning
104 #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
106 #ifndef EMULATED_THREADS_USE_NATIVE_CV
109 The implementation of condition variable is ported from Boost.Interprocess
110 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
112 static void impl_cond_do_signal(cnd_t
*cond
, int broadcast
)
116 EnterCriticalSection(&cond
->monitor
);
117 if (cond
->to_unblock
!= 0) {
118 if (cond
->blocked
== 0) {
119 LeaveCriticalSection(&cond
->monitor
);
123 cond
->to_unblock
+= nsignal
= cond
->blocked
;
130 } else if (cond
->blocked
> cond
->gone
) {
131 WaitForSingleObject(cond
->sem_gate
, INFINITE
);
132 if (cond
->gone
!= 0) {
133 cond
->blocked
-= cond
->gone
;
137 nsignal
= cond
->to_unblock
= cond
->blocked
;
140 nsignal
= cond
->to_unblock
= 1;
144 LeaveCriticalSection(&cond
->monitor
);
147 ReleaseSemaphore(cond
->sem_queue
, nsignal
, NULL
);
150 static int impl_cond_do_wait(cnd_t
*cond
, mtx_t
*mtx
, const xtime
*xt
)
157 WaitForSingleObject(cond
->sem_gate
, INFINITE
);
159 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
163 w
= WaitForSingleObject(cond
->sem_queue
, xt
? impl_xtime2msec(xt
) : INFINITE
);
164 timeout
= (w
== WAIT_TIMEOUT
);
166 EnterCriticalSection(&cond
->monitor
);
167 if ((nleft
= cond
->to_unblock
) != 0) {
169 if (cond
->blocked
!= 0) {
175 if (--cond
->to_unblock
== 0) {
176 if (cond
->blocked
!= 0) {
177 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
180 else if ((ngone
= cond
->gone
) != 0) {
184 } else if (++cond
->gone
== INT_MAX
/2) {
185 WaitForSingleObject(cond
->sem_gate
, INFINITE
);
186 cond
->blocked
-= cond
->gone
;
187 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
190 LeaveCriticalSection(&cond
->monitor
);
194 WaitForSingleObject(cond
->sem_queue
, INFINITE
);
195 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
199 return timeout
? thrd_busy
: thrd_success
;
201 #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
203 static struct impl_tss_dtor_entry
{
206 } impl_tss_dtor_tbl
[EMULATED_THREADS_TSS_DTOR_SLOTNUM
];
208 static int impl_tss_dtor_register(tss_t key
, tss_dtor_t dtor
)
211 for (i
= 0; i
< EMULATED_THREADS_TSS_DTOR_SLOTNUM
; i
++) {
212 if (!impl_tss_dtor_tbl
[i
].dtor
)
215 if (i
== EMULATED_THREADS_TSS_DTOR_SLOTNUM
)
217 impl_tss_dtor_tbl
[i
].key
= key
;
218 impl_tss_dtor_tbl
[i
].dtor
= dtor
;
222 static void impl_tss_dtor_invoke()
225 for (i
= 0; i
< EMULATED_THREADS_TSS_DTOR_SLOTNUM
; i
++) {
226 if (impl_tss_dtor_tbl
[i
].dtor
) {
227 void* val
= tss_get(impl_tss_dtor_tbl
[i
].key
);
229 (impl_tss_dtor_tbl
[i
].dtor
)(val
);
235 /*--------------- 7.25.2 Initialization functions ---------------*/
237 void call_once(once_flag
*flag
, void (*func
)(void))
239 assert(flag
&& func
);
240 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
242 struct impl_call_once_param param
;
244 InitOnceExecuteOnce(flag
, impl_call_once_callback
, (PVOID
)¶m
, NULL
);
247 if (InterlockedCompareExchange(&flag
->status
, 1, 0) == 0) {
249 InterlockedExchange(&flag
->status
, 2);
251 while (flag
->status
== 1) {
260 /*------------- 7.25.3 Condition variable functions -------------*/
262 int cnd_broadcast(cnd_t
*cond
)
264 if (!cond
) return thrd_error
;
265 #ifdef EMULATED_THREADS_USE_NATIVE_CV
266 WakeAllConditionVariable(&cond
->condvar
);
268 impl_cond_do_signal(cond
, 1);
274 void cnd_destroy(cnd_t
*cond
)
277 #ifdef EMULATED_THREADS_USE_NATIVE_CV
280 CloseHandle(cond
->sem_queue
);
281 CloseHandle(cond
->sem_gate
);
282 DeleteCriticalSection(&cond
->monitor
);
287 int cnd_init(cnd_t
*cond
)
289 if (!cond
) return thrd_error
;
290 #ifdef EMULATED_THREADS_USE_NATIVE_CV
291 InitializeConditionVariable(&cond
->condvar
);
295 cond
->to_unblock
= 0;
296 cond
->sem_queue
= CreateSemaphore(NULL
, 0, LONG_MAX
, NULL
);
297 cond
->sem_gate
= CreateSemaphore(NULL
, 1, 1, NULL
);
298 InitializeCriticalSection(&cond
->monitor
);
304 int cnd_signal(cnd_t
*cond
)
306 if (!cond
) return thrd_error
;
307 #ifdef EMULATED_THREADS_USE_NATIVE_CV
308 WakeConditionVariable(&cond
->condvar
);
310 impl_cond_do_signal(cond
, 0);
316 int cnd_timedwait(cnd_t
*cond
, mtx_t
*mtx
, const xtime
*xt
)
318 if (!cond
|| !mtx
|| !xt
) return thrd_error
;
319 #ifdef EMULATED_THREADS_USE_NATIVE_CV
320 if (SleepConditionVariableCS(&cond
->condvar
, &mtx
->cs
, impl_xtime2msec(xt
)))
322 return (GetLastError() == ERROR_TIMEOUT
) ? thrd_busy
: thrd_error
;
324 return impl_cond_do_wait(cond
, mtx
, xt
);
329 int cnd_wait(cnd_t
*cond
, mtx_t
*mtx
)
331 if (!cond
|| !mtx
) return thrd_error
;
332 #ifdef EMULATED_THREADS_USE_NATIVE_CV
333 SleepConditionVariableCS(&cond
->condvar
, &mtx
->cs
, INFINITE
);
335 impl_cond_do_wait(cond
, mtx
, NULL
);
341 /*-------------------- 7.25.4 Mutex functions --------------------*/
343 void mtx_destroy(mtx_t
*mtx
)
346 DeleteCriticalSection(&mtx
->cs
);
350 int mtx_init(mtx_t
*mtx
, int type
)
352 if (!mtx
) return thrd_error
;
353 if (type
!= mtx_plain
&& type
!= mtx_timed
&& type
!= mtx_try
354 && type
!= (mtx_plain
|mtx_recursive
)
355 && type
!= (mtx_timed
|mtx_recursive
)
356 && type
!= (mtx_try
|mtx_recursive
))
358 InitializeCriticalSection(&mtx
->cs
);
363 int mtx_lock(mtx_t
*mtx
)
365 if (!mtx
) return thrd_error
;
366 EnterCriticalSection(&mtx
->cs
);
371 int mtx_timedlock(mtx_t
*mtx
, const xtime
*xt
)
374 if (!mtx
|| !xt
) return thrd_error
;
377 while (mtx_trylock(mtx
) != thrd_success
) {
388 int mtx_trylock(mtx_t
*mtx
)
390 if (!mtx
) return thrd_error
;
391 return TryEnterCriticalSection(&mtx
->cs
) ? thrd_success
: thrd_busy
;
395 int mtx_unlock(mtx_t
*mtx
)
397 if (!mtx
) return thrd_error
;
398 LeaveCriticalSection(&mtx
->cs
);
403 /*------------------- 7.25.5 Thread functions -------------------*/
405 int thrd_create(thrd_t
*thr
, thrd_start_t func
, void *arg
)
407 struct impl_thrd_param
*pack
;
409 if (!thr
) return thrd_error
;
410 pack
= malloc(sizeof(struct impl_thrd_param
));
411 if (!pack
) return thrd_nomem
;
414 handle
= _beginthreadex(NULL
, 0, impl_thrd_routine
, pack
, 0, NULL
);
416 if (errno
== EAGAIN
|| errno
== EACCES
)
420 *thr
= (thrd_t
)handle
;
425 thrd_t
thrd_current(void)
427 return GetCurrentThread();
431 int thrd_detach(thrd_t thr
)
438 int thrd_equal(thrd_t thr0
, thrd_t thr1
)
440 return (thr0
== thr1
);
444 void thrd_exit(int res
)
446 impl_tss_dtor_invoke();
447 _endthreadex((unsigned)res
);
451 int thrd_join(thrd_t thr
, int *res
)
454 w
= WaitForSingleObject(thr
, INFINITE
);
455 if (w
!= WAIT_OBJECT_0
)
458 if (!GetExitCodeThread(thr
, &code
)) {
469 void thrd_sleep(const xtime
*xt
)
472 Sleep(impl_xtime2msec(xt
));
476 void thrd_yield(void)
482 /*----------- 7.25.6 Thread-specific storage functions -----------*/
484 int tss_create(tss_t
*key
, tss_dtor_t dtor
)
486 if (!key
) return thrd_error
;
489 if (impl_tss_dtor_register(*key
, dtor
)) {
494 return (*key
!= 0xFFFFFFFF) ? thrd_success
: thrd_error
;
498 void tss_delete(tss_t key
)
504 void *tss_get(tss_t key
)
506 return TlsGetValue(key
);
510 int tss_set(tss_t key
, void *val
)
512 return TlsSetValue(key
, val
) ? thrd_success
: thrd_error
;
516 /*-------------------- 7.25.7 Time functions --------------------*/
518 int xtime_get(xtime
*xt
, int base
)
521 if (base
== TIME_UTC
) {
522 xt
->sec
= time(NULL
);