1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
6 /// \brief Some threading related helper macros and functions
8 // Author: Lasse Collin
10 ///////////////////////////////////////////////////////////////////////////////
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
24 #ifdef MYTHREAD_ENABLED
26 ////////////////////////////////////////
27 // Shared between all threading types //
28 ////////////////////////////////////////
30 // Locks a mutex for a duration of a block.
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.
39 // mythread_sync(mutex) {
42 // break; // Skips bar()
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.
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; \
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)
64 #if !defined(MYTHREAD_ENABLED)
70 // Calls the given function once. This isn't thread safe.
71 #define mythread_once(func) \
73 static bool once_ = false; \
81 #if !(defined(_WIN32) && !defined(__CYGWIN__)) && !defined(__wasm__)
82 // Use sigprocmask() to set the signal mask in single-threaded programs.
86 mythread_sigmask(int how
, const sigset_t
*restrict set
,
87 sigset_t
*restrict oset
)
89 int ret
= sigprocmask(how
, set
, oset
);
96 #elif defined(MYTHREAD_POSIX)
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>
114 // MinGW-w64 with winpthreads:
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
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.
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.
128 # include <sys/types.h>
129 # define sigset_t _sigset_t
130 # define sigfillset(set_ptr) do { *(set_ptr) = 0; } while (0)
133 #define MYTHREAD_RET_TYPE void *
134 #define MYTHREAD_RET_VALUE NULL
136 typedef pthread_t mythread
;
137 typedef pthread_mutex_t mythread_mutex
;
141 #ifdef HAVE_CLOCK_GETTIME
142 // Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
143 // the condition variable.
148 typedef struct timespec mythread_condtime
;
151 // Calls the given function once in a thread-safe way.
152 #define mythread_once(func) \
154 static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
155 pthread_once(&once_, &func); \
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).
164 mythread_sigmask(int how
, const sigset_t
*restrict set
,
165 sigset_t
*restrict oset
)
167 #if defined(__VMS) || defined(__MINGW32__)
172 int ret
= pthread_sigmask(how
, set
, oset
);
179 // Creates a new thread with all signals blocked. Returns zero on success
180 // and non-zero on error.
182 mythread_create(mythread
*thread
, void *(*func
)(void *arg
), void *arg
)
188 mythread_sigmask(SIG_SETMASK
, &all
, &old
);
189 const int ret
= pthread_create(thread
, NULL
, func
, arg
);
190 mythread_sigmask(SIG_SETMASK
, &old
, NULL
);
195 // Joins a thread. Returns zero on success and non-zero on error.
197 mythread_join(mythread thread
)
199 return pthread_join(thread
, NULL
);
203 // Initializes a mutex. Returns zero on success and non-zero on error.
205 mythread_mutex_init(mythread_mutex
*mutex
)
207 return pthread_mutex_init(mutex
, NULL
);
211 mythread_mutex_destroy(mythread_mutex
*mutex
)
213 int ret
= pthread_mutex_destroy(mutex
);
219 mythread_mutex_lock(mythread_mutex
*mutex
)
221 int ret
= pthread_mutex_lock(mutex
);
227 mythread_mutex_unlock(mythread_mutex
*mutex
)
229 int ret
= pthread_mutex_unlock(mutex
);
235 // Initializes a condition variable.
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.
243 // If clock_gettime() isn't available at all, gettimeofday() will be used.
245 mythread_cond_init(mythread_cond
*mycond
)
247 #ifdef HAVE_CLOCK_GETTIME
248 # if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
249 defined(HAVE_CLOCK_MONOTONIC)
251 pthread_condattr_t condattr
;
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
);
261 ret
= pthread_cond_init(&mycond
->cond
, &condattr
);
263 pthread_condattr_destroy(&condattr
);
266 mycond
->clk_id
= CLOCK_MONOTONIC
;
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.
276 mycond
->clk_id
= CLOCK_REALTIME
;
279 return pthread_cond_init(&mycond
->cond
, NULL
);
283 mythread_cond_destroy(mythread_cond
*cond
)
285 int ret
= pthread_cond_destroy(&cond
->cond
);
291 mythread_cond_signal(mythread_cond
*cond
)
293 int ret
= pthread_cond_signal(&cond
->cond
);
299 mythread_cond_wait(mythread_cond
*cond
, mythread_mutex
*mutex
)
301 int ret
= pthread_cond_wait(&cond
->cond
, mutex
);
306 // Waits on a condition or until a timeout expires. If the timeout expires,
307 // non-zero is returned, otherwise zero is returned.
309 mythread_cond_timedwait(mythread_cond
*cond
, mythread_mutex
*mutex
,
310 const mythread_condtime
*condtime
)
312 int ret
= pthread_cond_timedwait(&cond
->cond
, mutex
, condtime
);
313 assert(ret
== 0 || ret
== ETIMEDOUT
);
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.
320 mythread_condtime_set(mythread_condtime
*condtime
, const mythread_cond
*cond
,
323 condtime
->tv_sec
= (time_t)(timeout_ms
/ 1000);
324 condtime
->tv_nsec
= (long)((timeout_ms
% 1000) * 1000000);
326 #ifdef HAVE_CLOCK_GETTIME
328 int ret
= clock_gettime(cond
->clk_id
, &now
);
332 condtime
->tv_sec
+= now
.tv_sec
;
333 condtime
->tv_nsec
+= now
.tv_nsec
;
338 gettimeofday(&now
, NULL
);
340 condtime
->tv_sec
+= now
.tv_sec
;
341 condtime
->tv_nsec
+= now
.tv_usec
* 1000L;
344 // tv_nsec must stay in the range [0, 999_999_999].
345 if (condtime
->tv_nsec
>= 1000000000L) {
346 condtime
->tv_nsec
-= 1000000000L;
352 #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA)
354 /////////////////////
355 // Windows threads //
356 /////////////////////
358 #define WIN32_LEAN_AND_MEAN
359 #ifdef MYTHREAD_VISTA
361 # define _WIN32_WINNT 0x0600
366 #define MYTHREAD_RET_TYPE unsigned int __stdcall
367 #define MYTHREAD_RET_VALUE 0
369 typedef HANDLE mythread
;
370 typedef CRITICAL_SECTION mythread_mutex
;
372 #ifdef MYTHREAD_WIN95
373 typedef HANDLE mythread_cond
;
375 typedef CONDITION_VARIABLE mythread_cond
;
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.
384 // Length of the timeout in milliseconds. The timeout expires
385 // when the current tick count minus "start" is equal or greater
391 // mythread_once() is only available with Vista threads.
392 #ifdef MYTHREAD_VISTA
393 #define mythread_once(func) \
395 static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \
397 if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \
401 if (!InitOnceComplete(&once_, 0, NULL)) \
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.
413 mythread_create(mythread
*thread
,
414 unsigned int (__stdcall
*func
)(void *arg
), void *arg
)
416 uintptr_t ret
= _beginthreadex(NULL
, 0, func
, arg
, 0, NULL
);
420 *thread
= (HANDLE
)ret
;
425 mythread_join(mythread thread
)
429 if (WaitForSingleObject(thread
, INFINITE
) != WAIT_OBJECT_0
)
432 if (!CloseHandle(thread
))
440 mythread_mutex_init(mythread_mutex
*mutex
)
442 InitializeCriticalSection(mutex
);
447 mythread_mutex_destroy(mythread_mutex
*mutex
)
449 DeleteCriticalSection(mutex
);
453 mythread_mutex_lock(mythread_mutex
*mutex
)
455 EnterCriticalSection(mutex
);
459 mythread_mutex_unlock(mythread_mutex
*mutex
)
461 LeaveCriticalSection(mutex
);
466 mythread_cond_init(mythread_cond
*cond
)
468 #ifdef MYTHREAD_WIN95
469 *cond
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
470 return *cond
== NULL
? -1 : 0;
472 InitializeConditionVariable(cond
);
478 mythread_cond_destroy(mythread_cond
*cond
)
480 #ifdef MYTHREAD_WIN95
488 mythread_cond_signal(mythread_cond
*cond
)
490 #ifdef MYTHREAD_WIN95
493 WakeConditionVariable(cond
);
498 mythread_cond_wait(mythread_cond
*cond
, mythread_mutex
*mutex
)
500 #ifdef MYTHREAD_WIN95
501 LeaveCriticalSection(mutex
);
502 WaitForSingleObject(*cond
, INFINITE
);
503 EnterCriticalSection(mutex
);
505 BOOL ret
= SleepConditionVariableCS(cond
, mutex
, INFINITE
);
512 mythread_cond_timedwait(mythread_cond
*cond
, mythread_mutex
*mutex
,
513 const mythread_condtime
*condtime
)
515 #ifdef MYTHREAD_WIN95
516 LeaveCriticalSection(mutex
);
519 DWORD elapsed
= GetTickCount() - condtime
->start
;
520 DWORD timeout
= elapsed
>= condtime
->timeout
521 ? 0 : condtime
->timeout
- elapsed
;
523 #ifdef MYTHREAD_WIN95
524 DWORD ret
= WaitForSingleObject(*cond
, timeout
);
525 assert(ret
== WAIT_OBJECT_0
|| ret
== WAIT_TIMEOUT
);
527 EnterCriticalSection(mutex
);
529 return ret
== WAIT_TIMEOUT
;
531 BOOL ret
= SleepConditionVariableCS(cond
, mutex
, timeout
);
532 assert(ret
|| GetLastError() == ERROR_TIMEOUT
);
538 mythread_condtime_set(mythread_condtime
*condtime
, const mythread_cond
*cond
,
542 condtime
->start
= GetTickCount();
543 condtime
->timeout
= timeout
;