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