Parolin 0.7.9 6796
Console (soon DLLs) to do a tar like job
Loading...
Searching...
No Matches
mythread.h
Go to the documentation of this file.
1// SPDX-License-Identifier: 0BSD
2
4//
7//
8// Author: Lasse Collin
9//
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
27// Shared between all threading types //
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
67// No threading //
69
70// Calls the given function once. This isn't thread safe.
71#define mythread_once(func) \
72do { \
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
85static inline void
86mythread_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
99// Using pthreads //
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
136typedef pthread_t mythread;
137typedef pthread_mutex_t mythread_mutex;
138
139typedef 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
148typedef 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).
163static inline void
164mythread_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.
181static inline int
182mythread_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.
196static inline int
197mythread_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.
204static inline int
205mythread_mutex_init(mythread_mutex *mutex)
206{
207 return pthread_mutex_init(mutex, NULL);
208}
209
210static inline void
211mythread_mutex_destroy(mythread_mutex *mutex)
212{
213 int ret = pthread_mutex_destroy(mutex);
214 assert(ret == 0);
215 (void)ret;
216}
217
218static inline void
219mythread_mutex_lock(mythread_mutex *mutex)
220{
221 int ret = pthread_mutex_lock(mutex);
222 assert(ret == 0);
223 (void)ret;
224}
225
226static inline void
227mythread_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.
244static inline int
245mythread_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
282static inline void
283mythread_cond_destroy(mythread_cond *cond)
284{
285 int ret = pthread_cond_destroy(&cond->cond);
286 assert(ret == 0);
287 (void)ret;
288}
289
290static inline void
291mythread_cond_signal(mythread_cond *cond)
292{
293 int ret = pthread_cond_signal(&cond->cond);
294 assert(ret == 0);
295 (void)ret;
296}
297
298static inline void
299mythread_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.
308static inline int
309mythread_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.
319static inline void
320mythread_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
355// Windows threads //
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
369typedef HANDLE mythread;
370typedef CRITICAL_SECTION mythread_mutex;
371
372#ifdef MYTHREAD_WIN95
373typedef HANDLE mythread_cond;
374#else
375typedef CONDITION_VARIABLE mythread_cond;
376#endif
377
378typedef 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".
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
412static inline int
413mythread_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
424static inline int
425mythread_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
439static inline int
440mythread_mutex_init(mythread_mutex *mutex)
441{
442 InitializeCriticalSection(mutex);
443 return 0;
444}
445
446static inline void
447mythread_mutex_destroy(mythread_mutex *mutex)
448{
449 DeleteCriticalSection(mutex);
450}
451
452static inline void
453mythread_mutex_lock(mythread_mutex *mutex)
454{
455 EnterCriticalSection(mutex);
456}
457
458static inline void
459mythread_mutex_unlock(mythread_mutex *mutex)
460{
461 LeaveCriticalSection(mutex);
462}
463
464
465static inline int
466mythread_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
477static inline void
478mythread_cond_destroy(mythread_cond *cond)
479{
480#ifdef MYTHREAD_WIN95
481 CloseHandle(*cond);
482#else
483 (void)cond;
484#endif
485}
486
487static inline void
488mythread_cond_signal(mythread_cond *cond)
489{
490#ifdef MYTHREAD_WIN95
491 SetEvent(*cond);
492#else
493 WakeConditionVariable(cond);
494#endif
495}
496
497static inline void
498mythread_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
511static inline int
512mythread_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
537static inline void
538mythread_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
UINT32 DWORD
Definition 7zTypes.h:194
int BOOL
Definition 7zMain.c:321
int start()
#define assert(condition)
Definition lz4.c:273
now
Definition rateLimiter.py:31
timeout
Definition run.py:691
size_t uintptr_t
Definition fuzzer.c:71
#define NULL
Definition getopt1.c:37
Common includes, definitions, system-specific things etc.
ret
Definition zlib_interface.c:30