~mmach/netext73/mesa-haswell

« back to all changes in this revision

Viewing changes to include/c11/threads_win32.h

  • Committer: mmach
  • Date: 2022-09-22 19:56:13 UTC
  • Revision ID: netbit73@gmail.com-20220922195613-wtik9mmy20tmor0i
2022-09-22 21:17:09

Show diffs side-by-side

added added

removed removed

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