tests/gl_basic_test: undo overzealous clang-format changes
[mesa-waffle.git] / third_party / threads / threads_win32.c
blob76c2855a79a4181dfe7ca9882332790c6aac503c
1 /*
2 * C11 <threads.h> emulation library
4 * (C) Copyright yohhoy 2012.
5 * Distributed under the Boost Software License, Version 1.0.
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:
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.
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.
29 #ifndef assert
30 #include <assert.h>
31 #endif
32 #include <limits.h>
33 #include <errno.h>
34 #include <process.h> // MSVCRT
37 Configuration macro:
39 EMULATED_THREADS_USE_NATIVE_CALL_ONCE
40 Use native WindowsAPI one-time initialization function.
41 (requires WinVista or later)
42 Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
44 EMULATED_THREADS_USE_NATIVE_CV
45 Use native WindowsAPI condition variable object.
46 (requires WinVista or later)
47 Otherwise use emulated implementation for WinXP.
49 EMULATED_THREADS_TSS_DTOR_SLOTNUM
50 Max registerable TSS dtor number.
53 // XXX: Retain XP compatability
54 #if 0
55 #if _WIN32_WINNT >= 0x0600
56 // Prefer native WindowsAPI on newer environment.
57 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
58 #define EMULATED_THREADS_USE_NATIVE_CV
59 #endif
60 #endif
61 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
63 #include "threads.h"
67 Implementation limits:
68 - Conditionally emulation for "Initialization functions"
69 (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
70 - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
72 static void impl_tss_dtor_invoke(void); // forward decl.
74 struct impl_thrd_param {
75 thrd_start_t func;
76 void *arg;
79 static unsigned __stdcall impl_thrd_routine(void *p)
81 struct impl_thrd_param pack;
82 int code;
83 memcpy(&pack, p, sizeof(struct impl_thrd_param));
84 free(p);
85 code = pack.func(pack.arg);
86 impl_tss_dtor_invoke();
87 return (unsigned)code;
90 static DWORD impl_xtime2msec(const xtime *xt)
92 return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L));
95 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
96 struct impl_call_once_param { void (*func)(void); };
97 static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
99 struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
100 (param->func)();
101 ((void)InitOnce); ((void)Context); // suppress warning
102 return TRUE;
104 #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
106 #ifndef EMULATED_THREADS_USE_NATIVE_CV
108 Note:
109 The implementation of condition variable is ported from Boost.Interprocess
110 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
112 static void impl_cond_do_signal(cnd_t *cond, int broadcast)
114 int nsignal = 0;
116 EnterCriticalSection(&cond->monitor);
117 if (cond->to_unblock != 0) {
118 if (cond->blocked == 0) {
119 LeaveCriticalSection(&cond->monitor);
120 return;
122 if (broadcast) {
123 cond->to_unblock += nsignal = cond->blocked;
124 cond->blocked = 0;
125 } else {
126 nsignal = 1;
127 cond->to_unblock++;
128 cond->blocked--;
130 } else if (cond->blocked > cond->gone) {
131 WaitForSingleObject(cond->sem_gate, INFINITE);
132 if (cond->gone != 0) {
133 cond->blocked -= cond->gone;
134 cond->gone = 0;
136 if (broadcast) {
137 nsignal = cond->to_unblock = cond->blocked;
138 cond->blocked = 0;
139 } else {
140 nsignal = cond->to_unblock = 1;
141 cond->blocked--;
144 LeaveCriticalSection(&cond->monitor);
146 if (0 < nsignal)
147 ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
150 static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
152 int nleft = 0;
153 int ngone = 0;
154 int timeout = 0;
155 DWORD w;
157 WaitForSingleObject(cond->sem_gate, INFINITE);
158 cond->blocked++;
159 ReleaseSemaphore(cond->sem_gate, 1, NULL);
161 mtx_unlock(mtx);
163 w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE);
164 timeout = (w == WAIT_TIMEOUT);
166 EnterCriticalSection(&cond->monitor);
167 if ((nleft = cond->to_unblock) != 0) {
168 if (timeout) {
169 if (cond->blocked != 0) {
170 cond->blocked--;
171 } else {
172 cond->gone++;
175 if (--cond->to_unblock == 0) {
176 if (cond->blocked != 0) {
177 ReleaseSemaphore(cond->sem_gate, 1, NULL);
178 nleft = 0;
180 else if ((ngone = cond->gone) != 0) {
181 cond->gone = 0;
184 } else if (++cond->gone == INT_MAX/2) {
185 WaitForSingleObject(cond->sem_gate, INFINITE);
186 cond->blocked -= cond->gone;
187 ReleaseSemaphore(cond->sem_gate, 1, NULL);
188 cond->gone = 0;
190 LeaveCriticalSection(&cond->monitor);
192 if (nleft == 1) {
193 while (ngone--)
194 WaitForSingleObject(cond->sem_queue, INFINITE);
195 ReleaseSemaphore(cond->sem_gate, 1, NULL);
198 mtx_lock(mtx);
199 return timeout ? thrd_busy : thrd_success;
201 #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
203 static struct impl_tss_dtor_entry {
204 tss_t key;
205 tss_dtor_t dtor;
206 } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
208 static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
210 int i;
211 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
212 if (!impl_tss_dtor_tbl[i].dtor)
213 break;
215 if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
216 return 1;
217 impl_tss_dtor_tbl[i].key = key;
218 impl_tss_dtor_tbl[i].dtor = dtor;
219 return 0;
222 static void impl_tss_dtor_invoke()
224 int i;
225 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
226 if (impl_tss_dtor_tbl[i].dtor) {
227 void* val = tss_get(impl_tss_dtor_tbl[i].key);
228 if (val)
229 (impl_tss_dtor_tbl[i].dtor)(val);
235 /*--------------- 7.25.2 Initialization functions ---------------*/
236 // 7.25.2.1
237 void call_once(once_flag *flag, void (*func)(void))
239 assert(flag && func);
240 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
242 struct impl_call_once_param param;
243 param.func = func;
244 InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)&param, NULL);
246 #else
247 if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
248 (func)();
249 InterlockedExchange(&flag->status, 2);
250 } else {
251 while (flag->status == 1) {
252 // busy loop!
253 thrd_yield();
256 #endif
260 /*------------- 7.25.3 Condition variable functions -------------*/
261 // 7.25.3.1
262 int cnd_broadcast(cnd_t *cond)
264 if (!cond) return thrd_error;
265 #ifdef EMULATED_THREADS_USE_NATIVE_CV
266 WakeAllConditionVariable(&cond->condvar);
267 #else
268 impl_cond_do_signal(cond, 1);
269 #endif
270 return thrd_success;
273 // 7.25.3.2
274 void cnd_destroy(cnd_t *cond)
276 assert(cond);
277 #ifdef EMULATED_THREADS_USE_NATIVE_CV
278 // do nothing
279 #else
280 CloseHandle(cond->sem_queue);
281 CloseHandle(cond->sem_gate);
282 DeleteCriticalSection(&cond->monitor);
283 #endif
286 // 7.25.3.3
287 int cnd_init(cnd_t *cond)
289 if (!cond) return thrd_error;
290 #ifdef EMULATED_THREADS_USE_NATIVE_CV
291 InitializeConditionVariable(&cond->condvar);
292 #else
293 cond->blocked = 0;
294 cond->gone = 0;
295 cond->to_unblock = 0;
296 cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
297 cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
298 InitializeCriticalSection(&cond->monitor);
299 #endif
300 return thrd_success;
303 // 7.25.3.4
304 int cnd_signal(cnd_t *cond)
306 if (!cond) return thrd_error;
307 #ifdef EMULATED_THREADS_USE_NATIVE_CV
308 WakeConditionVariable(&cond->condvar);
309 #else
310 impl_cond_do_signal(cond, 0);
311 #endif
312 return thrd_success;
315 // 7.25.3.5
316 int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
318 if (!cond || !mtx || !xt) return thrd_error;
319 #ifdef EMULATED_THREADS_USE_NATIVE_CV
320 if (SleepConditionVariableCS(&cond->condvar, &mtx->cs, impl_xtime2msec(xt)))
321 return thrd_success;
322 return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
323 #else
324 return impl_cond_do_wait(cond, mtx, xt);
325 #endif
328 // 7.25.3.6
329 int cnd_wait(cnd_t *cond, mtx_t *mtx)
331 if (!cond || !mtx) return thrd_error;
332 #ifdef EMULATED_THREADS_USE_NATIVE_CV
333 SleepConditionVariableCS(&cond->condvar, &mtx->cs, INFINITE);
334 #else
335 impl_cond_do_wait(cond, mtx, NULL);
336 #endif
337 return thrd_success;
341 /*-------------------- 7.25.4 Mutex functions --------------------*/
342 // 7.25.4.1
343 void mtx_destroy(mtx_t *mtx)
345 assert(mtx);
346 DeleteCriticalSection(&mtx->cs);
349 // 7.25.4.2
350 int mtx_init(mtx_t *mtx, int type)
352 if (!mtx) return thrd_error;
353 if (type != mtx_plain && type != mtx_timed && type != mtx_try
354 && type != (mtx_plain|mtx_recursive)
355 && type != (mtx_timed|mtx_recursive)
356 && type != (mtx_try|mtx_recursive))
357 return thrd_error;
358 InitializeCriticalSection(&mtx->cs);
359 return thrd_success;
362 // 7.25.4.3
363 int mtx_lock(mtx_t *mtx)
365 if (!mtx) return thrd_error;
366 EnterCriticalSection(&mtx->cs);
367 return thrd_success;
370 // 7.25.4.4
371 int mtx_timedlock(mtx_t *mtx, const xtime *xt)
373 time_t expire, now;
374 if (!mtx || !xt) return thrd_error;
375 expire = time(NULL);
376 expire += xt->sec;
377 while (mtx_trylock(mtx) != thrd_success) {
378 now = time(NULL);
379 if (expire < now)
380 return thrd_busy;
381 // busy loop!
382 thrd_yield();
384 return thrd_success;
387 // 7.25.4.5
388 int mtx_trylock(mtx_t *mtx)
390 if (!mtx) return thrd_error;
391 return TryEnterCriticalSection(&mtx->cs) ? thrd_success : thrd_busy;
394 // 7.25.4.6
395 int mtx_unlock(mtx_t *mtx)
397 if (!mtx) return thrd_error;
398 LeaveCriticalSection(&mtx->cs);
399 return thrd_success;
403 /*------------------- 7.25.5 Thread functions -------------------*/
404 // 7.25.5.1
405 int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
407 struct impl_thrd_param *pack;
408 uintptr_t handle;
409 if (!thr) return thrd_error;
410 pack = malloc(sizeof(struct impl_thrd_param));
411 if (!pack) return thrd_nomem;
412 pack->func = func;
413 pack->arg = arg;
414 handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
415 if (handle == 0) {
416 if (errno == EAGAIN || errno == EACCES)
417 return thrd_nomem;
418 return thrd_error;
420 *thr = (thrd_t)handle;
421 return thrd_success;
424 // 7.25.5.2
425 thrd_t thrd_current(void)
427 return GetCurrentThread();
430 // 7.25.5.3
431 int thrd_detach(thrd_t thr)
433 CloseHandle(thr);
434 return thrd_success;
437 // 7.25.5.4
438 int thrd_equal(thrd_t thr0, thrd_t thr1)
440 return (thr0 == thr1);
443 // 7.25.5.5
444 void thrd_exit(int res)
446 impl_tss_dtor_invoke();
447 _endthreadex((unsigned)res);
450 // 7.25.5.6
451 int thrd_join(thrd_t thr, int *res)
453 DWORD w, code;
454 w = WaitForSingleObject(thr, INFINITE);
455 if (w != WAIT_OBJECT_0)
456 return thrd_error;
457 if (res) {
458 if (!GetExitCodeThread(thr, &code)) {
459 CloseHandle(thr);
460 return thrd_error;
462 *res = (int)code;
464 CloseHandle(thr);
465 return thrd_success;
468 // 7.25.5.7
469 void thrd_sleep(const xtime *xt)
471 assert(xt);
472 Sleep(impl_xtime2msec(xt));
475 // 7.25.5.8
476 void thrd_yield(void)
478 SwitchToThread();
482 /*----------- 7.25.6 Thread-specific storage functions -----------*/
483 // 7.25.6.1
484 int tss_create(tss_t *key, tss_dtor_t dtor)
486 if (!key) return thrd_error;
487 *key = TlsAlloc();
488 if (dtor) {
489 if (impl_tss_dtor_register(*key, dtor)) {
490 TlsFree(*key);
491 return thrd_error;
494 return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
497 // 7.25.6.2
498 void tss_delete(tss_t key)
500 TlsFree(key);
503 // 7.25.6.3
504 void *tss_get(tss_t key)
506 return TlsGetValue(key);
509 // 7.25.6.4
510 int tss_set(tss_t key, void *val)
512 return TlsSetValue(key, val) ? thrd_success : thrd_error;
516 /*-------------------- 7.25.7 Time functions --------------------*/
517 // 7.25.6.1
518 int xtime_get(xtime *xt, int base)
520 if (!xt) return 0;
521 if (base == TIME_UTC) {
522 xt->sec = time(NULL);
523 xt->nsec = 0;
524 return base;
526 return 0;