2008-12-30 17:30:49 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
/// \file mythread.h
|
2011-04-10 14:23:21 -04:00
|
|
|
/// \brief Some threading related helper macros and functions
|
2008-12-30 17:30:49 -05:00
|
|
|
//
|
2009-04-13 04:27:40 -04:00
|
|
|
// Author: Lasse Collin
|
|
|
|
//
|
2008-12-30 17:30:49 -05:00
|
|
|
// This file has been put into the public domain.
|
2009-04-13 04:27:40 -04:00
|
|
|
// You can do whatever you want with this file.
|
2008-12-30 17:30:49 -05:00
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "sysdefs.h"
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
|
|
|
2011-04-10 14:23:21 -04:00
|
|
|
////////////////////
|
|
|
|
// Using pthreads //
|
|
|
|
////////////////////
|
2008-12-30 17:30:49 -05:00
|
|
|
|
2011-04-10 14:23:21 -04:00
|
|
|
#include <pthread.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
|
|
/// \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;
|
|
|
|
}
|
2009-01-07 11:41:15 -05:00
|
|
|
|
2008-12-30 17:30:49 -05:00
|
|
|
#else
|
|
|
|
|
2011-04-10 14:23:21 -04:00
|
|
|
//////////////////
|
|
|
|
// 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)
|
2009-01-07 11:41:15 -05:00
|
|
|
|
2008-12-30 17:30:49 -05:00
|
|
|
#endif
|