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