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