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