Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmliblzma/common/mythread.h
3153 views
1
// SPDX-License-Identifier: 0BSD
2
3
///////////////////////////////////////////////////////////////////////////////
4
//
5
/// \file mythread.h
6
/// \brief Some threading related helper macros and functions
7
//
8
// Author: Lasse Collin
9
//
10
///////////////////////////////////////////////////////////////////////////////
11
12
#ifndef MYTHREAD_H
13
#define MYTHREAD_H
14
15
#include "sysdefs.h"
16
17
// If any type of threading is enabled, #define MYTHREAD_ENABLED.
18
#if defined(MYTHREAD_POSIX) || defined(MYTHREAD_WIN95) \
19
|| defined(MYTHREAD_VISTA)
20
# define MYTHREAD_ENABLED 1
21
#endif
22
23
24
#ifdef MYTHREAD_ENABLED
25
26
////////////////////////////////////////
27
// Shared between all threading types //
28
////////////////////////////////////////
29
30
// Locks a mutex for a duration of a block.
31
//
32
// Perform mythread_mutex_lock(&mutex) in the beginning of a block
33
// and mythread_mutex_unlock(&mutex) at the end of the block. "break"
34
// may be used to unlock the mutex and jump out of the block.
35
// mythread_sync blocks may be nested.
36
//
37
// Example:
38
//
39
// mythread_sync(mutex) {
40
// foo();
41
// if (some_error)
42
// break; // Skips bar()
43
// bar();
44
// }
45
//
46
// At least GCC optimizes the loops completely away so it doesn't slow
47
// things down at all compared to plain mythread_mutex_lock(&mutex)
48
// and mythread_mutex_unlock(&mutex) calls.
49
//
50
#define mythread_sync(mutex) mythread_sync_helper1(mutex, __LINE__)
51
#define mythread_sync_helper1(mutex, line) mythread_sync_helper2(mutex, line)
52
#define mythread_sync_helper2(mutex, line) \
53
for (unsigned int mythread_i_ ## line = 0; \
54
mythread_i_ ## line \
55
? (mythread_mutex_unlock(&(mutex)), 0) \
56
: (mythread_mutex_lock(&(mutex)), 1); \
57
mythread_i_ ## line = 1) \
58
for (unsigned int mythread_j_ ## line = 0; \
59
!mythread_j_ ## line; \
60
mythread_j_ ## line = 1)
61
#endif
62
63
64
#if !defined(MYTHREAD_ENABLED)
65
66
//////////////////
67
// No threading //
68
//////////////////
69
70
// Calls the given function once. This isn't thread safe.
71
#define mythread_once(func) \
72
do { \
73
static bool once_ = false; \
74
if (!once_) { \
75
func(); \
76
once_ = true; \
77
} \
78
} while (0)
79
80
81
#if !(defined(_WIN32) && !defined(__CYGWIN__)) && !defined(__wasm__)
82
// Use sigprocmask() to set the signal mask in single-threaded programs.
83
#include <signal.h>
84
85
static inline void
86
mythread_sigmask(int how, const sigset_t *restrict set,
87
sigset_t *restrict oset)
88
{
89
int ret = sigprocmask(how, set, oset);
90
assert(ret == 0);
91
(void)ret;
92
}
93
#endif
94
95
96
#elif defined(MYTHREAD_POSIX)
97
98
////////////////////
99
// Using pthreads //
100
////////////////////
101
102
#include <pthread.h>
103
#include <signal.h>
104
#include <time.h>
105
#include <errno.h>
106
107
// If clock_gettime() isn't available, use gettimeofday() from <sys/time.h>
108
// as a fallback. gettimeofday() is in SUSv2 and thus is supported on all
109
// relevant POSIX systems.
110
#ifndef HAVE_CLOCK_GETTIME
111
# include <sys/time.h>
112
#endif
113
114
// MinGW-w64 with winpthreads:
115
//
116
// NOTE: Typical builds with MinGW-w64 don't use this code (MYTHREAD_POSIX).
117
// Instead, native Windows threading APIs are used (MYTHREAD_VISTA or
118
// MYTHREAD_WIN95).
119
//
120
// MinGW-w64 has _sigset_t (an integer type) in <sys/types.h>.
121
// If _POSIX was #defined, the header would add the alias sigset_t too.
122
// Let's keep this working even without _POSIX.
123
//
124
// There are no functions that actually do something with sigset_t
125
// because signals barely exist on Windows. The sigfillset macro below
126
// is just to silence warnings. There is no sigfillset() in MinGW-w64.
127
#ifdef __MINGW32__
128
# include <sys/types.h>
129
# define sigset_t _sigset_t
130
# define sigfillset(set_ptr) do { *(set_ptr) = 0; } while (0)
131
#endif
132
133
#define MYTHREAD_RET_TYPE void *
134
#define MYTHREAD_RET_VALUE NULL
135
136
typedef pthread_t mythread;
137
typedef pthread_mutex_t mythread_mutex;
138
139
typedef struct {
140
pthread_cond_t cond;
141
#ifdef HAVE_CLOCK_GETTIME
142
// Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
143
// the condition variable.
144
clockid_t clk_id;
145
#endif
146
} mythread_cond;
147
148
typedef struct timespec mythread_condtime;
149
150
151
// Calls the given function once in a thread-safe way.
152
#define mythread_once(func) \
153
do { \
154
static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
155
pthread_once(&once_, &func); \
156
} while (0)
157
158
159
// Use pthread_sigmask() to set the signal mask in multi-threaded programs.
160
// Do nothing on OpenVMS since it lacks pthread_sigmask().
161
// Do nothing on MinGW-w64 too to silence warnings (its pthread_sigmask()
162
// is #defined to 0 so it's a no-op).
163
static inline void
164
mythread_sigmask(int how, const sigset_t *restrict set,
165
sigset_t *restrict oset)
166
{
167
#if defined(__VMS) || defined(__MINGW32__)
168
(void)how;
169
(void)set;
170
(void)oset;
171
#else
172
int ret = pthread_sigmask(how, set, oset);
173
assert(ret == 0);
174
(void)ret;
175
#endif
176
}
177
178
179
// Creates a new thread with all signals blocked. Returns zero on success
180
// and non-zero on error.
181
static inline int
182
mythread_create(mythread *thread, void *(*func)(void *arg), void *arg)
183
{
184
sigset_t old;
185
sigset_t all;
186
sigfillset(&all);
187
188
mythread_sigmask(SIG_SETMASK, &all, &old);
189
const int ret = pthread_create(thread, NULL, func, arg);
190
mythread_sigmask(SIG_SETMASK, &old, NULL);
191
192
return ret;
193
}
194
195
// Joins a thread. Returns zero on success and non-zero on error.
196
static inline int
197
mythread_join(mythread thread)
198
{
199
return pthread_join(thread, NULL);
200
}
201
202
203
// Initializes a mutex. Returns zero on success and non-zero on error.
204
static inline int
205
mythread_mutex_init(mythread_mutex *mutex)
206
{
207
return pthread_mutex_init(mutex, NULL);
208
}
209
210
static inline void
211
mythread_mutex_destroy(mythread_mutex *mutex)
212
{
213
int ret = pthread_mutex_destroy(mutex);
214
assert(ret == 0);
215
(void)ret;
216
}
217
218
static inline void
219
mythread_mutex_lock(mythread_mutex *mutex)
220
{
221
int ret = pthread_mutex_lock(mutex);
222
assert(ret == 0);
223
(void)ret;
224
}
225
226
static inline void
227
mythread_mutex_unlock(mythread_mutex *mutex)
228
{
229
int ret = pthread_mutex_unlock(mutex);
230
assert(ret == 0);
231
(void)ret;
232
}
233
234
235
// Initializes a condition variable.
236
//
237
// Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the
238
// timeout in pthread_cond_timedwait() work correctly also if system time
239
// is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available
240
// everywhere while the default CLOCK_REALTIME is, so the default is
241
// used if CLOCK_MONOTONIC isn't available.
242
//
243
// If clock_gettime() isn't available at all, gettimeofday() will be used.
244
static inline int
245
mythread_cond_init(mythread_cond *mycond)
246
{
247
#ifdef HAVE_CLOCK_GETTIME
248
# if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
249
defined(HAVE_CLOCK_MONOTONIC)
250
struct timespec ts;
251
pthread_condattr_t condattr;
252
253
// POSIX doesn't seem to *require* that pthread_condattr_setclock()
254
// will fail if given an unsupported clock ID. Test that
255
// CLOCK_MONOTONIC really is supported using clock_gettime().
256
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0
257
&& pthread_condattr_init(&condattr) == 0) {
258
int ret = pthread_condattr_setclock(
259
&condattr, CLOCK_MONOTONIC);
260
if (ret == 0)
261
ret = pthread_cond_init(&mycond->cond, &condattr);
262
263
pthread_condattr_destroy(&condattr);
264
265
if (ret == 0) {
266
mycond->clk_id = CLOCK_MONOTONIC;
267
return 0;
268
}
269
}
270
271
// If anything above fails, fall back to the default CLOCK_REALTIME.
272
// POSIX requires that all implementations of clock_gettime() must
273
// support at least CLOCK_REALTIME.
274
# endif
275
276
mycond->clk_id = CLOCK_REALTIME;
277
#endif
278
279
return pthread_cond_init(&mycond->cond, NULL);
280
}
281
282
static inline void
283
mythread_cond_destroy(mythread_cond *cond)
284
{
285
int ret = pthread_cond_destroy(&cond->cond);
286
assert(ret == 0);
287
(void)ret;
288
}
289
290
static inline void
291
mythread_cond_signal(mythread_cond *cond)
292
{
293
int ret = pthread_cond_signal(&cond->cond);
294
assert(ret == 0);
295
(void)ret;
296
}
297
298
static inline void
299
mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
300
{
301
int ret = pthread_cond_wait(&cond->cond, mutex);
302
assert(ret == 0);
303
(void)ret;
304
}
305
306
// Waits on a condition or until a timeout expires. If the timeout expires,
307
// non-zero is returned, otherwise zero is returned.
308
static inline int
309
mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
310
const mythread_condtime *condtime)
311
{
312
int ret = pthread_cond_timedwait(&cond->cond, mutex, condtime);
313
assert(ret == 0 || ret == ETIMEDOUT);
314
return ret;
315
}
316
317
// Sets condtime to the absolute time that is timeout_ms milliseconds
318
// in the future. The type of the clock to use is taken from cond.
319
static inline void
320
mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
321
uint32_t timeout_ms)
322
{
323
condtime->tv_sec = (time_t)(timeout_ms / 1000);
324
condtime->tv_nsec = (long)((timeout_ms % 1000) * 1000000);
325
326
#ifdef HAVE_CLOCK_GETTIME
327
struct timespec now;
328
int ret = clock_gettime(cond->clk_id, &now);
329
assert(ret == 0);
330
(void)ret;
331
332
condtime->tv_sec += now.tv_sec;
333
condtime->tv_nsec += now.tv_nsec;
334
#else
335
(void)cond;
336
337
struct timeval now;
338
gettimeofday(&now, NULL);
339
340
condtime->tv_sec += now.tv_sec;
341
condtime->tv_nsec += now.tv_usec * 1000L;
342
#endif
343
344
// tv_nsec must stay in the range [0, 999_999_999].
345
if (condtime->tv_nsec >= 1000000000L) {
346
condtime->tv_nsec -= 1000000000L;
347
++condtime->tv_sec;
348
}
349
}
350
351
352
#elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA)
353
354
/////////////////////
355
// Windows threads //
356
/////////////////////
357
358
#define WIN32_LEAN_AND_MEAN
359
#ifdef MYTHREAD_VISTA
360
# undef _WIN32_WINNT
361
# define _WIN32_WINNT 0x0600
362
#endif
363
#include <windows.h>
364
#include <process.h>
365
366
#define MYTHREAD_RET_TYPE unsigned int __stdcall
367
#define MYTHREAD_RET_VALUE 0
368
369
typedef HANDLE mythread;
370
typedef CRITICAL_SECTION mythread_mutex;
371
372
#ifdef MYTHREAD_WIN95
373
typedef HANDLE mythread_cond;
374
#else
375
typedef CONDITION_VARIABLE mythread_cond;
376
#endif
377
378
typedef struct {
379
// Tick count (milliseconds) in the beginning of the timeout.
380
// NOTE: This is 32 bits so it wraps around after 49.7 days.
381
// Multi-day timeouts may not work as expected.
382
DWORD start;
383
384
// Length of the timeout in milliseconds. The timeout expires
385
// when the current tick count minus "start" is equal or greater
386
// than "timeout".
387
DWORD timeout;
388
} mythread_condtime;
389
390
391
// mythread_once() is only available with Vista threads.
392
#ifdef MYTHREAD_VISTA
393
#define mythread_once(func) \
394
do { \
395
static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \
396
BOOL pending_; \
397
if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \
398
abort(); \
399
if (pending_) { \
400
func(); \
401
if (!InitOnceComplete(&once_, 0, NULL)) \
402
abort(); \
403
} \
404
} while (0)
405
#endif
406
407
408
// mythread_sigmask() isn't available on Windows. Even a dummy version would
409
// make no sense because the other POSIX signal functions are missing anyway.
410
411
412
static inline int
413
mythread_create(mythread *thread,
414
unsigned int (__stdcall *func)(void *arg), void *arg)
415
{
416
uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL);
417
if (ret == 0)
418
return -1;
419
420
*thread = (HANDLE)ret;
421
return 0;
422
}
423
424
static inline int
425
mythread_join(mythread thread)
426
{
427
int ret = 0;
428
429
if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
430
ret = -1;
431
432
if (!CloseHandle(thread))
433
ret = -1;
434
435
return ret;
436
}
437
438
439
static inline int
440
mythread_mutex_init(mythread_mutex *mutex)
441
{
442
InitializeCriticalSection(mutex);
443
return 0;
444
}
445
446
static inline void
447
mythread_mutex_destroy(mythread_mutex *mutex)
448
{
449
DeleteCriticalSection(mutex);
450
}
451
452
static inline void
453
mythread_mutex_lock(mythread_mutex *mutex)
454
{
455
EnterCriticalSection(mutex);
456
}
457
458
static inline void
459
mythread_mutex_unlock(mythread_mutex *mutex)
460
{
461
LeaveCriticalSection(mutex);
462
}
463
464
465
static inline int
466
mythread_cond_init(mythread_cond *cond)
467
{
468
#ifdef MYTHREAD_WIN95
469
*cond = CreateEvent(NULL, FALSE, FALSE, NULL);
470
return *cond == NULL ? -1 : 0;
471
#else
472
InitializeConditionVariable(cond);
473
return 0;
474
#endif
475
}
476
477
static inline void
478
mythread_cond_destroy(mythread_cond *cond)
479
{
480
#ifdef MYTHREAD_WIN95
481
CloseHandle(*cond);
482
#else
483
(void)cond;
484
#endif
485
}
486
487
static inline void
488
mythread_cond_signal(mythread_cond *cond)
489
{
490
#ifdef MYTHREAD_WIN95
491
SetEvent(*cond);
492
#else
493
WakeConditionVariable(cond);
494
#endif
495
}
496
497
static inline void
498
mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
499
{
500
#ifdef MYTHREAD_WIN95
501
LeaveCriticalSection(mutex);
502
WaitForSingleObject(*cond, INFINITE);
503
EnterCriticalSection(mutex);
504
#else
505
BOOL ret = SleepConditionVariableCS(cond, mutex, INFINITE);
506
assert(ret);
507
(void)ret;
508
#endif
509
}
510
511
static inline int
512
mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
513
const mythread_condtime *condtime)
514
{
515
#ifdef MYTHREAD_WIN95
516
LeaveCriticalSection(mutex);
517
#endif
518
519
DWORD elapsed = GetTickCount() - condtime->start;
520
DWORD timeout = elapsed >= condtime->timeout
521
? 0 : condtime->timeout - elapsed;
522
523
#ifdef MYTHREAD_WIN95
524
DWORD ret = WaitForSingleObject(*cond, timeout);
525
assert(ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT);
526
527
EnterCriticalSection(mutex);
528
529
return ret == WAIT_TIMEOUT;
530
#else
531
BOOL ret = SleepConditionVariableCS(cond, mutex, timeout);
532
assert(ret || GetLastError() == ERROR_TIMEOUT);
533
return !ret;
534
#endif
535
}
536
537
static inline void
538
mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
539
uint32_t timeout)
540
{
541
(void)cond;
542
condtime->start = GetTickCount();
543
condtime->timeout = timeout;
544
}
545
546
#endif
547
548
#endif
549
550