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