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