1 /////////////////////////////////////////////////////////////////////////////// 2 // 3 /// \file mythread.h 4 /// \brief Some threading related helper macros and functions 5 // 6 // Author: Lasse Collin 7 // 8 // This file has been put into the public domain. 9 // You can do whatever you want with this file. 10 // 11 /////////////////////////////////////////////////////////////////////////////// 12 13 #ifndef MYTHREAD_H 14 #define MYTHREAD_H 15 16 #include "sysdefs.h" 17 18 // If any type of threading is enabled, #define MYTHREAD_ENABLED. 19 #if defined(MYTHREAD_POSIX) || defined(MYTHREAD_WIN95) \ 20 || defined(MYTHREAD_VISTA) 21 # define MYTHREAD_ENABLED 1 22 #endif 23 24 25 #ifdef MYTHREAD_ENABLED 26 27 //////////////////////////////////////// 28 // Shared between all threading types // 29 //////////////////////////////////////// 30 31 // Locks a mutex for a duration of a block. 32 // 33 // Perform mythread_mutex_lock(&mutex) in the beginning of a block 34 // and mythread_mutex_unlock(&mutex) at the end of the block. "break" 35 // may be used to unlock the mutex and jump out of the block. 36 // mythread_sync blocks may be nested. 37 // 38 // Example: 39 // 40 // mythread_sync(mutex) { 41 // foo(); 42 // if (some_error) 43 // break; // Skips bar() 44 // bar(); 45 // } 46 // 47 // At least GCC optimizes the loops completely away so it doesn't slow 48 // things down at all compared to plain mythread_mutex_lock(&mutex) 49 // and mythread_mutex_unlock(&mutex) calls. 50 // 51 #define mythread_sync(mutex) mythread_sync_helper1(mutex, __LINE__) 52 #define mythread_sync_helper1(mutex, line) mythread_sync_helper2(mutex, line) 53 #define mythread_sync_helper2(mutex, line) \ 54 for (unsigned int mythread_i_ ## line = 0; \ 55 mythread_i_ ## line \ 56 ? (mythread_mutex_unlock(&(mutex)), 0) \ 57 : (mythread_mutex_lock(&(mutex)), 1); \ 58 mythread_i_ ## line = 1) \ 59 for (unsigned int mythread_j_ ## line = 0; \ 60 !mythread_j_ ## line; \ 61 mythread_j_ ## line = 1) 62 #endif 63 64 65 #if !defined(MYTHREAD_ENABLED) 66 67 ////////////////// 68 // No threading // 69 ////////////////// 70 71 // Calls the given function once. This isn't thread safe. 72 #define mythread_once(func) \ 73 do { \ 74 static bool once_ = false; \ 75 if (!once_) { \ 76 func(); \ 77 once_ = true; \ 78 } \ 79 } while (0) 80 81 82 #if !(defined(_WIN32) && !defined(__CYGWIN__)) && !defined(__wasm__) 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 #define MYTHREAD_RET_TYPE void * 116 #define MYTHREAD_RET_VALUE NULL 117 118 typedef pthread_t mythread; 119 typedef pthread_mutex_t mythread_mutex; 120 121 typedef struct { 122 pthread_cond_t cond; 123 #ifdef HAVE_CLOCK_GETTIME 124 // Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with 125 // the condition variable. 126 clockid_t clk_id; 127 #endif 128 } mythread_cond; 129 130 typedef struct timespec mythread_condtime; 131 132 133 // Calls the given function once in a thread-safe way. 134 #define mythread_once(func) \ 135 do { \ 136 static pthread_once_t once_ = PTHREAD_ONCE_INIT; \ 137 pthread_once(&once_, &func); \ 138 } while (0) 139 140 141 // Use pthread_sigmask() to set the signal mask in multi-threaded programs. 142 // Do nothing on OpenVMS since it lacks pthread_sigmask(). 143 static inline void 144 mythread_sigmask(int how, const sigset_t *restrict set, 145 sigset_t *restrict oset) 146 { 147 #ifdef __VMS 148 (void)how; 149 (void)set; 150 (void)oset; 151 #else 152 int ret = pthread_sigmask(how, set, oset); 153 assert(ret == 0); 154 (void)ret; 155 #endif 156 } 157 158 159 // Creates a new thread with all signals blocked. Returns zero on success 160 // and non-zero on error. 161 static inline int 162 mythread_create(mythread *thread, void *(*func)(void *arg), void *arg) 163 { 164 sigset_t old; 165 sigset_t all; 166 sigfillset(&all); 167 168 mythread_sigmask(SIG_SETMASK, &all, &old); 169 const int ret = pthread_create(thread, NULL, func, arg); 170 mythread_sigmask(SIG_SETMASK, &old, NULL); 171 172 return ret; 173 } 174 175 // Joins a thread. Returns zero on success and non-zero on error. 176 static inline int 177 mythread_join(mythread thread) 178 { 179 return pthread_join(thread, NULL); 180 } 181 182 183 // Initiatlizes a mutex. Returns zero on success and non-zero on error. 184 static inline int 185 mythread_mutex_init(mythread_mutex *mutex) 186 { 187 return pthread_mutex_init(mutex, NULL); 188 } 189 190 static inline void 191 mythread_mutex_destroy(mythread_mutex *mutex) 192 { 193 int ret = pthread_mutex_destroy(mutex); 194 assert(ret == 0); 195 (void)ret; 196 } 197 198 static inline void 199 mythread_mutex_lock(mythread_mutex *mutex) 200 { 201 int ret = pthread_mutex_lock(mutex); 202 assert(ret == 0); 203 (void)ret; 204 } 205 206 static inline void 207 mythread_mutex_unlock(mythread_mutex *mutex) 208 { 209 int ret = pthread_mutex_unlock(mutex); 210 assert(ret == 0); 211 (void)ret; 212 } 213 214 215 // Initializes a condition variable. 216 // 217 // Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the 218 // timeout in pthread_cond_timedwait() work correctly also if system time 219 // is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available 220 // everywhere while the default CLOCK_REALTIME is, so the default is 221 // used if CLOCK_MONOTONIC isn't available. 222 // 223 // If clock_gettime() isn't available at all, gettimeofday() will be used. 224 static inline int 225 mythread_cond_init(mythread_cond *mycond) 226 { 227 #ifdef HAVE_CLOCK_GETTIME 228 # if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \ 229 defined(HAVE_CLOCK_MONOTONIC) 230 struct timespec ts; 231 pthread_condattr_t condattr; 232 233 // POSIX doesn't seem to *require* that pthread_condattr_setclock() 234 // will fail if given an unsupported clock ID. Test that 235 // CLOCK_MONOTONIC really is supported using clock_gettime(). 236 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0 237 && pthread_condattr_init(&condattr) == 0) { 238 int ret = pthread_condattr_setclock( 239 &condattr, CLOCK_MONOTONIC); 240 if (ret == 0) 241 ret = pthread_cond_init(&mycond->cond, &condattr); 242 243 pthread_condattr_destroy(&condattr); 244 245 if (ret == 0) { 246 mycond->clk_id = CLOCK_MONOTONIC; 247 return 0; 248 } 249 } 250 251 // If anything above fails, fall back to the default CLOCK_REALTIME. 252 // POSIX requires that all implementations of clock_gettime() must 253 // support at least CLOCK_REALTIME. 254 # endif 255 256 mycond->clk_id = CLOCK_REALTIME; 257 #endif 258 259 return pthread_cond_init(&mycond->cond, NULL); 260 } 261 262 static inline void 263 mythread_cond_destroy(mythread_cond *cond) 264 { 265 int ret = pthread_cond_destroy(&cond->cond); 266 assert(ret == 0); 267 (void)ret; 268 } 269 270 static inline void 271 mythread_cond_signal(mythread_cond *cond) 272 { 273 int ret = pthread_cond_signal(&cond->cond); 274 assert(ret == 0); 275 (void)ret; 276 } 277 278 static inline void 279 mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex) 280 { 281 int ret = pthread_cond_wait(&cond->cond, mutex); 282 assert(ret == 0); 283 (void)ret; 284 } 285 286 // Waits on a condition or until a timeout expires. If the timeout expires, 287 // non-zero is returned, otherwise zero is returned. 288 static inline int 289 mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex, 290 const mythread_condtime *condtime) 291 { 292 int ret = pthread_cond_timedwait(&cond->cond, mutex, condtime); 293 assert(ret == 0 || ret == ETIMEDOUT); 294 return ret; 295 } 296 297 // Sets condtime to the absolute time that is timeout_ms milliseconds 298 // in the future. The type of the clock to use is taken from cond. 299 static inline void 300 mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond, 301 uint32_t timeout_ms) 302 { 303 condtime->tv_sec = (time_t)(timeout_ms / 1000); 304 condtime->tv_nsec = (long)((timeout_ms % 1000) * 1000000); 305 306 #ifdef HAVE_CLOCK_GETTIME 307 struct timespec now; 308 int ret = clock_gettime(cond->clk_id, &now); 309 assert(ret == 0); 310 (void)ret; 311 312 condtime->tv_sec += now.tv_sec; 313 condtime->tv_nsec += now.tv_nsec; 314 #else 315 (void)cond; 316 317 struct timeval now; 318 gettimeofday(&now, NULL); 319 320 condtime->tv_sec += now.tv_sec; 321 condtime->tv_nsec += now.tv_usec * 1000L; 322 #endif 323 324 // tv_nsec must stay in the range [0, 999_999_999]. 325 if (condtime->tv_nsec >= 1000000000L) { 326 condtime->tv_nsec -= 1000000000L; 327 ++condtime->tv_sec; 328 } 329 } 330 331 332 #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA) 333 334 ///////////////////// 335 // Windows threads // 336 ///////////////////// 337 338 #define WIN32_LEAN_AND_MEAN 339 #ifdef MYTHREAD_VISTA 340 # undef _WIN32_WINNT 341 # define _WIN32_WINNT 0x0600 342 #endif 343 #include <windows.h> 344 #include <process.h> 345 346 #define MYTHREAD_RET_TYPE unsigned int __stdcall 347 #define MYTHREAD_RET_VALUE 0 348 349 typedef HANDLE mythread; 350 typedef CRITICAL_SECTION mythread_mutex; 351 352 #ifdef MYTHREAD_WIN95 353 typedef HANDLE mythread_cond; 354 #else 355 typedef CONDITION_VARIABLE mythread_cond; 356 #endif 357 358 typedef struct { 359 // Tick count (milliseconds) in the beginning of the timeout. 360 // NOTE: This is 32 bits so it wraps around after 49.7 days. 361 // Multi-day timeouts may not work as expected. 362 DWORD start; 363 364 // Length of the timeout in milliseconds. The timeout expires 365 // when the current tick count minus "start" is equal or greater 366 // than "timeout". 367 DWORD timeout; 368 } mythread_condtime; 369 370 371 // mythread_once() is only available with Vista threads. 372 #ifdef MYTHREAD_VISTA 373 #define mythread_once(func) \ 374 do { \ 375 static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \ 376 BOOL pending_; \ 377 if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \ 378 abort(); \ 379 if (pending_) { \ 380 func(); \ 381 if (!InitOnceComplete(&once_, 0, NULL)) \ 382 abort(); \ 383 } \ 384 } while (0) 385 #endif 386 387 388 // mythread_sigmask() isn't available on Windows. Even a dummy version would 389 // make no sense because the other POSIX signal functions are missing anyway. 390 391 392 static inline int 393 mythread_create(mythread *thread, 394 unsigned int (__stdcall *func)(void *arg), void *arg) 395 { 396 uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL); 397 if (ret == 0) 398 return -1; 399 400 *thread = (HANDLE)ret; 401 return 0; 402 } 403 404 static inline int 405 mythread_join(mythread thread) 406 { 407 int ret = 0; 408 409 if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) 410 ret = -1; 411 412 if (!CloseHandle(thread)) 413 ret = -1; 414 415 return ret; 416 } 417 418 419 static inline int 420 mythread_mutex_init(mythread_mutex *mutex) 421 { 422 InitializeCriticalSection(mutex); 423 return 0; 424 } 425 426 static inline void 427 mythread_mutex_destroy(mythread_mutex *mutex) 428 { 429 DeleteCriticalSection(mutex); 430 } 431 432 static inline void 433 mythread_mutex_lock(mythread_mutex *mutex) 434 { 435 EnterCriticalSection(mutex); 436 } 437 438 static inline void 439 mythread_mutex_unlock(mythread_mutex *mutex) 440 { 441 LeaveCriticalSection(mutex); 442 } 443 444 445 static inline int 446 mythread_cond_init(mythread_cond *cond) 447 { 448 #ifdef MYTHREAD_WIN95 449 *cond = CreateEvent(NULL, FALSE, FALSE, NULL); 450 return *cond == NULL ? -1 : 0; 451 #else 452 InitializeConditionVariable(cond); 453 return 0; 454 #endif 455 } 456 457 static inline void 458 mythread_cond_destroy(mythread_cond *cond) 459 { 460 #ifdef MYTHREAD_WIN95 461 CloseHandle(*cond); 462 #else 463 (void)cond; 464 #endif 465 } 466 467 static inline void 468 mythread_cond_signal(mythread_cond *cond) 469 { 470 #ifdef MYTHREAD_WIN95 471 SetEvent(*cond); 472 #else 473 WakeConditionVariable(cond); 474 #endif 475 } 476 477 static inline void 478 mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex) 479 { 480 #ifdef MYTHREAD_WIN95 481 LeaveCriticalSection(mutex); 482 WaitForSingleObject(*cond, INFINITE); 483 EnterCriticalSection(mutex); 484 #else 485 BOOL ret = SleepConditionVariableCS(cond, mutex, INFINITE); 486 assert(ret); 487 (void)ret; 488 #endif 489 } 490 491 static inline int 492 mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex, 493 const mythread_condtime *condtime) 494 { 495 #ifdef MYTHREAD_WIN95 496 LeaveCriticalSection(mutex); 497 #endif 498 499 DWORD elapsed = GetTickCount() - condtime->start; 500 DWORD timeout = elapsed >= condtime->timeout 501 ? 0 : condtime->timeout - elapsed; 502 503 #ifdef MYTHREAD_WIN95 504 DWORD ret = WaitForSingleObject(*cond, timeout); 505 assert(ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT); 506 507 EnterCriticalSection(mutex); 508 509 return ret == WAIT_TIMEOUT; 510 #else 511 BOOL ret = SleepConditionVariableCS(cond, mutex, timeout); 512 assert(ret || GetLastError() == ERROR_TIMEOUT); 513 return !ret; 514 #endif 515 } 516 517 static inline void 518 mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond, 519 uint32_t timeout) 520 { 521 (void)cond; 522 condtime->start = GetTickCount(); 523 condtime->timeout = timeout; 524 } 525 526 #endif 527 528 #endif 529