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