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