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
mythread_sigmask(int how,const sigset_t * restrict set,sigset_t * restrict oset)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
mythread_sigmask(int how,const sigset_t * restrict set,sigset_t * restrict oset)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
mythread_create(mythread * thread,void * (* func)(void * arg),void * arg)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
mythread_join(mythread thread)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
mythread_mutex_init(mythread_mutex * mutex)179 mythread_mutex_init(mythread_mutex *mutex)
180 {
181 	return pthread_mutex_init(mutex, NULL);
182 }
183 
184 static inline void
mythread_mutex_destroy(mythread_mutex * mutex)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
mythread_mutex_lock(mythread_mutex * mutex)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
mythread_mutex_unlock(mythread_mutex * mutex)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
mythread_cond_init(mythread_cond * mycond)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
mythread_cond_destroy(mythread_cond * cond)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
mythread_cond_signal(mythread_cond * cond)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
mythread_cond_wait(mythread_cond * cond,mythread_mutex * mutex)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
mythread_cond_timedwait(mythread_cond * cond,mythread_mutex * mutex,const mythread_condtime * condtime)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
mythread_condtime_set(mythread_condtime * condtime,const mythread_cond * cond,uint32_t timeout_ms)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
mythread_create(mythread * thread,unsigned int (__stdcall * func)(void * arg),void * arg)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
mythread_join(mythread thread)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
mythread_mutex_init(mythread_mutex * mutex)413 mythread_mutex_init(mythread_mutex *mutex)
414 {
415 	InitializeCriticalSection(mutex);
416 	return 0;
417 }
418 
419 static inline void
mythread_mutex_destroy(mythread_mutex * mutex)420 mythread_mutex_destroy(mythread_mutex *mutex)
421 {
422 	DeleteCriticalSection(mutex);
423 }
424 
425 static inline void
mythread_mutex_lock(mythread_mutex * mutex)426 mythread_mutex_lock(mythread_mutex *mutex)
427 {
428 	EnterCriticalSection(mutex);
429 }
430 
431 static inline void
mythread_mutex_unlock(mythread_mutex * mutex)432 mythread_mutex_unlock(mythread_mutex *mutex)
433 {
434 	LeaveCriticalSection(mutex);
435 }
436 
437 
438 static inline int
mythread_cond_init(mythread_cond * cond)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
mythread_cond_destroy(mythread_cond * cond)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
mythread_cond_signal(mythread_cond * cond)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
mythread_cond_wait(mythread_cond * cond,mythread_mutex * mutex)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
mythread_cond_timedwait(mythread_cond * cond,mythread_mutex * mutex,const mythread_condtime * condtime)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
mythread_condtime_set(mythread_condtime * condtime,const mythread_cond * cond,uint32_t timeout)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
522