threads: update for late C11 changes
[mesa.git] / include / c11 / threads_win32.h
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_USE_NATIVE_CV
46 Use native WindowsAPI condition variable object.
47 (requires WinVista or later)
48 Otherwise use emulated implementation for WinXP.
49
50 EMULATED_THREADS_TSS_DTOR_SLOTNUM
51 Max registerable TSS dtor number.
52 */
53
54 // XXX: Retain XP compatability
55 #if 0
56 #if _WIN32_WINNT >= 0x0600
57 // Prefer native WindowsAPI on newer environment.
58 #if !defined(__MINGW32__)
59 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
60 #endif
61 #define EMULATED_THREADS_USE_NATIVE_CV
62 #endif
63 #endif
64 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
65
66
67 #include <windows.h>
68
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
72 #endif
73
74 #if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600)
75 #error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600
76 #endif
77
78 /* Visual Studio 2015 and later */
79 #if _MSC_VER >= 1900
80 #define HAVE_TIMESPEC
81 #endif
82
83 #ifndef HAVE_TIMESPEC
84 struct timespec {
85 time_t tv_sec;
86 long tv_nsec;
87 };
88 #endif
89
90 /*---------------------------- macros ----------------------------*/
91 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
92 #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
93 #else
94 #define ONCE_FLAG_INIT {0}
95 #endif
96 #define TSS_DTOR_ITERATIONS 1
97
98 // FIXME: temporary non-standard hack to ease transition
99 #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
100
101 /*---------------------------- types ----------------------------*/
102 typedef struct cnd_t {
103 #ifdef EMULATED_THREADS_USE_NATIVE_CV
104 CONDITION_VARIABLE condvar;
105 #else
106 int blocked;
107 int gone;
108 int to_unblock;
109 HANDLE sem_queue;
110 HANDLE sem_gate;
111 CRITICAL_SECTION monitor;
112 #endif
113 } cnd_t;
114
115 typedef HANDLE thrd_t;
116
117 typedef DWORD tss_t;
118
119 typedef CRITICAL_SECTION mtx_t;
120
121 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
122 typedef INIT_ONCE once_flag;
123 #else
124 typedef struct once_flag_t {
125 volatile LONG status;
126 } once_flag;
127 #endif
128
129
130 static inline void * tss_get(tss_t key);
131 static inline void thrd_yield(void);
132 static inline int mtx_trylock(mtx_t *mtx);
133 static inline int mtx_lock(mtx_t *mtx);
134 static inline int mtx_unlock(mtx_t *mtx);
135
136 /*
137 Implementation limits:
138 - Conditionally emulation for "Initialization functions"
139 (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
140 - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
141 */
142 static void impl_tss_dtor_invoke(void); // forward decl.
143
144 struct impl_thrd_param {
145 thrd_start_t func;
146 void *arg;
147 };
148
149 static unsigned __stdcall impl_thrd_routine(void *p)
150 {
151 struct impl_thrd_param pack;
152 int code;
153 memcpy(&pack, p, sizeof(struct impl_thrd_param));
154 free(p);
155 code = pack.func(pack.arg);
156 impl_tss_dtor_invoke();
157 return (unsigned)code;
158 }
159
160 static DWORD impl_timespec2msec(const struct timespec *ts)
161 {
162 return (DWORD)((ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L));
163 }
164
165 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
166 struct impl_call_once_param { void (*func)(void); };
167 static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
168 {
169 struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
170 (param->func)();
171 ((void)InitOnce); ((void)Context); // suppress warning
172 return TRUE;
173 }
174 #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
175
176 #ifndef EMULATED_THREADS_USE_NATIVE_CV
177 /*
178 Note:
179 The implementation of condition variable is ported from Boost.Interprocess
180 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
181 */
182 static void impl_cond_do_signal(cnd_t *cond, int broadcast)
183 {
184 int nsignal = 0;
185
186 EnterCriticalSection(&cond->monitor);
187 if (cond->to_unblock != 0) {
188 if (cond->blocked == 0) {
189 LeaveCriticalSection(&cond->monitor);
190 return;
191 }
192 if (broadcast) {
193 cond->to_unblock += nsignal = cond->blocked;
194 cond->blocked = 0;
195 } else {
196 nsignal = 1;
197 cond->to_unblock++;
198 cond->blocked--;
199 }
200 } else if (cond->blocked > cond->gone) {
201 WaitForSingleObject(cond->sem_gate, INFINITE);
202 if (cond->gone != 0) {
203 cond->blocked -= cond->gone;
204 cond->gone = 0;
205 }
206 if (broadcast) {
207 nsignal = cond->to_unblock = cond->blocked;
208 cond->blocked = 0;
209 } else {
210 nsignal = cond->to_unblock = 1;
211 cond->blocked--;
212 }
213 }
214 LeaveCriticalSection(&cond->monitor);
215
216 if (0 < nsignal)
217 ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
218 }
219
220 static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
221 {
222 int nleft = 0;
223 int ngone = 0;
224 int timeout = 0;
225 DWORD w;
226
227 WaitForSingleObject(cond->sem_gate, INFINITE);
228 cond->blocked++;
229 ReleaseSemaphore(cond->sem_gate, 1, NULL);
230
231 mtx_unlock(mtx);
232
233 w = WaitForSingleObject(cond->sem_queue, ts ? impl_timespec2msec(ts) : INFINITE);
234 timeout = (w == WAIT_TIMEOUT);
235
236 EnterCriticalSection(&cond->monitor);
237 if ((nleft = cond->to_unblock) != 0) {
238 if (timeout) {
239 if (cond->blocked != 0) {
240 cond->blocked--;
241 } else {
242 cond->gone++;
243 }
244 }
245 if (--cond->to_unblock == 0) {
246 if (cond->blocked != 0) {
247 ReleaseSemaphore(cond->sem_gate, 1, NULL);
248 nleft = 0;
249 }
250 else if ((ngone = cond->gone) != 0) {
251 cond->gone = 0;
252 }
253 }
254 } else if (++cond->gone == INT_MAX/2) {
255 WaitForSingleObject(cond->sem_gate, INFINITE);
256 cond->blocked -= cond->gone;
257 ReleaseSemaphore(cond->sem_gate, 1, NULL);
258 cond->gone = 0;
259 }
260 LeaveCriticalSection(&cond->monitor);
261
262 if (nleft == 1) {
263 while (ngone--)
264 WaitForSingleObject(cond->sem_queue, INFINITE);
265 ReleaseSemaphore(cond->sem_gate, 1, NULL);
266 }
267
268 mtx_lock(mtx);
269 return timeout ? thrd_busy : thrd_success;
270 }
271 #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
272
273 static struct impl_tss_dtor_entry {
274 tss_t key;
275 tss_dtor_t dtor;
276 } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
277
278 static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
279 {
280 int i;
281 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
282 if (!impl_tss_dtor_tbl[i].dtor)
283 break;
284 }
285 if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
286 return 1;
287 impl_tss_dtor_tbl[i].key = key;
288 impl_tss_dtor_tbl[i].dtor = dtor;
289 return 0;
290 }
291
292 static void impl_tss_dtor_invoke()
293 {
294 int i;
295 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
296 if (impl_tss_dtor_tbl[i].dtor) {
297 void* val = tss_get(impl_tss_dtor_tbl[i].key);
298 if (val)
299 (impl_tss_dtor_tbl[i].dtor)(val);
300 }
301 }
302 }
303
304
305 /*--------------- 7.25.2 Initialization functions ---------------*/
306 // 7.25.2.1
307 static inline void
308 call_once(once_flag *flag, void (*func)(void))
309 {
310 assert(flag && func);
311 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
312 {
313 struct impl_call_once_param param;
314 param.func = func;
315 InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)&param, NULL);
316 }
317 #else
318 if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
319 (func)();
320 InterlockedExchange(&flag->status, 2);
321 } else {
322 while (flag->status == 1) {
323 // busy loop!
324 thrd_yield();
325 }
326 }
327 #endif
328 }
329
330
331 /*------------- 7.25.3 Condition variable functions -------------*/
332 // 7.25.3.1
333 static inline int
334 cnd_broadcast(cnd_t *cond)
335 {
336 if (!cond) return thrd_error;
337 #ifdef EMULATED_THREADS_USE_NATIVE_CV
338 WakeAllConditionVariable(&cond->condvar);
339 #else
340 impl_cond_do_signal(cond, 1);
341 #endif
342 return thrd_success;
343 }
344
345 // 7.25.3.2
346 static inline void
347 cnd_destroy(cnd_t *cond)
348 {
349 assert(cond);
350 #ifdef EMULATED_THREADS_USE_NATIVE_CV
351 // do nothing
352 #else
353 CloseHandle(cond->sem_queue);
354 CloseHandle(cond->sem_gate);
355 DeleteCriticalSection(&cond->monitor);
356 #endif
357 }
358
359 // 7.25.3.3
360 static inline int
361 cnd_init(cnd_t *cond)
362 {
363 if (!cond) return thrd_error;
364 #ifdef EMULATED_THREADS_USE_NATIVE_CV
365 InitializeConditionVariable(&cond->condvar);
366 #else
367 cond->blocked = 0;
368 cond->gone = 0;
369 cond->to_unblock = 0;
370 cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
371 cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
372 InitializeCriticalSection(&cond->monitor);
373 #endif
374 return thrd_success;
375 }
376
377 // 7.25.3.4
378 static inline int
379 cnd_signal(cnd_t *cond)
380 {
381 if (!cond) return thrd_error;
382 #ifdef EMULATED_THREADS_USE_NATIVE_CV
383 WakeConditionVariable(&cond->condvar);
384 #else
385 impl_cond_do_signal(cond, 0);
386 #endif
387 return thrd_success;
388 }
389
390 // 7.25.3.5
391 static inline int
392 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
393 {
394 if (!cond || !mtx || !abs_time) return thrd_error;
395 #ifdef EMULATED_THREADS_USE_NATIVE_CV
396 if (SleepConditionVariableCS(&cond->condvar, mtx, impl_timespec2msec(abs_time)))
397 return thrd_success;
398 return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
399 #else
400 return impl_cond_do_wait(cond, mtx, abs_time);
401 #endif
402 }
403
404 // 7.25.3.6
405 static inline int
406 cnd_wait(cnd_t *cond, mtx_t *mtx)
407 {
408 if (!cond || !mtx) return thrd_error;
409 #ifdef EMULATED_THREADS_USE_NATIVE_CV
410 SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
411 #else
412 impl_cond_do_wait(cond, mtx, NULL);
413 #endif
414 return thrd_success;
415 }
416
417
418 /*-------------------- 7.25.4 Mutex functions --------------------*/
419 // 7.25.4.1
420 static inline void
421 mtx_destroy(mtx_t *mtx)
422 {
423 assert(mtx);
424 DeleteCriticalSection(mtx);
425 }
426
427 // 7.25.4.2
428 static inline int
429 mtx_init(mtx_t *mtx, int type)
430 {
431 if (!mtx) return thrd_error;
432 if (type != mtx_plain && type != mtx_timed && type != mtx_try
433 && type != (mtx_plain|mtx_recursive)
434 && type != (mtx_timed|mtx_recursive)
435 && type != (mtx_try|mtx_recursive))
436 return thrd_error;
437 InitializeCriticalSection(mtx);
438 return thrd_success;
439 }
440
441 // 7.25.4.3
442 static inline int
443 mtx_lock(mtx_t *mtx)
444 {
445 if (!mtx) return thrd_error;
446 EnterCriticalSection(mtx);
447 return thrd_success;
448 }
449
450 // 7.25.4.4
451 static inline int
452 mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
453 {
454 time_t expire, now;
455 if (!mtx || !ts) return thrd_error;
456 expire = time(NULL);
457 expire += ts->tv_sec;
458 while (mtx_trylock(mtx) != thrd_success) {
459 now = time(NULL);
460 if (expire < now)
461 return thrd_busy;
462 // busy loop!
463 thrd_yield();
464 }
465 return thrd_success;
466 }
467
468 // 7.25.4.5
469 static inline int
470 mtx_trylock(mtx_t *mtx)
471 {
472 if (!mtx) return thrd_error;
473 return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
474 }
475
476 // 7.25.4.6
477 static inline int
478 mtx_unlock(mtx_t *mtx)
479 {
480 if (!mtx) return thrd_error;
481 LeaveCriticalSection(mtx);
482 return thrd_success;
483 }
484
485
486 /*------------------- 7.25.5 Thread functions -------------------*/
487 // 7.25.5.1
488 static inline int
489 thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
490 {
491 struct impl_thrd_param *pack;
492 uintptr_t handle;
493 if (!thr) return thrd_error;
494 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
495 if (!pack) return thrd_nomem;
496 pack->func = func;
497 pack->arg = arg;
498 handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
499 if (handle == 0) {
500 if (errno == EAGAIN || errno == EACCES)
501 return thrd_nomem;
502 return thrd_error;
503 }
504 *thr = (thrd_t)handle;
505 return thrd_success;
506 }
507
508 #if 0
509 // 7.25.5.2
510 static inline thrd_t
511 thrd_current(void)
512 {
513 HANDLE hCurrentThread;
514 BOOL bRet;
515
516 /* GetCurrentThread() returns a pseudo-handle, which we need
517 * to pass to DuplicateHandle(). Only the resulting handle can be used
518 * from other threads.
519 *
520 * Note that neither handle can be compared to the one by thread_create.
521 * Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
522 * can be compared directly.
523 *
524 * Other potential solutions would be:
525 * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
526 * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
527 *
528 * Neither is particularly nice.
529 *
530 * Life would be much easier if C11 threads had different abstractions for
531 * threads and thread IDs, just like C++11 threads does...
532 */
533
534 bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
535 GetCurrentThread(), // source (pseudo) handle
536 GetCurrentProcess(), // target process
537 &hCurrentThread, // target handle
538 0,
539 FALSE,
540 DUPLICATE_SAME_ACCESS);
541 assert(bRet);
542 if (!bRet) {
543 hCurrentThread = GetCurrentThread();
544 }
545 return hCurrentThread;
546 }
547 #endif
548
549 // 7.25.5.3
550 static inline int
551 thrd_detach(thrd_t thr)
552 {
553 CloseHandle(thr);
554 return thrd_success;
555 }
556
557 // 7.25.5.4
558 static inline int
559 thrd_equal(thrd_t thr0, thrd_t thr1)
560 {
561 return GetThreadId(thr0) == GetThreadId(thr1);
562 }
563
564 // 7.25.5.5
565 static inline void
566 thrd_exit(int res)
567 {
568 impl_tss_dtor_invoke();
569 _endthreadex((unsigned)res);
570 }
571
572 // 7.25.5.6
573 static inline int
574 thrd_join(thrd_t thr, int *res)
575 {
576 DWORD w, code;
577 w = WaitForSingleObject(thr, INFINITE);
578 if (w != WAIT_OBJECT_0)
579 return thrd_error;
580 if (res) {
581 if (!GetExitCodeThread(thr, &code)) {
582 CloseHandle(thr);
583 return thrd_error;
584 }
585 *res = (int)code;
586 }
587 CloseHandle(thr);
588 return thrd_success;
589 }
590
591 // 7.25.5.7
592 static inline void
593 thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
594 {
595 assert(time_point);
596 assert(!remaining); /* not implemented */
597 Sleep(impl_timespec2msec(time_point));
598 }
599
600 // 7.25.5.8
601 static inline void
602 thrd_yield(void)
603 {
604 SwitchToThread();
605 }
606
607
608 /*----------- 7.25.6 Thread-specific storage functions -----------*/
609 // 7.25.6.1
610 static inline int
611 tss_create(tss_t *key, tss_dtor_t dtor)
612 {
613 if (!key) return thrd_error;
614 *key = TlsAlloc();
615 if (dtor) {
616 if (impl_tss_dtor_register(*key, dtor)) {
617 TlsFree(*key);
618 return thrd_error;
619 }
620 }
621 return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
622 }
623
624 // 7.25.6.2
625 static inline void
626 tss_delete(tss_t key)
627 {
628 TlsFree(key);
629 }
630
631 // 7.25.6.3
632 static inline void *
633 tss_get(tss_t key)
634 {
635 return TlsGetValue(key);
636 }
637
638 // 7.25.6.4
639 static inline int
640 tss_set(tss_t key, void *val)
641 {
642 return TlsSetValue(key, val) ? thrd_success : thrd_error;
643 }
644
645
646 /*-------------------- 7.25.7 Time functions --------------------*/
647 // 7.25.6.1
648 #ifndef HAVE_TIMESPEC
649 static inline int
650 timespec_get(struct timespec *ts, int base)
651 {
652 if (!ts) return 0;
653 if (base == TIME_UTC) {
654 ts->tv_sec = time(NULL);
655 ts->tv_nsec = 0;
656 return base;
657 }
658 return 0;
659 }
660 #endif