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
78 /* Visual Studio 2015 and later */
81 #define HAVE_TIMESPEC_GET
82 #elif defined(__MINGW32__)
93 /*---------------------------- macros ----------------------------*/
94 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
95 #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
97 #define ONCE_FLAG_INIT {0}
99 #define TSS_DTOR_ITERATIONS 1
101 // FIXME: temporary non-standard hack to ease transition
102 #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
104 /*---------------------------- types ----------------------------*/
105 typedef struct cnd_t
{
106 #ifdef EMULATED_THREADS_USE_NATIVE_CV
107 CONDITION_VARIABLE condvar
;
114 CRITICAL_SECTION monitor
;
118 typedef HANDLE thrd_t
;
122 typedef CRITICAL_SECTION mtx_t
;
124 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
125 typedef INIT_ONCE once_flag
;
127 typedef struct once_flag_t
{
128 volatile LONG status
;
133 static inline void * tss_get(tss_t key
);
134 static inline void thrd_yield(void);
135 static inline int mtx_trylock(mtx_t
*mtx
);
136 static inline int mtx_lock(mtx_t
*mtx
);
137 static inline int mtx_unlock(mtx_t
*mtx
);
140 Implementation limits:
141 - Conditionally emulation for "Initialization functions"
142 (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
143 - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
145 static void impl_tss_dtor_invoke(void); // forward decl.
147 struct impl_thrd_param
{
152 static unsigned __stdcall
impl_thrd_routine(void *p
)
154 struct impl_thrd_param pack
;
156 memcpy(&pack
, p
, sizeof(struct impl_thrd_param
));
158 code
= pack
.func(pack
.arg
);
159 impl_tss_dtor_invoke();
160 return (unsigned)code
;
163 static DWORD
impl_timespec2msec(const struct timespec
*ts
)
165 return (DWORD
)((ts
->tv_sec
* 1000U) + (ts
->tv_nsec
/ 1000000L));
168 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
169 struct impl_call_once_param
{ void (*func
)(void); };
170 static BOOL CALLBACK
impl_call_once_callback(PINIT_ONCE InitOnce
, PVOID Parameter
, PVOID
*Context
)
172 struct impl_call_once_param
*param
= (struct impl_call_once_param
*)Parameter
;
174 ((void)InitOnce
); ((void)Context
); // suppress warning
177 #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
179 #ifndef EMULATED_THREADS_USE_NATIVE_CV
182 The implementation of condition variable is ported from Boost.Interprocess
183 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
185 static void impl_cond_do_signal(cnd_t
*cond
, int broadcast
)
189 EnterCriticalSection(&cond
->monitor
);
190 if (cond
->to_unblock
!= 0) {
191 if (cond
->blocked
== 0) {
192 LeaveCriticalSection(&cond
->monitor
);
196 cond
->to_unblock
+= nsignal
= cond
->blocked
;
203 } else if (cond
->blocked
> cond
->gone
) {
204 WaitForSingleObject(cond
->sem_gate
, INFINITE
);
205 if (cond
->gone
!= 0) {
206 cond
->blocked
-= cond
->gone
;
210 nsignal
= cond
->to_unblock
= cond
->blocked
;
213 nsignal
= cond
->to_unblock
= 1;
217 LeaveCriticalSection(&cond
->monitor
);
220 ReleaseSemaphore(cond
->sem_queue
, nsignal
, NULL
);
223 static int impl_cond_do_wait(cnd_t
*cond
, mtx_t
*mtx
, const struct timespec
*ts
)
230 WaitForSingleObject(cond
->sem_gate
, INFINITE
);
232 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
236 w
= WaitForSingleObject(cond
->sem_queue
, ts
? impl_timespec2msec(ts
) : INFINITE
);
237 timeout
= (w
== WAIT_TIMEOUT
);
239 EnterCriticalSection(&cond
->monitor
);
240 if ((nleft
= cond
->to_unblock
) != 0) {
242 if (cond
->blocked
!= 0) {
248 if (--cond
->to_unblock
== 0) {
249 if (cond
->blocked
!= 0) {
250 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
253 else if ((ngone
= cond
->gone
) != 0) {
257 } else if (++cond
->gone
== INT_MAX
/2) {
258 WaitForSingleObject(cond
->sem_gate
, INFINITE
);
259 cond
->blocked
-= cond
->gone
;
260 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
263 LeaveCriticalSection(&cond
->monitor
);
267 WaitForSingleObject(cond
->sem_queue
, INFINITE
);
268 ReleaseSemaphore(cond
->sem_gate
, 1, NULL
);
272 return timeout
? thrd_busy
: thrd_success
;
274 #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
276 static struct impl_tss_dtor_entry
{
279 } impl_tss_dtor_tbl
[EMULATED_THREADS_TSS_DTOR_SLOTNUM
];
281 static int impl_tss_dtor_register(tss_t key
, tss_dtor_t dtor
)
284 for (i
= 0; i
< EMULATED_THREADS_TSS_DTOR_SLOTNUM
; i
++) {
285 if (!impl_tss_dtor_tbl
[i
].dtor
)
288 if (i
== EMULATED_THREADS_TSS_DTOR_SLOTNUM
)
290 impl_tss_dtor_tbl
[i
].key
= key
;
291 impl_tss_dtor_tbl
[i
].dtor
= dtor
;
295 static void impl_tss_dtor_invoke()
298 for (i
= 0; i
< EMULATED_THREADS_TSS_DTOR_SLOTNUM
; i
++) {
299 if (impl_tss_dtor_tbl
[i
].dtor
) {
300 void* val
= tss_get(impl_tss_dtor_tbl
[i
].key
);
302 (impl_tss_dtor_tbl
[i
].dtor
)(val
);
308 /*--------------- 7.25.2 Initialization functions ---------------*/
311 call_once(once_flag
*flag
, void (*func
)(void))
313 assert(flag
&& func
);
314 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
316 struct impl_call_once_param param
;
318 InitOnceExecuteOnce(flag
, impl_call_once_callback
, (PVOID
)¶m
, NULL
);
321 if (InterlockedCompareExchange(&flag
->status
, 1, 0) == 0) {
323 InterlockedExchange(&flag
->status
, 2);
325 while (flag
->status
== 1) {
334 /*------------- 7.25.3 Condition variable functions -------------*/
337 cnd_broadcast(cnd_t
*cond
)
339 if (!cond
) return thrd_error
;
340 #ifdef EMULATED_THREADS_USE_NATIVE_CV
341 WakeAllConditionVariable(&cond
->condvar
);
343 impl_cond_do_signal(cond
, 1);
350 cnd_destroy(cnd_t
*cond
)
353 #ifdef EMULATED_THREADS_USE_NATIVE_CV
356 CloseHandle(cond
->sem_queue
);
357 CloseHandle(cond
->sem_gate
);
358 DeleteCriticalSection(&cond
->monitor
);
364 cnd_init(cnd_t
*cond
)
366 if (!cond
) return thrd_error
;
367 #ifdef EMULATED_THREADS_USE_NATIVE_CV
368 InitializeConditionVariable(&cond
->condvar
);
372 cond
->to_unblock
= 0;
373 cond
->sem_queue
= CreateSemaphore(NULL
, 0, LONG_MAX
, NULL
);
374 cond
->sem_gate
= CreateSemaphore(NULL
, 1, 1, NULL
);
375 InitializeCriticalSection(&cond
->monitor
);
382 cnd_signal(cnd_t
*cond
)
384 if (!cond
) return thrd_error
;
385 #ifdef EMULATED_THREADS_USE_NATIVE_CV
386 WakeConditionVariable(&cond
->condvar
);
388 impl_cond_do_signal(cond
, 0);
395 cnd_timedwait(cnd_t
*cond
, mtx_t
*mtx
, const struct timespec
*abs_time
)
397 if (!cond
|| !mtx
|| !abs_time
) return thrd_error
;
398 #ifdef EMULATED_THREADS_USE_NATIVE_CV
399 if (SleepConditionVariableCS(&cond
->condvar
, mtx
, impl_timespec2msec(abs_time
)))
401 return (GetLastError() == ERROR_TIMEOUT
) ? thrd_busy
: thrd_error
;
403 return impl_cond_do_wait(cond
, mtx
, abs_time
);
409 cnd_wait(cnd_t
*cond
, mtx_t
*mtx
)
411 if (!cond
|| !mtx
) return thrd_error
;
412 #ifdef EMULATED_THREADS_USE_NATIVE_CV
413 SleepConditionVariableCS(&cond
->condvar
, mtx
, INFINITE
);
415 impl_cond_do_wait(cond
, mtx
, NULL
);
421 /*-------------------- 7.25.4 Mutex functions --------------------*/
424 mtx_destroy(mtx_t
*mtx
)
427 DeleteCriticalSection(mtx
);
432 mtx_init(mtx_t
*mtx
, int type
)
434 if (!mtx
) return thrd_error
;
435 if (type
!= mtx_plain
&& type
!= mtx_timed
&& type
!= mtx_try
436 && type
!= (mtx_plain
|mtx_recursive
)
437 && type
!= (mtx_timed
|mtx_recursive
)
438 && type
!= (mtx_try
|mtx_recursive
))
440 InitializeCriticalSection(mtx
);
448 if (!mtx
) return thrd_error
;
449 EnterCriticalSection(mtx
);
455 mtx_timedlock(mtx_t
*mtx
, const struct timespec
*ts
)
458 if (!mtx
|| !ts
) return thrd_error
;
460 expire
+= ts
->tv_sec
;
461 while (mtx_trylock(mtx
) != thrd_success
) {
473 mtx_trylock(mtx_t
*mtx
)
475 if (!mtx
) return thrd_error
;
476 return TryEnterCriticalSection(mtx
) ? thrd_success
: thrd_busy
;
481 mtx_unlock(mtx_t
*mtx
)
483 if (!mtx
) return thrd_error
;
484 LeaveCriticalSection(mtx
);
489 /*------------------- 7.25.5 Thread functions -------------------*/
492 thrd_create(thrd_t
*thr
, thrd_start_t func
, void *arg
)
494 struct impl_thrd_param
*pack
;
496 if (!thr
) return thrd_error
;
497 pack
= (struct impl_thrd_param
*)malloc(sizeof(struct impl_thrd_param
));
498 if (!pack
) return thrd_nomem
;
501 handle
= _beginthreadex(NULL
, 0, impl_thrd_routine
, pack
, 0, NULL
);
503 if (errno
== EAGAIN
|| errno
== EACCES
)
507 *thr
= (thrd_t
)handle
;
516 HANDLE hCurrentThread
;
519 /* GetCurrentThread() returns a pseudo-handle, which we need
520 * to pass to DuplicateHandle(). Only the resulting handle can be used
521 * from other threads.
523 * Note that neither handle can be compared to the one by thread_create.
524 * Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
525 * can be compared directly.
527 * Other potential solutions would be:
528 * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
529 * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
531 * Neither is particularly nice.
533 * Life would be much easier if C11 threads had different abstractions for
534 * threads and thread IDs, just like C++11 threads does...
537 bRet
= DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
538 GetCurrentThread(), // source (pseudo) handle
539 GetCurrentProcess(), // target process
540 &hCurrentThread
, // target handle
543 DUPLICATE_SAME_ACCESS
);
546 hCurrentThread
= GetCurrentThread();
548 return hCurrentThread
;
554 thrd_detach(thrd_t thr
)
562 thrd_equal(thrd_t thr0
, thrd_t thr1
)
564 return GetThreadId(thr0
) == GetThreadId(thr1
);
571 impl_tss_dtor_invoke();
572 _endthreadex((unsigned)res
);
577 thrd_join(thrd_t thr
, int *res
)
580 w
= WaitForSingleObject(thr
, INFINITE
);
581 if (w
!= WAIT_OBJECT_0
)
584 if (!GetExitCodeThread(thr
, &code
)) {
596 thrd_sleep(const struct timespec
*time_point
, struct timespec
*remaining
)
599 assert(!remaining
); /* not implemented */
600 Sleep(impl_timespec2msec(time_point
));
611 /*----------- 7.25.6 Thread-specific storage functions -----------*/
614 tss_create(tss_t
*key
, tss_dtor_t dtor
)
616 if (!key
) return thrd_error
;
619 if (impl_tss_dtor_register(*key
, dtor
)) {
624 return (*key
!= 0xFFFFFFFF) ? thrd_success
: thrd_error
;
629 tss_delete(tss_t key
)
638 return TlsGetValue(key
);
643 tss_set(tss_t key
, void *val
)
645 return TlsSetValue(key
, val
) ? thrd_success
: thrd_error
;
649 /*-------------------- 7.25.7 Time functions --------------------*/
651 #ifndef HAVE_TIMESPEC_GET
653 timespec_get(struct timespec
*ts
, int base
)
656 if (base
== TIME_UTC
) {
657 ts
->tv_sec
= time(NULL
);