| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \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. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #ifndef MYTHREAD_H |
| #define MYTHREAD_H |
| |
| #include "sysdefs.h" |
| |
| |
| #ifdef HAVE_PTHREAD |
| |
| //////////////////// |
| // Using pthreads // |
| //////////////////// |
| |
| #include <sys/time.h> |
| #include <pthread.h> |
| #include <signal.h> |
| #include <time.h> |
| |
| |
| #ifdef __VMS |
| // Do nothing on OpenVMS. It doesn't have pthread_sigmask(). |
| #define mythread_sigmask(how, set, oset) do { } while (0) |
| #else |
| /// \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) |
| #endif |
| |
| /// \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; |
| |
| #ifdef HAVE_CLOCK_GETTIME |
| /// Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with |
| /// the condition variable |
| clockid_t clk_id; |
| #endif |
| |
| } 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) |
| { |
| #ifdef HAVE_CLOCK_GETTIME |
| // NOTE: HAVE_DECL_CLOCK_MONOTONIC is always defined to 0 or 1. |
| # if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && HAVE_DECL_CLOCK_MONOTONIC |
| 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; |
| #endif |
| |
| 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) |
| { |
| #ifdef HAVE_CLOCK_GETTIME |
| struct timespec now; |
| clock_gettime(mycond->clk_id, &now); |
| |
| ts->tv_sec += now.tv_sec; |
| ts->tv_nsec += now.tv_nsec; |
| #else |
| (void)mycond; |
| |
| struct timeval now; |
| gettimeofday(&now, NULL); |
| |
| ts->tv_sec += now.tv_sec; |
| ts->tv_nsec += now.tv_usec * 1000L; |
| #endif |
| |
| // 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 |
| |
| #endif |