/////////////////////////////////////////////////////////////////////////////// // /// \file mythread.h /// \brief Some threading related helper macros and functions // // Author: Lasse Collin // // This file has been put into the public domain. // You can do whatever you want with this file. // /////////////////////////////////////////////////////////////////////////////// #include "sysdefs.h" #ifdef HAVE_PTHREAD //////////////////// // Using pthreads // //////////////////// #include #include #include #include /// \brief Set the process signal mask /// /// If threads are disabled, sigprocmask() is used instead /// of pthread_sigmask(). #define mythread_sigmask(how, set, oset) \ pthread_sigmask(how, set, oset) /// \brief Call the given function once /// /// If threads are disabled, a thread-unsafe version is used. #define mythread_once(func) \ do { \ static pthread_once_t once_ = PTHREAD_ONCE_INIT; \ pthread_once(&once_, &func); \ } while (0) /// \brief Lock a mutex for a duration of a block /// /// Perform pthread_mutex_lock(&mutex) in the beginning of a block /// and pthread_mutex_unlock(&mutex) at the end of the block. "break" /// may be used to unlock the mutex and jump out of the block. /// mythread_sync blocks may be nested. /// /// Example: /// /// mythread_sync(mutex) { /// foo(); /// if (some_error) /// break; // Skips bar() /// bar(); /// } /// /// At least GCC optimizes the loops completely away so it doesn't slow /// things down at all compared to plain pthread_mutex_lock(&mutex) /// and pthread_mutex_unlock(&mutex) calls. /// #define mythread_sync(mutex) mythread_sync_helper(mutex, __LINE__) #define mythread_sync_helper(mutex, line) \ for (unsigned int mythread_i_ ## line = 0; \ mythread_i_ ## line \ ? (pthread_mutex_unlock(&(mutex)), 0) \ : (pthread_mutex_lock(&(mutex)), 1); \ mythread_i_ ## line = 1) \ for (unsigned int mythread_j_ ## line = 0; \ !mythread_j_ ## line; \ mythread_j_ ## line = 1) typedef struct { /// Condition variable pthread_cond_t cond; /// Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with /// the condition variable clockid_t clk_id; } mythread_cond; /// \brief Initialize a condition variable to use CLOCK_MONOTONIC /// /// Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the /// timeout in pthread_cond_timedwait() work correctly also if system time /// is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available /// everywhere while the default CLOCK_REALTIME is, so the default is /// used if CLOCK_MONOTONIC isn't available. static inline int mythread_cond_init(mythread_cond *mycond) { #if defined(_POSIX_CLOCK_SELECTION) && defined(_POSIX_MONOTONIC_CLOCK) struct timespec ts; pthread_condattr_t condattr; // POSIX doesn't seem to *require* that pthread_condattr_setclock() // will fail if given an unsupported clock ID. Test that // CLOCK_MONOTONIC really is supported using clock_gettime(). if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0 && pthread_condattr_init(&condattr) == 0) { int ret = pthread_condattr_setclock( &condattr, CLOCK_MONOTONIC); if (ret == 0) ret = pthread_cond_init(&mycond->cond, &condattr); pthread_condattr_destroy(&condattr); if (ret == 0) { mycond->clk_id = CLOCK_MONOTONIC; return 0; } } // If anything above fails, fall back to the default CLOCK_REALTIME. #endif mycond->clk_id = CLOCK_REALTIME; return pthread_cond_init(&mycond->cond, NULL); } /// \brief Convert relative time to absolute time for use with timed wait /// /// The current time of the clock associated with the condition variable /// is added to the relative time in *ts. static inline void mythread_cond_abstime(const mythread_cond *mycond, struct timespec *ts) { struct timespec now; clock_gettime(mycond->clk_id, &now); ts->tv_sec += now.tv_sec; ts->tv_nsec += now.tv_nsec; // tv_nsec must stay in the range [0, 999_999_999]. if (ts->tv_nsec >= 1000000000L) { ts->tv_nsec -= 1000000000L; ++ts->tv_sec; } return; } #define mythread_cond_wait(mycondptr, mutexptr) \ pthread_cond_wait(&(mycondptr)->cond, mutexptr) #define mythread_cond_timedwait(mycondptr, mutexptr, abstimeptr) \ pthread_cond_timedwait(&(mycondptr)->cond, mutexptr, abstimeptr) #define mythread_cond_signal(mycondptr) \ pthread_cond_signal(&(mycondptr)->cond) #define mythread_cond_broadcast(mycondptr) \ pthread_cond_broadcast(&(mycondptr)->cond) #define mythread_cond_destroy(mycondptr) \ pthread_cond_destroy(&(mycondptr)->cond) /// \brief Create a thread with all signals blocked static inline int mythread_create(pthread_t *thread, void *(*func)(void *arg), void *arg) { sigset_t old; sigset_t all; sigfillset(&all); pthread_sigmask(SIG_SETMASK, &all, &old); const int ret = pthread_create(thread, NULL, func, arg); pthread_sigmask(SIG_SETMASK, &old, NULL); return ret; } #else ////////////////// // No threading // ////////////////// #define mythread_sigmask(how, set, oset) \ sigprocmask(how, set, oset) #define mythread_once(func) \ do { \ static bool once_ = false; \ if (!once_) { \ func(); \ once_ = true; \ } \ } while (0) #endif