• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)&param, 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