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