mythread.h   mythread.h 
skipping to change at line 18 skipping to change at line 18
// This file has been put into the public domain. // This file has been put into the public domain.
// You can do whatever you want with this file. // You can do whatever you want with this file.
// //
/////////////////////////////////////////////////////////////////////////// //// /////////////////////////////////////////////////////////////////////////// ////
#ifndef MYTHREAD_H #ifndef MYTHREAD_H
#define MYTHREAD_H #define MYTHREAD_H
#include "sysdefs.h" #include "sysdefs.h"
#ifdef HAVE_PTHREAD // If any type of threading is enabled, #define MYTHREAD_ENABLED.
#if defined(MYTHREAD_POSIX) || defined(MYTHREAD_WIN95) \
|| defined(MYTHREAD_VISTA)
# define MYTHREAD_ENABLED 1
#endif
#ifdef MYTHREAD_ENABLED
////////////////////////////////////////
// Shared between all threading types //
////////////////////////////////////////
// Locks a mutex for a duration of a block.
//
// Perform mythread_mutex_lock(&mutex) in the beginning of a block
// and mythread_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 mythread_mutex_lock(&mutex)
// and mythread_mutex_unlock(&mutex) calls.
//
#define mythread_sync(mutex) mythread_sync_helper1(mutex, __LINE__)
#define mythread_sync_helper1(mutex, line) mythread_sync_helper2(mutex, lin
e)
#define mythread_sync_helper2(mutex, line) \
for (unsigned int mythread_i_ ## line = 0; \
mythread_i_ ## line \
? (mythread_mutex_unlock(&(mutex)), 0) \
: (mythread_mutex_lock(&(mutex)), 1); \
mythread_i_ ## line = 1) \
for (unsigned int mythread_j_ ## line = 0; \
!mythread_j_ ## line; \
mythread_j_ ## line = 1)
#endif
#if !defined(MYTHREAD_ENABLED)
//////////////////
// No threading //
//////////////////
// Calls the given function once. This isn't thread safe.
#define mythread_once(func) \
do { \
static bool once_ = false; \
if (!once_) { \
func(); \
once_ = true; \
} \
} while (0)
#if !(defined(_WIN32) && !defined(__CYGWIN__))
// Use sigprocmask() to set the signal mask in single-threaded programs.
#include <signal.h>
static inline void
mythread_sigmask(int how, const sigset_t *restrict set,
sigset_t *restrict oset)
{
int ret = sigprocmask(how, set, oset);
assert(ret == 0);
(void)ret;
}
#endif
#elif defined(MYTHREAD_POSIX)
//////////////////// ////////////////////
// Using pthreads // // Using pthreads //
//////////////////// ////////////////////
#include <sys/time.h> #include <sys/time.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
#include <errno.h>
#ifdef __VMS #define MYTHREAD_RET_TYPE void *
// Do nothing on OpenVMS. It doesn't have pthread_sigmask(). #define MYTHREAD_RET_VALUE NULL
#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 typedef pthread_t mythread;
/// typedef pthread_mutex_t mythread_mutex;
/// 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 { typedef struct {
/// Condition variable
pthread_cond_t cond; pthread_cond_t cond;
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
/// Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with // Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
/// the condition variable // the condition variable.
clockid_t clk_id; clockid_t clk_id;
#endif #endif
} mythread_cond; } mythread_cond;
/// \brief Initialize a condition variable to use CLOCK_MONOTONIC typedef struct timespec mythread_condtime;
///
/// Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the // Calls the given function once in a thread-safe way.
/// timeout in pthread_cond_timedwait() work correctly also if system time #define mythread_once(func) \
/// is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available do { \
/// everywhere while the default CLOCK_REALTIME is, so the default is static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
/// used if CLOCK_MONOTONIC isn't available. pthread_once(&once_, &func); \
} while (0)
// Use pthread_sigmask() to set the signal mask in multi-threaded programs.
// Do nothing on OpenVMS since it lacks pthread_sigmask().
static inline void
mythread_sigmask(int how, const sigset_t *restrict set,
sigset_t *restrict oset)
{
#ifdef __VMS
(void)how;
(void)set;
(void)oset;
#else
int ret = pthread_sigmask(how, set, oset);
assert(ret == 0);
(void)ret;
#endif
}
// Creates a new thread with all signals blocked. Returns zero on success
// and non-zero on error.
static inline int
mythread_create(mythread *thread, void *(*func)(void *arg), void *arg)
{
sigset_t old;
sigset_t all;
sigfillset(&all);
mythread_sigmask(SIG_SETMASK, &all, &old);
const int ret = pthread_create(thread, NULL, func, arg);
mythread_sigmask(SIG_SETMASK, &old, NULL);
return ret;
}
// Joins a thread. Returns zero on success and non-zero on error.
static inline int
mythread_join(mythread thread)
{
return pthread_join(thread, NULL);
}
// Initiatlizes a mutex. Returns zero on success and non-zero on error.
static inline int
mythread_mutex_init(mythread_mutex *mutex)
{
return pthread_mutex_init(mutex, NULL);
}
static inline void
mythread_mutex_destroy(mythread_mutex *mutex)
{
int ret = pthread_mutex_destroy(mutex);
assert(ret == 0);
(void)ret;
}
static inline void
mythread_mutex_lock(mythread_mutex *mutex)
{
int ret = pthread_mutex_lock(mutex);
assert(ret == 0);
(void)ret;
}
static inline void
mythread_mutex_unlock(mythread_mutex *mutex)
{
int ret = pthread_mutex_unlock(mutex);
assert(ret == 0);
(void)ret;
}
// Initializes a condition variable.
//
// 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.
//
// If clock_gettime() isn't available at all, gettimeofday() will be used.
static inline int static inline int
mythread_cond_init(mythread_cond *mycond) mythread_cond_init(mythread_cond *mycond)
{ {
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
// NOTE: HAVE_DECL_CLOCK_MONOTONIC is always defined to 0 or 1. // NOTE: HAVE_DECL_CLOCK_MONOTONIC is always defined to 0 or 1.
# if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && HAVE_DECL_CLOCK_MONOTO NIC # if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && HAVE_DECL_CLOCK_MONOTO NIC
struct timespec ts; struct timespec ts;
pthread_condattr_t condattr; pthread_condattr_t condattr;
// POSIX doesn't seem to *require* that pthread_condattr_setclock() // POSIX doesn't seem to *require* that pthread_condattr_setclock()
skipping to change at line 128 skipping to change at line 237
pthread_condattr_destroy(&condattr); pthread_condattr_destroy(&condattr);
if (ret == 0) { if (ret == 0) {
mycond->clk_id = CLOCK_MONOTONIC; mycond->clk_id = CLOCK_MONOTONIC;
return 0; return 0;
} }
} }
// If anything above fails, fall back to the default CLOCK_REALTIME. // If anything above fails, fall back to the default CLOCK_REALTIME.
// POSIX requires that all implementations of clock_gettime() must
// support at least CLOCK_REALTIME.
# endif # endif
mycond->clk_id = CLOCK_REALTIME; mycond->clk_id = CLOCK_REALTIME;
#endif #endif
return pthread_cond_init(&mycond->cond, NULL); return pthread_cond_init(&mycond->cond, NULL);
} }
/// \brief Convert relative time to absolute time for use with timed w
ait
///
/// The current time of the clock associated with the condition variable
/// is added to the relative time in *ts.
static inline void static inline void
mythread_cond_abstime(const mythread_cond *mycond, struct timespec *ts) mythread_cond_destroy(mythread_cond *cond)
{ {
int ret = pthread_cond_destroy(&cond->cond);
assert(ret == 0);
(void)ret;
}
static inline void
mythread_cond_signal(mythread_cond *cond)
{
int ret = pthread_cond_signal(&cond->cond);
assert(ret == 0);
(void)ret;
}
static inline void
mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
{
int ret = pthread_cond_wait(&cond->cond, mutex);
assert(ret == 0);
(void)ret;
}
// Waits on a condition or until a timeout expires. If the timeout expires,
// non-zero is returned, otherwise zero is returned.
static inline int
mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
const mythread_condtime *condtime)
{
int ret = pthread_cond_timedwait(&cond->cond, mutex, condtime);
assert(ret == 0 || ret == ETIMEDOUT);
return ret;
}
// Sets condtime to the absolute time that is timeout_ms milliseconds
// in the future. The type of the clock to use is taken from cond.
static inline void
mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *con
d,
uint32_t timeout_ms)
{
condtime->tv_sec = timeout_ms / 1000;
condtime->tv_nsec = (timeout_ms % 1000) * 1000000;
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
struct timespec now; struct timespec now;
clock_gettime(mycond->clk_id, &now); int ret = clock_gettime(cond->clk_id, &now);
assert(ret == 0);
(void)ret;
ts->tv_sec += now.tv_sec; condtime->tv_sec += now.tv_sec;
ts->tv_nsec += now.tv_nsec; condtime->tv_nsec += now.tv_nsec;
#else #else
(void)mycond; (void)cond;
struct timeval now; struct timeval now;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
ts->tv_sec += now.tv_sec; condtime->tv_sec += now.tv_sec;
ts->tv_nsec += now.tv_usec * 1000L; condtime->tv_nsec += now.tv_usec * 1000L;
#endif #endif
// tv_nsec must stay in the range [0, 999_999_999]. // tv_nsec must stay in the range [0, 999_999_999].
if (ts->tv_nsec >= 1000000000L) { if (condtime->tv_nsec >= 1000000000L) {
ts->tv_nsec -= 1000000000L; condtime->tv_nsec -= 1000000000L;
++ts->tv_sec; ++condtime->tv_sec;
} }
return;
} }
#define mythread_cond_wait(mycondptr, mutexptr) \ #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA)
pthread_cond_wait(&(mycondptr)->cond, mutexptr)
/////////////////////
// Windows threads //
/////////////////////
#define WIN32_LEAN_AND_MEAN
#ifdef MYTHREAD_VISTA
# undef _WIN32_WINNT
# define _WIN32_WINNT 0x0600
#endif
#include <windows.h>
#include <process.h>
#define MYTHREAD_RET_TYPE unsigned int __stdcall
#define MYTHREAD_RET_VALUE 0
#define mythread_cond_timedwait(mycondptr, mutexptr, abstimeptr) \ typedef HANDLE mythread;
pthread_cond_timedwait(&(mycondptr)->cond, mutexptr, abstimeptr) typedef CRITICAL_SECTION mythread_mutex;
#define mythread_cond_signal(mycondptr) \ #ifdef MYTHREAD_WIN95
pthread_cond_signal(&(mycondptr)->cond) typedef HANDLE mythread_cond;
#else
typedef CONDITION_VARIABLE mythread_cond;
#endif
#define mythread_cond_broadcast(mycondptr) \ typedef struct {
pthread_cond_broadcast(&(mycondptr)->cond) // Tick count (milliseconds) in the beginning of the timeout.
// NOTE: This is 32 bits so it wraps around after 49.7 days.
// Multi-day timeouts may not work as expected.
DWORD start;
// Length of the timeout in milliseconds. The timeout expires
// when the current tick count minus "start" is equal or greater
// than "timeout".
DWORD timeout;
} mythread_condtime;
#define mythread_cond_destroy(mycondptr) \ // mythread_once() is only available with Vista threads.
pthread_cond_destroy(&(mycondptr)->cond) #ifdef MYTHREAD_VISTA
#define mythread_once(func) \
do { \
static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \
BOOL pending_; \
if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \
abort(); \
if (pending_) \
func(); \
if (!InitOnceComplete(&once, 0, NULL)) \
abort(); \
} while (0)
#endif
// mythread_sigmask() isn't available on Windows. Even a dummy version woul
d
// make no sense because the other POSIX signal functions are missing anywa
y.
/// \brief Create a thread with all signals blocked
static inline int static inline int
mythread_create(pthread_t *thread, void *(*func)(void *arg), void *arg) mythread_create(mythread *thread,
unsigned int (__stdcall *func)(void *arg), void *arg)
{ {
sigset_t old; uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL);
sigset_t all; if (ret == 0)
sigfillset(&all); return -1;
pthread_sigmask(SIG_SETMASK, &all, &old); *thread = (HANDLE)ret;
const int ret = pthread_create(thread, NULL, func, arg); return 0;
pthread_sigmask(SIG_SETMASK, &old, NULL); }
static inline int
mythread_join(mythread thread)
{
int ret = 0;
if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
ret = -1;
if (!CloseHandle(thread))
ret = -1;
return ret; return ret;
} }
static inline int
mythread_mutex_init(mythread_mutex *mutex)
{
InitializeCriticalSection(mutex);
return 0;
}
static inline void
mythread_mutex_destroy(mythread_mutex *mutex)
{
DeleteCriticalSection(mutex);
}
static inline void
mythread_mutex_lock(mythread_mutex *mutex)
{
EnterCriticalSection(mutex);
}
static inline void
mythread_mutex_unlock(mythread_mutex *mutex)
{
LeaveCriticalSection(mutex);
}
static inline int
mythread_cond_init(mythread_cond *cond)
{
#ifdef MYTHREAD_WIN95
*cond = CreateEvent(NULL, FALSE, FALSE, NULL);
return *cond == NULL ? -1 : 0;
#else #else
InitializeConditionVariable(cond);
return 0;
#endif
}
////////////////// static inline void
// No threading // mythread_cond_destroy(mythread_cond *cond)
////////////////// {
#ifdef MYTHREAD_WIN95
CloseHandle(*cond);
#else
(void)cond;
#endif
}
#define mythread_sigmask(how, set, oset) \ static inline void
sigprocmask(how, set, oset) mythread_cond_signal(mythread_cond *cond)
{
#ifdef MYTHREAD_WIN95
SetEvent(*cond);
#else
WakeConditionVariable(cond);
#endif
}
#define mythread_once(func) \ static inline void
do { \ mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
static bool once_ = false; \ {
if (!once_) { \ #ifdef MYTHREAD_WIN95
func(); \ LeaveCriticalSection(mutex);
once_ = true; \ WaitForSingleObject(*cond, INFINITE);
} \ EnterCriticalSection(mutex);
} while (0) #else
BOOL ret = SleepConditionVariableCS(cond, mutex, INFINITE);
assert(ret);
(void)ret;
#endif
}
static inline int
mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
const mythread_condtime *condtime)
{
#ifdef MYTHREAD_WIN95
LeaveCriticalSection(mutex);
#endif
DWORD elapsed = GetTickCount() - condtime->start;
DWORD timeout = elapsed >= condtime->timeout
? 0 : condtime->timeout - elapsed;
#ifdef MYTHREAD_WIN95
DWORD ret = WaitForSingleObject(*cond, timeout);
assert(ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT);
EnterCriticalSection(mutex);
return ret == WAIT_TIMEOUT;
#else
BOOL ret = SleepConditionVariableCS(cond, mutex, timeout);
assert(ret || GetLastError() == ERROR_TIMEOUT);
return !ret;
#endif
}
static inline void
mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *con
d,
uint32_t timeout)
{
(void)cond;
condtime->start = GetTickCount();
condtime->timeout = timeout;
}
#endif #endif
#endif #endif
 End of changes. 33 change blocks. 
111 lines changed or deleted 403 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/