/////////////////////////////////////////////////////////////////////////////// // /// \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" // 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, line) #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 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 // //////////////////// #include #include #include #include #include #define MYTHREAD_RET_TYPE void * #define MYTHREAD_RET_VALUE NULL typedef pthread_t mythread; typedef pthread_mutex_t mythread_mutex; typedef struct { 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; typedef struct timespec mythread_condtime; // Calls the given function once in a thread-safe way. #define mythread_once(func) \ do { \ static pthread_once_t once_ = PTHREAD_ONCE_INIT; \ 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 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. // POSIX requires that all implementations of clock_gettime() must // support at least CLOCK_REALTIME. # endif mycond->clk_id = CLOCK_REALTIME; #endif return pthread_cond_init(&mycond->cond, NULL); } static inline void 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 *cond, uint32_t timeout_ms) { condtime->tv_sec = timeout_ms / 1000; condtime->tv_nsec = (timeout_ms % 1000) * 1000000; #ifdef HAVE_CLOCK_GETTIME struct timespec now; int ret = clock_gettime(cond->clk_id, &now); assert(ret == 0); (void)ret; condtime->tv_sec += now.tv_sec; condtime->tv_nsec += now.tv_nsec; #else (void)cond; struct timeval now; gettimeofday(&now, NULL); condtime->tv_sec += now.tv_sec; condtime->tv_nsec += now.tv_usec * 1000L; #endif // tv_nsec must stay in the range [0, 999_999_999]. if (condtime->tv_nsec >= 1000000000L) { condtime->tv_nsec -= 1000000000L; ++condtime->tv_sec; } } #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA) ///////////////////// // Windows threads // ///////////////////// #define WIN32_LEAN_AND_MEAN #ifdef MYTHREAD_VISTA # undef _WIN32_WINNT # define _WIN32_WINNT 0x0600 #endif #include #include #define MYTHREAD_RET_TYPE unsigned int __stdcall #define MYTHREAD_RET_VALUE 0 typedef HANDLE mythread; typedef CRITICAL_SECTION mythread_mutex; #ifdef MYTHREAD_WIN95 typedef HANDLE mythread_cond; #else typedef CONDITION_VARIABLE mythread_cond; #endif typedef struct { // 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; // mythread_once() is only available with Vista threads. #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 would // make no sense because the other POSIX signal functions are missing anyway. static inline int mythread_create(mythread *thread, unsigned int (__stdcall *func)(void *arg), void *arg) { uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL); if (ret == 0) return -1; *thread = (HANDLE)ret; return 0; } 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; } 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 InitializeConditionVariable(cond); return 0; #endif } static inline void mythread_cond_destroy(mythread_cond *cond) { #ifdef MYTHREAD_WIN95 CloseHandle(*cond); #else (void)cond; #endif } static inline void mythread_cond_signal(mythread_cond *cond) { #ifdef MYTHREAD_WIN95 SetEvent(*cond); #else WakeConditionVariable(cond); #endif } static inline void mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex) { #ifdef MYTHREAD_WIN95 LeaveCriticalSection(mutex); WaitForSingleObject(*cond, INFINITE); EnterCriticalSection(mutex); #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 *cond, uint32_t timeout) { (void)cond; condtime->start = GetTickCount(); condtime->timeout = timeout; } #endif #endif