15a645f22SBen Gras ///////////////////////////////////////////////////////////////////////////////
25a645f22SBen Gras //
35a645f22SBen Gras /// \file mythread.h
4*0a6a1f1dSLionel Sambuc /// \brief Some threading related helper macros and functions
55a645f22SBen Gras //
65a645f22SBen Gras // Author: Lasse Collin
75a645f22SBen Gras //
85a645f22SBen Gras // This file has been put into the public domain.
95a645f22SBen Gras // You can do whatever you want with this file.
105a645f22SBen Gras //
115a645f22SBen Gras ///////////////////////////////////////////////////////////////////////////////
125a645f22SBen Gras
13*0a6a1f1dSLionel Sambuc #ifndef MYTHREAD_H
14*0a6a1f1dSLionel Sambuc #define MYTHREAD_H
15*0a6a1f1dSLionel Sambuc
165a645f22SBen Gras #include "sysdefs.h"
175a645f22SBen Gras
18*0a6a1f1dSLionel Sambuc // If any type of threading is enabled, #define MYTHREAD_ENABLED.
19*0a6a1f1dSLionel Sambuc #if defined(MYTHREAD_POSIX) || defined(MYTHREAD_WIN95) \
20*0a6a1f1dSLionel Sambuc || defined(MYTHREAD_VISTA)
21*0a6a1f1dSLionel Sambuc # define MYTHREAD_ENABLED 1
22*0a6a1f1dSLionel Sambuc #endif
235a645f22SBen Gras
245a645f22SBen Gras
25*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_ENABLED
265a645f22SBen Gras
27*0a6a1f1dSLionel Sambuc ////////////////////////////////////////
28*0a6a1f1dSLionel Sambuc // Shared between all threading types //
29*0a6a1f1dSLionel Sambuc ////////////////////////////////////////
305a645f22SBen Gras
31*0a6a1f1dSLionel Sambuc // Locks a mutex for a duration of a block.
32*0a6a1f1dSLionel Sambuc //
33*0a6a1f1dSLionel Sambuc // Perform mythread_mutex_lock(&mutex) in the beginning of a block
34*0a6a1f1dSLionel Sambuc // and mythread_mutex_unlock(&mutex) at the end of the block. "break"
35*0a6a1f1dSLionel Sambuc // may be used to unlock the mutex and jump out of the block.
36*0a6a1f1dSLionel Sambuc // mythread_sync blocks may be nested.
37*0a6a1f1dSLionel Sambuc //
38*0a6a1f1dSLionel Sambuc // Example:
39*0a6a1f1dSLionel Sambuc //
40*0a6a1f1dSLionel Sambuc // mythread_sync(mutex) {
41*0a6a1f1dSLionel Sambuc // foo();
42*0a6a1f1dSLionel Sambuc // if (some_error)
43*0a6a1f1dSLionel Sambuc // break; // Skips bar()
44*0a6a1f1dSLionel Sambuc // bar();
45*0a6a1f1dSLionel Sambuc // }
46*0a6a1f1dSLionel Sambuc //
47*0a6a1f1dSLionel Sambuc // At least GCC optimizes the loops completely away so it doesn't slow
48*0a6a1f1dSLionel Sambuc // things down at all compared to plain mythread_mutex_lock(&mutex)
49*0a6a1f1dSLionel Sambuc // and mythread_mutex_unlock(&mutex) calls.
50*0a6a1f1dSLionel Sambuc //
51*0a6a1f1dSLionel Sambuc #define mythread_sync(mutex) mythread_sync_helper1(mutex, __LINE__)
52*0a6a1f1dSLionel Sambuc #define mythread_sync_helper1(mutex, line) mythread_sync_helper2(mutex, line)
53*0a6a1f1dSLionel Sambuc #define mythread_sync_helper2(mutex, line) \
54*0a6a1f1dSLionel Sambuc for (unsigned int mythread_i_ ## line = 0; \
55*0a6a1f1dSLionel Sambuc mythread_i_ ## line \
56*0a6a1f1dSLionel Sambuc ? (mythread_mutex_unlock(&(mutex)), 0) \
57*0a6a1f1dSLionel Sambuc : (mythread_mutex_lock(&(mutex)), 1); \
58*0a6a1f1dSLionel Sambuc mythread_i_ ## line = 1) \
59*0a6a1f1dSLionel Sambuc for (unsigned int mythread_j_ ## line = 0; \
60*0a6a1f1dSLionel Sambuc !mythread_j_ ## line; \
61*0a6a1f1dSLionel Sambuc mythread_j_ ## line = 1)
62*0a6a1f1dSLionel Sambuc #endif
635a645f22SBen Gras
64*0a6a1f1dSLionel Sambuc
65*0a6a1f1dSLionel Sambuc #if !defined(MYTHREAD_ENABLED)
66*0a6a1f1dSLionel Sambuc
67*0a6a1f1dSLionel Sambuc //////////////////
68*0a6a1f1dSLionel Sambuc // No threading //
69*0a6a1f1dSLionel Sambuc //////////////////
70*0a6a1f1dSLionel Sambuc
71*0a6a1f1dSLionel Sambuc // Calls the given function once. This isn't thread safe.
725a645f22SBen Gras #define mythread_once(func) \
735a645f22SBen Gras do { \
745a645f22SBen Gras static bool once_ = false; \
755a645f22SBen Gras if (!once_) { \
765a645f22SBen Gras func(); \
775a645f22SBen Gras once_ = true; \
785a645f22SBen Gras } \
795a645f22SBen Gras } while (0)
805a645f22SBen Gras
81*0a6a1f1dSLionel Sambuc
82*0a6a1f1dSLionel Sambuc #if !(defined(_WIN32) && !defined(__CYGWIN__))
83*0a6a1f1dSLionel Sambuc // Use sigprocmask() to set the signal mask in single-threaded programs.
84*0a6a1f1dSLionel Sambuc #include <signal.h>
85*0a6a1f1dSLionel Sambuc
86*0a6a1f1dSLionel Sambuc static inline void
mythread_sigmask(int how,const sigset_t * restrict set,sigset_t * restrict oset)87*0a6a1f1dSLionel Sambuc mythread_sigmask(int how, const sigset_t *restrict set,
88*0a6a1f1dSLionel Sambuc sigset_t *restrict oset)
89*0a6a1f1dSLionel Sambuc {
90*0a6a1f1dSLionel Sambuc int ret = sigprocmask(how, set, oset);
91*0a6a1f1dSLionel Sambuc assert(ret == 0);
92*0a6a1f1dSLionel Sambuc (void)ret;
93*0a6a1f1dSLionel Sambuc }
94*0a6a1f1dSLionel Sambuc #endif
95*0a6a1f1dSLionel Sambuc
96*0a6a1f1dSLionel Sambuc
97*0a6a1f1dSLionel Sambuc #elif defined(MYTHREAD_POSIX)
98*0a6a1f1dSLionel Sambuc
99*0a6a1f1dSLionel Sambuc ////////////////////
100*0a6a1f1dSLionel Sambuc // Using pthreads //
101*0a6a1f1dSLionel Sambuc ////////////////////
102*0a6a1f1dSLionel Sambuc
103*0a6a1f1dSLionel Sambuc #include <sys/time.h>
104*0a6a1f1dSLionel Sambuc #include <pthread.h>
105*0a6a1f1dSLionel Sambuc #include <signal.h>
106*0a6a1f1dSLionel Sambuc #include <time.h>
107*0a6a1f1dSLionel Sambuc #include <errno.h>
108*0a6a1f1dSLionel Sambuc
109*0a6a1f1dSLionel Sambuc #define MYTHREAD_RET_TYPE void *
110*0a6a1f1dSLionel Sambuc #define MYTHREAD_RET_VALUE NULL
111*0a6a1f1dSLionel Sambuc
112*0a6a1f1dSLionel Sambuc typedef pthread_t mythread;
113*0a6a1f1dSLionel Sambuc typedef pthread_mutex_t mythread_mutex;
114*0a6a1f1dSLionel Sambuc
115*0a6a1f1dSLionel Sambuc typedef struct {
116*0a6a1f1dSLionel Sambuc pthread_cond_t cond;
117*0a6a1f1dSLionel Sambuc #ifdef HAVE_CLOCK_GETTIME
118*0a6a1f1dSLionel Sambuc // Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
119*0a6a1f1dSLionel Sambuc // the condition variable.
120*0a6a1f1dSLionel Sambuc clockid_t clk_id;
121*0a6a1f1dSLionel Sambuc #endif
122*0a6a1f1dSLionel Sambuc } mythread_cond;
123*0a6a1f1dSLionel Sambuc
124*0a6a1f1dSLionel Sambuc typedef struct timespec mythread_condtime;
125*0a6a1f1dSLionel Sambuc
126*0a6a1f1dSLionel Sambuc
127*0a6a1f1dSLionel Sambuc // Calls the given function once in a thread-safe way.
128*0a6a1f1dSLionel Sambuc #define mythread_once(func) \
129*0a6a1f1dSLionel Sambuc do { \
130*0a6a1f1dSLionel Sambuc static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
131*0a6a1f1dSLionel Sambuc pthread_once(&once_, &func); \
132*0a6a1f1dSLionel Sambuc } while (0)
133*0a6a1f1dSLionel Sambuc
134*0a6a1f1dSLionel Sambuc
135*0a6a1f1dSLionel Sambuc // Use pthread_sigmask() to set the signal mask in multi-threaded programs.
136*0a6a1f1dSLionel Sambuc // Do nothing on OpenVMS since it lacks pthread_sigmask().
137*0a6a1f1dSLionel Sambuc static inline void
mythread_sigmask(int how,const sigset_t * restrict set,sigset_t * restrict oset)138*0a6a1f1dSLionel Sambuc mythread_sigmask(int how, const sigset_t *restrict set,
139*0a6a1f1dSLionel Sambuc sigset_t *restrict oset)
140*0a6a1f1dSLionel Sambuc {
141*0a6a1f1dSLionel Sambuc #ifdef __VMS
142*0a6a1f1dSLionel Sambuc (void)how;
143*0a6a1f1dSLionel Sambuc (void)set;
144*0a6a1f1dSLionel Sambuc (void)oset;
145*0a6a1f1dSLionel Sambuc #else
146*0a6a1f1dSLionel Sambuc int ret = pthread_sigmask(how, set, oset);
147*0a6a1f1dSLionel Sambuc assert(ret == 0);
148*0a6a1f1dSLionel Sambuc (void)ret;
149*0a6a1f1dSLionel Sambuc #endif
150*0a6a1f1dSLionel Sambuc }
151*0a6a1f1dSLionel Sambuc
152*0a6a1f1dSLionel Sambuc
153*0a6a1f1dSLionel Sambuc // Creates a new thread with all signals blocked. Returns zero on success
154*0a6a1f1dSLionel Sambuc // and non-zero on error.
155*0a6a1f1dSLionel Sambuc static inline int
mythread_create(mythread * thread,void * (* func)(void * arg),void * arg)156*0a6a1f1dSLionel Sambuc mythread_create(mythread *thread, void *(*func)(void *arg), void *arg)
157*0a6a1f1dSLionel Sambuc {
158*0a6a1f1dSLionel Sambuc sigset_t old;
159*0a6a1f1dSLionel Sambuc sigset_t all;
160*0a6a1f1dSLionel Sambuc sigfillset(&all);
161*0a6a1f1dSLionel Sambuc
162*0a6a1f1dSLionel Sambuc mythread_sigmask(SIG_SETMASK, &all, &old);
163*0a6a1f1dSLionel Sambuc const int ret = pthread_create(thread, NULL, func, arg);
164*0a6a1f1dSLionel Sambuc mythread_sigmask(SIG_SETMASK, &old, NULL);
165*0a6a1f1dSLionel Sambuc
166*0a6a1f1dSLionel Sambuc return ret;
167*0a6a1f1dSLionel Sambuc }
168*0a6a1f1dSLionel Sambuc
169*0a6a1f1dSLionel Sambuc // Joins a thread. Returns zero on success and non-zero on error.
170*0a6a1f1dSLionel Sambuc static inline int
mythread_join(mythread thread)171*0a6a1f1dSLionel Sambuc mythread_join(mythread thread)
172*0a6a1f1dSLionel Sambuc {
173*0a6a1f1dSLionel Sambuc return pthread_join(thread, NULL);
174*0a6a1f1dSLionel Sambuc }
175*0a6a1f1dSLionel Sambuc
176*0a6a1f1dSLionel Sambuc
177*0a6a1f1dSLionel Sambuc // Initiatlizes a mutex. Returns zero on success and non-zero on error.
178*0a6a1f1dSLionel Sambuc static inline int
mythread_mutex_init(mythread_mutex * mutex)179*0a6a1f1dSLionel Sambuc mythread_mutex_init(mythread_mutex *mutex)
180*0a6a1f1dSLionel Sambuc {
181*0a6a1f1dSLionel Sambuc return pthread_mutex_init(mutex, NULL);
182*0a6a1f1dSLionel Sambuc }
183*0a6a1f1dSLionel Sambuc
184*0a6a1f1dSLionel Sambuc static inline void
mythread_mutex_destroy(mythread_mutex * mutex)185*0a6a1f1dSLionel Sambuc mythread_mutex_destroy(mythread_mutex *mutex)
186*0a6a1f1dSLionel Sambuc {
187*0a6a1f1dSLionel Sambuc int ret = pthread_mutex_destroy(mutex);
188*0a6a1f1dSLionel Sambuc assert(ret == 0);
189*0a6a1f1dSLionel Sambuc (void)ret;
190*0a6a1f1dSLionel Sambuc }
191*0a6a1f1dSLionel Sambuc
192*0a6a1f1dSLionel Sambuc static inline void
mythread_mutex_lock(mythread_mutex * mutex)193*0a6a1f1dSLionel Sambuc mythread_mutex_lock(mythread_mutex *mutex)
194*0a6a1f1dSLionel Sambuc {
195*0a6a1f1dSLionel Sambuc int ret = pthread_mutex_lock(mutex);
196*0a6a1f1dSLionel Sambuc assert(ret == 0);
197*0a6a1f1dSLionel Sambuc (void)ret;
198*0a6a1f1dSLionel Sambuc }
199*0a6a1f1dSLionel Sambuc
200*0a6a1f1dSLionel Sambuc static inline void
mythread_mutex_unlock(mythread_mutex * mutex)201*0a6a1f1dSLionel Sambuc mythread_mutex_unlock(mythread_mutex *mutex)
202*0a6a1f1dSLionel Sambuc {
203*0a6a1f1dSLionel Sambuc int ret = pthread_mutex_unlock(mutex);
204*0a6a1f1dSLionel Sambuc assert(ret == 0);
205*0a6a1f1dSLionel Sambuc (void)ret;
206*0a6a1f1dSLionel Sambuc }
207*0a6a1f1dSLionel Sambuc
208*0a6a1f1dSLionel Sambuc
209*0a6a1f1dSLionel Sambuc // Initializes a condition variable.
210*0a6a1f1dSLionel Sambuc //
211*0a6a1f1dSLionel Sambuc // Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the
212*0a6a1f1dSLionel Sambuc // timeout in pthread_cond_timedwait() work correctly also if system time
213*0a6a1f1dSLionel Sambuc // is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available
214*0a6a1f1dSLionel Sambuc // everywhere while the default CLOCK_REALTIME is, so the default is
215*0a6a1f1dSLionel Sambuc // used if CLOCK_MONOTONIC isn't available.
216*0a6a1f1dSLionel Sambuc //
217*0a6a1f1dSLionel Sambuc // If clock_gettime() isn't available at all, gettimeofday() will be used.
218*0a6a1f1dSLionel Sambuc static inline int
mythread_cond_init(mythread_cond * mycond)219*0a6a1f1dSLionel Sambuc mythread_cond_init(mythread_cond *mycond)
220*0a6a1f1dSLionel Sambuc {
221*0a6a1f1dSLionel Sambuc #ifdef HAVE_CLOCK_GETTIME
222*0a6a1f1dSLionel Sambuc // NOTE: HAVE_DECL_CLOCK_MONOTONIC is always defined to 0 or 1.
223*0a6a1f1dSLionel Sambuc # if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && HAVE_DECL_CLOCK_MONOTONIC
224*0a6a1f1dSLionel Sambuc struct timespec ts;
225*0a6a1f1dSLionel Sambuc pthread_condattr_t condattr;
226*0a6a1f1dSLionel Sambuc
227*0a6a1f1dSLionel Sambuc // POSIX doesn't seem to *require* that pthread_condattr_setclock()
228*0a6a1f1dSLionel Sambuc // will fail if given an unsupported clock ID. Test that
229*0a6a1f1dSLionel Sambuc // CLOCK_MONOTONIC really is supported using clock_gettime().
230*0a6a1f1dSLionel Sambuc if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0
231*0a6a1f1dSLionel Sambuc && pthread_condattr_init(&condattr) == 0) {
232*0a6a1f1dSLionel Sambuc int ret = pthread_condattr_setclock(
233*0a6a1f1dSLionel Sambuc &condattr, CLOCK_MONOTONIC);
234*0a6a1f1dSLionel Sambuc if (ret == 0)
235*0a6a1f1dSLionel Sambuc ret = pthread_cond_init(&mycond->cond, &condattr);
236*0a6a1f1dSLionel Sambuc
237*0a6a1f1dSLionel Sambuc pthread_condattr_destroy(&condattr);
238*0a6a1f1dSLionel Sambuc
239*0a6a1f1dSLionel Sambuc if (ret == 0) {
240*0a6a1f1dSLionel Sambuc mycond->clk_id = CLOCK_MONOTONIC;
241*0a6a1f1dSLionel Sambuc return 0;
242*0a6a1f1dSLionel Sambuc }
243*0a6a1f1dSLionel Sambuc }
244*0a6a1f1dSLionel Sambuc
245*0a6a1f1dSLionel Sambuc // If anything above fails, fall back to the default CLOCK_REALTIME.
246*0a6a1f1dSLionel Sambuc // POSIX requires that all implementations of clock_gettime() must
247*0a6a1f1dSLionel Sambuc // support at least CLOCK_REALTIME.
248*0a6a1f1dSLionel Sambuc # endif
249*0a6a1f1dSLionel Sambuc
250*0a6a1f1dSLionel Sambuc mycond->clk_id = CLOCK_REALTIME;
251*0a6a1f1dSLionel Sambuc #endif
252*0a6a1f1dSLionel Sambuc
253*0a6a1f1dSLionel Sambuc return pthread_cond_init(&mycond->cond, NULL);
254*0a6a1f1dSLionel Sambuc }
255*0a6a1f1dSLionel Sambuc
256*0a6a1f1dSLionel Sambuc static inline void
mythread_cond_destroy(mythread_cond * cond)257*0a6a1f1dSLionel Sambuc mythread_cond_destroy(mythread_cond *cond)
258*0a6a1f1dSLionel Sambuc {
259*0a6a1f1dSLionel Sambuc int ret = pthread_cond_destroy(&cond->cond);
260*0a6a1f1dSLionel Sambuc assert(ret == 0);
261*0a6a1f1dSLionel Sambuc (void)ret;
262*0a6a1f1dSLionel Sambuc }
263*0a6a1f1dSLionel Sambuc
264*0a6a1f1dSLionel Sambuc static inline void
mythread_cond_signal(mythread_cond * cond)265*0a6a1f1dSLionel Sambuc mythread_cond_signal(mythread_cond *cond)
266*0a6a1f1dSLionel Sambuc {
267*0a6a1f1dSLionel Sambuc int ret = pthread_cond_signal(&cond->cond);
268*0a6a1f1dSLionel Sambuc assert(ret == 0);
269*0a6a1f1dSLionel Sambuc (void)ret;
270*0a6a1f1dSLionel Sambuc }
271*0a6a1f1dSLionel Sambuc
272*0a6a1f1dSLionel Sambuc static inline void
mythread_cond_wait(mythread_cond * cond,mythread_mutex * mutex)273*0a6a1f1dSLionel Sambuc mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
274*0a6a1f1dSLionel Sambuc {
275*0a6a1f1dSLionel Sambuc int ret = pthread_cond_wait(&cond->cond, mutex);
276*0a6a1f1dSLionel Sambuc assert(ret == 0);
277*0a6a1f1dSLionel Sambuc (void)ret;
278*0a6a1f1dSLionel Sambuc }
279*0a6a1f1dSLionel Sambuc
280*0a6a1f1dSLionel Sambuc // Waits on a condition or until a timeout expires. If the timeout expires,
281*0a6a1f1dSLionel Sambuc // non-zero is returned, otherwise zero is returned.
282*0a6a1f1dSLionel Sambuc static inline int
mythread_cond_timedwait(mythread_cond * cond,mythread_mutex * mutex,const mythread_condtime * condtime)283*0a6a1f1dSLionel Sambuc mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
284*0a6a1f1dSLionel Sambuc const mythread_condtime *condtime)
285*0a6a1f1dSLionel Sambuc {
286*0a6a1f1dSLionel Sambuc int ret = pthread_cond_timedwait(&cond->cond, mutex, condtime);
287*0a6a1f1dSLionel Sambuc assert(ret == 0 || ret == ETIMEDOUT);
288*0a6a1f1dSLionel Sambuc return ret;
289*0a6a1f1dSLionel Sambuc }
290*0a6a1f1dSLionel Sambuc
291*0a6a1f1dSLionel Sambuc // Sets condtime to the absolute time that is timeout_ms milliseconds
292*0a6a1f1dSLionel Sambuc // in the future. The type of the clock to use is taken from cond.
293*0a6a1f1dSLionel Sambuc static inline void
mythread_condtime_set(mythread_condtime * condtime,const mythread_cond * cond,uint32_t timeout_ms)294*0a6a1f1dSLionel Sambuc mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
295*0a6a1f1dSLionel Sambuc uint32_t timeout_ms)
296*0a6a1f1dSLionel Sambuc {
297*0a6a1f1dSLionel Sambuc condtime->tv_sec = timeout_ms / 1000;
298*0a6a1f1dSLionel Sambuc condtime->tv_nsec = (timeout_ms % 1000) * 1000000;
299*0a6a1f1dSLionel Sambuc
300*0a6a1f1dSLionel Sambuc #ifdef HAVE_CLOCK_GETTIME
301*0a6a1f1dSLionel Sambuc struct timespec now;
302*0a6a1f1dSLionel Sambuc int ret = clock_gettime(cond->clk_id, &now);
303*0a6a1f1dSLionel Sambuc assert(ret == 0);
304*0a6a1f1dSLionel Sambuc (void)ret;
305*0a6a1f1dSLionel Sambuc
306*0a6a1f1dSLionel Sambuc condtime->tv_sec += now.tv_sec;
307*0a6a1f1dSLionel Sambuc condtime->tv_nsec += now.tv_nsec;
308*0a6a1f1dSLionel Sambuc #else
309*0a6a1f1dSLionel Sambuc (void)cond;
310*0a6a1f1dSLionel Sambuc
311*0a6a1f1dSLionel Sambuc struct timeval now;
312*0a6a1f1dSLionel Sambuc gettimeofday(&now, NULL);
313*0a6a1f1dSLionel Sambuc
314*0a6a1f1dSLionel Sambuc condtime->tv_sec += now.tv_sec;
315*0a6a1f1dSLionel Sambuc condtime->tv_nsec += now.tv_usec * 1000L;
316*0a6a1f1dSLionel Sambuc #endif
317*0a6a1f1dSLionel Sambuc
318*0a6a1f1dSLionel Sambuc // tv_nsec must stay in the range [0, 999_999_999].
319*0a6a1f1dSLionel Sambuc if (condtime->tv_nsec >= 1000000000L) {
320*0a6a1f1dSLionel Sambuc condtime->tv_nsec -= 1000000000L;
321*0a6a1f1dSLionel Sambuc ++condtime->tv_sec;
322*0a6a1f1dSLionel Sambuc }
323*0a6a1f1dSLionel Sambuc }
324*0a6a1f1dSLionel Sambuc
325*0a6a1f1dSLionel Sambuc
326*0a6a1f1dSLionel Sambuc #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA)
327*0a6a1f1dSLionel Sambuc
328*0a6a1f1dSLionel Sambuc /////////////////////
329*0a6a1f1dSLionel Sambuc // Windows threads //
330*0a6a1f1dSLionel Sambuc /////////////////////
331*0a6a1f1dSLionel Sambuc
332*0a6a1f1dSLionel Sambuc #define WIN32_LEAN_AND_MEAN
333*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_VISTA
334*0a6a1f1dSLionel Sambuc # undef _WIN32_WINNT
335*0a6a1f1dSLionel Sambuc # define _WIN32_WINNT 0x0600
336*0a6a1f1dSLionel Sambuc #endif
337*0a6a1f1dSLionel Sambuc #include <windows.h>
338*0a6a1f1dSLionel Sambuc #include <process.h>
339*0a6a1f1dSLionel Sambuc
340*0a6a1f1dSLionel Sambuc #define MYTHREAD_RET_TYPE unsigned int __stdcall
341*0a6a1f1dSLionel Sambuc #define MYTHREAD_RET_VALUE 0
342*0a6a1f1dSLionel Sambuc
343*0a6a1f1dSLionel Sambuc typedef HANDLE mythread;
344*0a6a1f1dSLionel Sambuc typedef CRITICAL_SECTION mythread_mutex;
345*0a6a1f1dSLionel Sambuc
346*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
347*0a6a1f1dSLionel Sambuc typedef HANDLE mythread_cond;
348*0a6a1f1dSLionel Sambuc #else
349*0a6a1f1dSLionel Sambuc typedef CONDITION_VARIABLE mythread_cond;
350*0a6a1f1dSLionel Sambuc #endif
351*0a6a1f1dSLionel Sambuc
352*0a6a1f1dSLionel Sambuc typedef struct {
353*0a6a1f1dSLionel Sambuc // Tick count (milliseconds) in the beginning of the timeout.
354*0a6a1f1dSLionel Sambuc // NOTE: This is 32 bits so it wraps around after 49.7 days.
355*0a6a1f1dSLionel Sambuc // Multi-day timeouts may not work as expected.
356*0a6a1f1dSLionel Sambuc DWORD start;
357*0a6a1f1dSLionel Sambuc
358*0a6a1f1dSLionel Sambuc // Length of the timeout in milliseconds. The timeout expires
359*0a6a1f1dSLionel Sambuc // when the current tick count minus "start" is equal or greater
360*0a6a1f1dSLionel Sambuc // than "timeout".
361*0a6a1f1dSLionel Sambuc DWORD timeout;
362*0a6a1f1dSLionel Sambuc } mythread_condtime;
363*0a6a1f1dSLionel Sambuc
364*0a6a1f1dSLionel Sambuc
365*0a6a1f1dSLionel Sambuc // mythread_once() is only available with Vista threads.
366*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_VISTA
367*0a6a1f1dSLionel Sambuc #define mythread_once(func) \
368*0a6a1f1dSLionel Sambuc do { \
369*0a6a1f1dSLionel Sambuc static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \
370*0a6a1f1dSLionel Sambuc BOOL pending_; \
371*0a6a1f1dSLionel Sambuc if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \
372*0a6a1f1dSLionel Sambuc abort(); \
373*0a6a1f1dSLionel Sambuc if (pending_) \
374*0a6a1f1dSLionel Sambuc func(); \
375*0a6a1f1dSLionel Sambuc if (!InitOnceComplete(&once, 0, NULL)) \
376*0a6a1f1dSLionel Sambuc abort(); \
377*0a6a1f1dSLionel Sambuc } while (0)
378*0a6a1f1dSLionel Sambuc #endif
379*0a6a1f1dSLionel Sambuc
380*0a6a1f1dSLionel Sambuc
381*0a6a1f1dSLionel Sambuc // mythread_sigmask() isn't available on Windows. Even a dummy version would
382*0a6a1f1dSLionel Sambuc // make no sense because the other POSIX signal functions are missing anyway.
383*0a6a1f1dSLionel Sambuc
384*0a6a1f1dSLionel Sambuc
385*0a6a1f1dSLionel Sambuc static inline int
mythread_create(mythread * thread,unsigned int (__stdcall * func)(void * arg),void * arg)386*0a6a1f1dSLionel Sambuc mythread_create(mythread *thread,
387*0a6a1f1dSLionel Sambuc unsigned int (__stdcall *func)(void *arg), void *arg)
388*0a6a1f1dSLionel Sambuc {
389*0a6a1f1dSLionel Sambuc uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL);
390*0a6a1f1dSLionel Sambuc if (ret == 0)
391*0a6a1f1dSLionel Sambuc return -1;
392*0a6a1f1dSLionel Sambuc
393*0a6a1f1dSLionel Sambuc *thread = (HANDLE)ret;
394*0a6a1f1dSLionel Sambuc return 0;
395*0a6a1f1dSLionel Sambuc }
396*0a6a1f1dSLionel Sambuc
397*0a6a1f1dSLionel Sambuc static inline int
mythread_join(mythread thread)398*0a6a1f1dSLionel Sambuc mythread_join(mythread thread)
399*0a6a1f1dSLionel Sambuc {
400*0a6a1f1dSLionel Sambuc int ret = 0;
401*0a6a1f1dSLionel Sambuc
402*0a6a1f1dSLionel Sambuc if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
403*0a6a1f1dSLionel Sambuc ret = -1;
404*0a6a1f1dSLionel Sambuc
405*0a6a1f1dSLionel Sambuc if (!CloseHandle(thread))
406*0a6a1f1dSLionel Sambuc ret = -1;
407*0a6a1f1dSLionel Sambuc
408*0a6a1f1dSLionel Sambuc return ret;
409*0a6a1f1dSLionel Sambuc }
410*0a6a1f1dSLionel Sambuc
411*0a6a1f1dSLionel Sambuc
412*0a6a1f1dSLionel Sambuc static inline int
mythread_mutex_init(mythread_mutex * mutex)413*0a6a1f1dSLionel Sambuc mythread_mutex_init(mythread_mutex *mutex)
414*0a6a1f1dSLionel Sambuc {
415*0a6a1f1dSLionel Sambuc InitializeCriticalSection(mutex);
416*0a6a1f1dSLionel Sambuc return 0;
417*0a6a1f1dSLionel Sambuc }
418*0a6a1f1dSLionel Sambuc
419*0a6a1f1dSLionel Sambuc static inline void
mythread_mutex_destroy(mythread_mutex * mutex)420*0a6a1f1dSLionel Sambuc mythread_mutex_destroy(mythread_mutex *mutex)
421*0a6a1f1dSLionel Sambuc {
422*0a6a1f1dSLionel Sambuc DeleteCriticalSection(mutex);
423*0a6a1f1dSLionel Sambuc }
424*0a6a1f1dSLionel Sambuc
425*0a6a1f1dSLionel Sambuc static inline void
mythread_mutex_lock(mythread_mutex * mutex)426*0a6a1f1dSLionel Sambuc mythread_mutex_lock(mythread_mutex *mutex)
427*0a6a1f1dSLionel Sambuc {
428*0a6a1f1dSLionel Sambuc EnterCriticalSection(mutex);
429*0a6a1f1dSLionel Sambuc }
430*0a6a1f1dSLionel Sambuc
431*0a6a1f1dSLionel Sambuc static inline void
mythread_mutex_unlock(mythread_mutex * mutex)432*0a6a1f1dSLionel Sambuc mythread_mutex_unlock(mythread_mutex *mutex)
433*0a6a1f1dSLionel Sambuc {
434*0a6a1f1dSLionel Sambuc LeaveCriticalSection(mutex);
435*0a6a1f1dSLionel Sambuc }
436*0a6a1f1dSLionel Sambuc
437*0a6a1f1dSLionel Sambuc
438*0a6a1f1dSLionel Sambuc static inline int
mythread_cond_init(mythread_cond * cond)439*0a6a1f1dSLionel Sambuc mythread_cond_init(mythread_cond *cond)
440*0a6a1f1dSLionel Sambuc {
441*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
442*0a6a1f1dSLionel Sambuc *cond = CreateEvent(NULL, FALSE, FALSE, NULL);
443*0a6a1f1dSLionel Sambuc return *cond == NULL ? -1 : 0;
444*0a6a1f1dSLionel Sambuc #else
445*0a6a1f1dSLionel Sambuc InitializeConditionVariable(cond);
446*0a6a1f1dSLionel Sambuc return 0;
447*0a6a1f1dSLionel Sambuc #endif
448*0a6a1f1dSLionel Sambuc }
449*0a6a1f1dSLionel Sambuc
450*0a6a1f1dSLionel Sambuc static inline void
mythread_cond_destroy(mythread_cond * cond)451*0a6a1f1dSLionel Sambuc mythread_cond_destroy(mythread_cond *cond)
452*0a6a1f1dSLionel Sambuc {
453*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
454*0a6a1f1dSLionel Sambuc CloseHandle(*cond);
455*0a6a1f1dSLionel Sambuc #else
456*0a6a1f1dSLionel Sambuc (void)cond;
457*0a6a1f1dSLionel Sambuc #endif
458*0a6a1f1dSLionel Sambuc }
459*0a6a1f1dSLionel Sambuc
460*0a6a1f1dSLionel Sambuc static inline void
mythread_cond_signal(mythread_cond * cond)461*0a6a1f1dSLionel Sambuc mythread_cond_signal(mythread_cond *cond)
462*0a6a1f1dSLionel Sambuc {
463*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
464*0a6a1f1dSLionel Sambuc SetEvent(*cond);
465*0a6a1f1dSLionel Sambuc #else
466*0a6a1f1dSLionel Sambuc WakeConditionVariable(cond);
467*0a6a1f1dSLionel Sambuc #endif
468*0a6a1f1dSLionel Sambuc }
469*0a6a1f1dSLionel Sambuc
470*0a6a1f1dSLionel Sambuc static inline void
mythread_cond_wait(mythread_cond * cond,mythread_mutex * mutex)471*0a6a1f1dSLionel Sambuc mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
472*0a6a1f1dSLionel Sambuc {
473*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
474*0a6a1f1dSLionel Sambuc LeaveCriticalSection(mutex);
475*0a6a1f1dSLionel Sambuc WaitForSingleObject(*cond, INFINITE);
476*0a6a1f1dSLionel Sambuc EnterCriticalSection(mutex);
477*0a6a1f1dSLionel Sambuc #else
478*0a6a1f1dSLionel Sambuc BOOL ret = SleepConditionVariableCS(cond, mutex, INFINITE);
479*0a6a1f1dSLionel Sambuc assert(ret);
480*0a6a1f1dSLionel Sambuc (void)ret;
481*0a6a1f1dSLionel Sambuc #endif
482*0a6a1f1dSLionel Sambuc }
483*0a6a1f1dSLionel Sambuc
484*0a6a1f1dSLionel Sambuc static inline int
mythread_cond_timedwait(mythread_cond * cond,mythread_mutex * mutex,const mythread_condtime * condtime)485*0a6a1f1dSLionel Sambuc mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
486*0a6a1f1dSLionel Sambuc const mythread_condtime *condtime)
487*0a6a1f1dSLionel Sambuc {
488*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
489*0a6a1f1dSLionel Sambuc LeaveCriticalSection(mutex);
490*0a6a1f1dSLionel Sambuc #endif
491*0a6a1f1dSLionel Sambuc
492*0a6a1f1dSLionel Sambuc DWORD elapsed = GetTickCount() - condtime->start;
493*0a6a1f1dSLionel Sambuc DWORD timeout = elapsed >= condtime->timeout
494*0a6a1f1dSLionel Sambuc ? 0 : condtime->timeout - elapsed;
495*0a6a1f1dSLionel Sambuc
496*0a6a1f1dSLionel Sambuc #ifdef MYTHREAD_WIN95
497*0a6a1f1dSLionel Sambuc DWORD ret = WaitForSingleObject(*cond, timeout);
498*0a6a1f1dSLionel Sambuc assert(ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT);
499*0a6a1f1dSLionel Sambuc
500*0a6a1f1dSLionel Sambuc EnterCriticalSection(mutex);
501*0a6a1f1dSLionel Sambuc
502*0a6a1f1dSLionel Sambuc return ret == WAIT_TIMEOUT;
503*0a6a1f1dSLionel Sambuc #else
504*0a6a1f1dSLionel Sambuc BOOL ret = SleepConditionVariableCS(cond, mutex, timeout);
505*0a6a1f1dSLionel Sambuc assert(ret || GetLastError() == ERROR_TIMEOUT);
506*0a6a1f1dSLionel Sambuc return !ret;
507*0a6a1f1dSLionel Sambuc #endif
508*0a6a1f1dSLionel Sambuc }
509*0a6a1f1dSLionel Sambuc
510*0a6a1f1dSLionel Sambuc static inline void
mythread_condtime_set(mythread_condtime * condtime,const mythread_cond * cond,uint32_t timeout)511*0a6a1f1dSLionel Sambuc mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
512*0a6a1f1dSLionel Sambuc uint32_t timeout)
513*0a6a1f1dSLionel Sambuc {
514*0a6a1f1dSLionel Sambuc (void)cond;
515*0a6a1f1dSLionel Sambuc condtime->start = GetTickCount();
516*0a6a1f1dSLionel Sambuc condtime->timeout = timeout;
517*0a6a1f1dSLionel Sambuc }
518*0a6a1f1dSLionel Sambuc
519*0a6a1f1dSLionel Sambuc #endif
5205a645f22SBen Gras
5215a645f22SBen Gras #endif
522