Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/include/c11/threads_win32.h
4550 views
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_TSS_DTOR_SLOTNUM
46
Max registerable TSS dtor number.
47
*/
48
49
#if _WIN32_WINNT >= 0x0600
50
// Prefer native WindowsAPI on newer environment.
51
#if !defined(__MINGW32__)
52
#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
53
#endif
54
#endif
55
#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
56
57
58
#include <windows.h>
59
60
// check configuration
61
#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)
62
#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
63
#endif
64
65
/* Visual Studio 2015 and later */
66
#ifdef _MSC_VER
67
#define HAVE_TIMESPEC_GET
68
#endif
69
70
/*---------------------------- macros ----------------------------*/
71
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
72
#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
73
#else
74
#define ONCE_FLAG_INIT {0}
75
#endif
76
#define TSS_DTOR_ITERATIONS 1
77
78
// FIXME: temporary non-standard hack to ease transition
79
#define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
80
81
/*---------------------------- types ----------------------------*/
82
typedef CONDITION_VARIABLE cnd_t;
83
84
typedef HANDLE thrd_t;
85
86
typedef DWORD tss_t;
87
88
typedef CRITICAL_SECTION mtx_t;
89
90
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
91
typedef INIT_ONCE once_flag;
92
#else
93
typedef struct once_flag_t {
94
volatile LONG status;
95
} once_flag;
96
#endif
97
98
99
static inline void * tss_get(tss_t key);
100
static inline void thrd_yield(void);
101
static inline int mtx_trylock(mtx_t *mtx);
102
static inline int mtx_lock(mtx_t *mtx);
103
static inline int mtx_unlock(mtx_t *mtx);
104
105
/*
106
Implementation limits:
107
- Conditionally emulation for "Initialization functions"
108
(see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
109
- Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
110
*/
111
static void impl_tss_dtor_invoke(void); // forward decl.
112
113
struct impl_thrd_param {
114
thrd_start_t func;
115
void *arg;
116
};
117
118
static unsigned __stdcall impl_thrd_routine(void *p)
119
{
120
struct impl_thrd_param pack;
121
int code;
122
memcpy(&pack, p, sizeof(struct impl_thrd_param));
123
free(p);
124
code = pack.func(pack.arg);
125
impl_tss_dtor_invoke();
126
return (unsigned)code;
127
}
128
129
static time_t impl_timespec2msec(const struct timespec *ts)
130
{
131
return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);
132
}
133
134
#ifdef HAVE_TIMESPEC_GET
135
static DWORD impl_abs2relmsec(const struct timespec *abs_time)
136
{
137
const time_t abs_ms = impl_timespec2msec(abs_time);
138
struct timespec now;
139
timespec_get(&now, TIME_UTC);
140
const time_t now_ms = impl_timespec2msec(&now);
141
const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD)(abs_ms - now_ms) : 0;
142
return rel_ms;
143
}
144
#endif
145
146
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
147
struct impl_call_once_param { void (*func)(void); };
148
static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
149
{
150
struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
151
(param->func)();
152
((void)InitOnce); ((void)Context); // suppress warning
153
return TRUE;
154
}
155
#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
156
157
static struct impl_tss_dtor_entry {
158
tss_t key;
159
tss_dtor_t dtor;
160
} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
161
162
static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
163
{
164
int i;
165
for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
166
if (!impl_tss_dtor_tbl[i].dtor)
167
break;
168
}
169
if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
170
return 1;
171
impl_tss_dtor_tbl[i].key = key;
172
impl_tss_dtor_tbl[i].dtor = dtor;
173
return 0;
174
}
175
176
static void impl_tss_dtor_invoke()
177
{
178
int i;
179
for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
180
if (impl_tss_dtor_tbl[i].dtor) {
181
void* val = tss_get(impl_tss_dtor_tbl[i].key);
182
if (val)
183
(impl_tss_dtor_tbl[i].dtor)(val);
184
}
185
}
186
}
187
188
189
/*--------------- 7.25.2 Initialization functions ---------------*/
190
// 7.25.2.1
191
static inline void
192
call_once(once_flag *flag, void (*func)(void))
193
{
194
assert(flag && func);
195
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
196
{
197
struct impl_call_once_param param;
198
param.func = func;
199
InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)&param, NULL);
200
}
201
#else
202
if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
203
(func)();
204
InterlockedExchange(&flag->status, 2);
205
} else {
206
while (flag->status == 1) {
207
// busy loop!
208
thrd_yield();
209
}
210
}
211
#endif
212
}
213
214
215
/*------------- 7.25.3 Condition variable functions -------------*/
216
// 7.25.3.1
217
static inline int
218
cnd_broadcast(cnd_t *cond)
219
{
220
assert(cond != NULL);
221
WakeAllConditionVariable(cond);
222
return thrd_success;
223
}
224
225
// 7.25.3.2
226
static inline void
227
cnd_destroy(cnd_t *cond)
228
{
229
assert(cond != NULL);
230
// do nothing
231
}
232
233
// 7.25.3.3
234
static inline int
235
cnd_init(cnd_t *cond)
236
{
237
assert(cond != NULL);
238
InitializeConditionVariable(cond);
239
return thrd_success;
240
}
241
242
// 7.25.3.4
243
static inline int
244
cnd_signal(cnd_t *cond)
245
{
246
assert(cond != NULL);
247
WakeConditionVariable(cond);
248
return thrd_success;
249
}
250
251
// 7.25.3.5
252
static inline int
253
cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
254
{
255
assert(cond != NULL);
256
assert(mtx != NULL);
257
assert(abs_time != NULL);
258
#ifdef HAVE_TIMESPEC_GET
259
const DWORD timeout = impl_abs2relmsec(abs_time);
260
if (SleepConditionVariableCS(cond, mtx, timeout))
261
return thrd_success;
262
return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
263
#else
264
return thrd_error;
265
#endif
266
}
267
268
// 7.25.3.6
269
static inline int
270
cnd_wait(cnd_t *cond, mtx_t *mtx)
271
{
272
assert(cond != NULL);
273
assert(mtx != NULL);
274
SleepConditionVariableCS(cond, mtx, INFINITE);
275
return thrd_success;
276
}
277
278
279
/*-------------------- 7.25.4 Mutex functions --------------------*/
280
// 7.25.4.1
281
static inline void
282
mtx_destroy(mtx_t *mtx)
283
{
284
assert(mtx);
285
DeleteCriticalSection(mtx);
286
}
287
288
// 7.25.4.2
289
static inline int
290
mtx_init(mtx_t *mtx, int type)
291
{
292
assert(mtx != NULL);
293
if (type != mtx_plain && type != mtx_timed && type != mtx_try
294
&& type != (mtx_plain|mtx_recursive)
295
&& type != (mtx_timed|mtx_recursive)
296
&& type != (mtx_try|mtx_recursive))
297
return thrd_error;
298
InitializeCriticalSection(mtx);
299
return thrd_success;
300
}
301
302
// 7.25.4.3
303
static inline int
304
mtx_lock(mtx_t *mtx)
305
{
306
assert(mtx != NULL);
307
EnterCriticalSection(mtx);
308
return thrd_success;
309
}
310
311
// 7.25.4.4
312
static inline int
313
mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
314
{
315
assert(mtx != NULL);
316
assert(ts != NULL);
317
#ifdef HAVE_TIMESPEC_GET
318
while (mtx_trylock(mtx) != thrd_success) {
319
if (impl_abs2relmsec(ts) == 0)
320
return thrd_busy;
321
// busy loop!
322
thrd_yield();
323
}
324
return thrd_success;
325
#else
326
return thrd_error;
327
#endif
328
}
329
330
// 7.25.4.5
331
static inline int
332
mtx_trylock(mtx_t *mtx)
333
{
334
assert(mtx != NULL);
335
return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
336
}
337
338
// 7.25.4.6
339
static inline int
340
mtx_unlock(mtx_t *mtx)
341
{
342
assert(mtx != NULL);
343
LeaveCriticalSection(mtx);
344
return thrd_success;
345
}
346
347
348
/*------------------- 7.25.5 Thread functions -------------------*/
349
// 7.25.5.1
350
static inline int
351
thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
352
{
353
struct impl_thrd_param *pack;
354
uintptr_t handle;
355
assert(thr != NULL);
356
pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
357
if (!pack) return thrd_nomem;
358
pack->func = func;
359
pack->arg = arg;
360
handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
361
if (handle == 0) {
362
if (errno == EAGAIN || errno == EACCES)
363
return thrd_nomem;
364
return thrd_error;
365
}
366
*thr = (thrd_t)handle;
367
return thrd_success;
368
}
369
370
#if 0
371
// 7.25.5.2
372
static inline thrd_t
373
thrd_current(void)
374
{
375
HANDLE hCurrentThread;
376
BOOL bRet;
377
378
/* GetCurrentThread() returns a pseudo-handle, which we need
379
* to pass to DuplicateHandle(). Only the resulting handle can be used
380
* from other threads.
381
*
382
* Note that neither handle can be compared to the one by thread_create.
383
* Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
384
* can be compared directly.
385
*
386
* Other potential solutions would be:
387
* - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
388
* - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
389
*
390
* Neither is particularly nice.
391
*
392
* Life would be much easier if C11 threads had different abstractions for
393
* threads and thread IDs, just like C++11 threads does...
394
*/
395
396
bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
397
GetCurrentThread(), // source (pseudo) handle
398
GetCurrentProcess(), // target process
399
&hCurrentThread, // target handle
400
0,
401
FALSE,
402
DUPLICATE_SAME_ACCESS);
403
assert(bRet);
404
if (!bRet) {
405
hCurrentThread = GetCurrentThread();
406
}
407
return hCurrentThread;
408
}
409
#endif
410
411
// 7.25.5.3
412
static inline int
413
thrd_detach(thrd_t thr)
414
{
415
CloseHandle(thr);
416
return thrd_success;
417
}
418
419
// 7.25.5.4
420
static inline int
421
thrd_equal(thrd_t thr0, thrd_t thr1)
422
{
423
return GetThreadId(thr0) == GetThreadId(thr1);
424
}
425
426
// 7.25.5.5
427
static inline void
428
thrd_exit(int res)
429
{
430
impl_tss_dtor_invoke();
431
_endthreadex((unsigned)res);
432
}
433
434
// 7.25.5.6
435
static inline int
436
thrd_join(thrd_t thr, int *res)
437
{
438
DWORD w, code;
439
w = WaitForSingleObject(thr, INFINITE);
440
if (w != WAIT_OBJECT_0)
441
return thrd_error;
442
if (res) {
443
if (!GetExitCodeThread(thr, &code)) {
444
CloseHandle(thr);
445
return thrd_error;
446
}
447
*res = (int)code;
448
}
449
CloseHandle(thr);
450
return thrd_success;
451
}
452
453
// 7.25.5.7
454
static inline void
455
thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
456
{
457
assert(time_point);
458
assert(!remaining); /* not implemented */
459
Sleep((DWORD)impl_timespec2msec(time_point));
460
}
461
462
// 7.25.5.8
463
static inline void
464
thrd_yield(void)
465
{
466
SwitchToThread();
467
}
468
469
470
/*----------- 7.25.6 Thread-specific storage functions -----------*/
471
// 7.25.6.1
472
static inline int
473
tss_create(tss_t *key, tss_dtor_t dtor)
474
{
475
assert(key != NULL);
476
*key = TlsAlloc();
477
if (dtor) {
478
if (impl_tss_dtor_register(*key, dtor)) {
479
TlsFree(*key);
480
return thrd_error;
481
}
482
}
483
return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
484
}
485
486
// 7.25.6.2
487
static inline void
488
tss_delete(tss_t key)
489
{
490
TlsFree(key);
491
}
492
493
// 7.25.6.3
494
static inline void *
495
tss_get(tss_t key)
496
{
497
return TlsGetValue(key);
498
}
499
500
// 7.25.6.4
501
static inline int
502
tss_set(tss_t key, void *val)
503
{
504
return TlsSetValue(key, val) ? thrd_success : thrd_error;
505
}
506
507
508
/*-------------------- 7.25.7 Time functions --------------------*/
509
// 7.25.6.1
510
#ifndef HAVE_TIMESPEC_GET
511
static inline int
512
timespec_get(struct timespec *ts, int base)
513
{
514
assert(ts != NULL);
515
if (base == TIME_UTC) {
516
ts->tv_sec = time(NULL);
517
ts->tv_nsec = 0;
518
return base;
519
}
520
return 0;
521
}
522
#endif
523
524