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