xref: /freebsd/contrib/xz/src/common/mythread.h (revision 911f0260390e18cf85f3dbf2c719b593efdc1e3c)
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 <pthread.h>
104 #include <signal.h>
105 #include <time.h>
106 #include <errno.h>
107 
108 // If clock_gettime() isn't available, use gettimeofday() from <sys/time.h>
109 // as a fallback. gettimeofday() is in SUSv2 and thus is supported on all
110 // relevant POSIX systems.
111 #ifndef HAVE_CLOCK_GETTIME
112 #	include <sys/time.h>
113 #endif
114 
115 #define MYTHREAD_RET_TYPE void *
116 #define MYTHREAD_RET_VALUE NULL
117 
118 typedef pthread_t mythread;
119 typedef pthread_mutex_t mythread_mutex;
120 
121 typedef struct {
122 	pthread_cond_t cond;
123 #ifdef HAVE_CLOCK_GETTIME
124 	// Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
125 	// the condition variable.
126 	clockid_t clk_id;
127 #endif
128 } mythread_cond;
129 
130 typedef struct timespec mythread_condtime;
131 
132 
133 // Calls the given function once in a thread-safe way.
134 #define mythread_once(func) \
135 	do { \
136 		static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
137 		pthread_once(&once_, &func); \
138 	} while (0)
139 
140 
141 // Use pthread_sigmask() to set the signal mask in multi-threaded programs.
142 // Do nothing on OpenVMS since it lacks pthread_sigmask().
143 static inline void
144 mythread_sigmask(int how, const sigset_t *restrict set,
145 		sigset_t *restrict oset)
146 {
147 #ifdef __VMS
148 	(void)how;
149 	(void)set;
150 	(void)oset;
151 #else
152 	int ret = pthread_sigmask(how, set, oset);
153 	assert(ret == 0);
154 	(void)ret;
155 #endif
156 }
157 
158 
159 // Creates a new thread with all signals blocked. Returns zero on success
160 // and non-zero on error.
161 static inline int
162 mythread_create(mythread *thread, void *(*func)(void *arg), void *arg)
163 {
164 	sigset_t old;
165 	sigset_t all;
166 	sigfillset(&all);
167 
168 	mythread_sigmask(SIG_SETMASK, &all, &old);
169 	const int ret = pthread_create(thread, NULL, func, arg);
170 	mythread_sigmask(SIG_SETMASK, &old, NULL);
171 
172 	return ret;
173 }
174 
175 // Joins a thread. Returns zero on success and non-zero on error.
176 static inline int
177 mythread_join(mythread thread)
178 {
179 	return pthread_join(thread, NULL);
180 }
181 
182 
183 // Initiatlizes a mutex. Returns zero on success and non-zero on error.
184 static inline int
185 mythread_mutex_init(mythread_mutex *mutex)
186 {
187 	return pthread_mutex_init(mutex, NULL);
188 }
189 
190 static inline void
191 mythread_mutex_destroy(mythread_mutex *mutex)
192 {
193 	int ret = pthread_mutex_destroy(mutex);
194 	assert(ret == 0);
195 	(void)ret;
196 }
197 
198 static inline void
199 mythread_mutex_lock(mythread_mutex *mutex)
200 {
201 	int ret = pthread_mutex_lock(mutex);
202 	assert(ret == 0);
203 	(void)ret;
204 }
205 
206 static inline void
207 mythread_mutex_unlock(mythread_mutex *mutex)
208 {
209 	int ret = pthread_mutex_unlock(mutex);
210 	assert(ret == 0);
211 	(void)ret;
212 }
213 
214 
215 // Initializes a condition variable.
216 //
217 // Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the
218 // timeout in pthread_cond_timedwait() work correctly also if system time
219 // is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available
220 // everywhere while the default CLOCK_REALTIME is, so the default is
221 // used if CLOCK_MONOTONIC isn't available.
222 //
223 // If clock_gettime() isn't available at all, gettimeofday() will be used.
224 static inline int
225 mythread_cond_init(mythread_cond *mycond)
226 {
227 #ifdef HAVE_CLOCK_GETTIME
228 #	if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
229 		defined(HAVE_CLOCK_MONOTONIC)
230 	struct timespec ts;
231 	pthread_condattr_t condattr;
232 
233 	// POSIX doesn't seem to *require* that pthread_condattr_setclock()
234 	// will fail if given an unsupported clock ID. Test that
235 	// CLOCK_MONOTONIC really is supported using clock_gettime().
236 	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0
237 			&& pthread_condattr_init(&condattr) == 0) {
238 		int ret = pthread_condattr_setclock(
239 				&condattr, CLOCK_MONOTONIC);
240 		if (ret == 0)
241 			ret = pthread_cond_init(&mycond->cond, &condattr);
242 
243 		pthread_condattr_destroy(&condattr);
244 
245 		if (ret == 0) {
246 			mycond->clk_id = CLOCK_MONOTONIC;
247 			return 0;
248 		}
249 	}
250 
251 	// If anything above fails, fall back to the default CLOCK_REALTIME.
252 	// POSIX requires that all implementations of clock_gettime() must
253 	// support at least CLOCK_REALTIME.
254 #	endif
255 
256 	mycond->clk_id = CLOCK_REALTIME;
257 #endif
258 
259 	return pthread_cond_init(&mycond->cond, NULL);
260 }
261 
262 static inline void
263 mythread_cond_destroy(mythread_cond *cond)
264 {
265 	int ret = pthread_cond_destroy(&cond->cond);
266 	assert(ret == 0);
267 	(void)ret;
268 }
269 
270 static inline void
271 mythread_cond_signal(mythread_cond *cond)
272 {
273 	int ret = pthread_cond_signal(&cond->cond);
274 	assert(ret == 0);
275 	(void)ret;
276 }
277 
278 static inline void
279 mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
280 {
281 	int ret = pthread_cond_wait(&cond->cond, mutex);
282 	assert(ret == 0);
283 	(void)ret;
284 }
285 
286 // Waits on a condition or until a timeout expires. If the timeout expires,
287 // non-zero is returned, otherwise zero is returned.
288 static inline int
289 mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
290 		const mythread_condtime *condtime)
291 {
292 	int ret = pthread_cond_timedwait(&cond->cond, mutex, condtime);
293 	assert(ret == 0 || ret == ETIMEDOUT);
294 	return ret;
295 }
296 
297 // Sets condtime to the absolute time that is timeout_ms milliseconds
298 // in the future. The type of the clock to use is taken from cond.
299 static inline void
300 mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
301 		uint32_t timeout_ms)
302 {
303 	condtime->tv_sec = (time_t)(timeout_ms / 1000);
304 	condtime->tv_nsec = (long)((timeout_ms % 1000) * 1000000);
305 
306 #ifdef HAVE_CLOCK_GETTIME
307 	struct timespec now;
308 	int ret = clock_gettime(cond->clk_id, &now);
309 	assert(ret == 0);
310 	(void)ret;
311 
312 	condtime->tv_sec += now.tv_sec;
313 	condtime->tv_nsec += now.tv_nsec;
314 #else
315 	(void)cond;
316 
317 	struct timeval now;
318 	gettimeofday(&now, NULL);
319 
320 	condtime->tv_sec += now.tv_sec;
321 	condtime->tv_nsec += now.tv_usec * 1000L;
322 #endif
323 
324 	// tv_nsec must stay in the range [0, 999_999_999].
325 	if (condtime->tv_nsec >= 1000000000L) {
326 		condtime->tv_nsec -= 1000000000L;
327 		++condtime->tv_sec;
328 	}
329 }
330 
331 
332 #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA)
333 
334 /////////////////////
335 // Windows threads //
336 /////////////////////
337 
338 #define WIN32_LEAN_AND_MEAN
339 #ifdef MYTHREAD_VISTA
340 #	undef _WIN32_WINNT
341 #	define _WIN32_WINNT 0x0600
342 #endif
343 #include <windows.h>
344 #include <process.h>
345 
346 #define MYTHREAD_RET_TYPE unsigned int __stdcall
347 #define MYTHREAD_RET_VALUE 0
348 
349 typedef HANDLE mythread;
350 typedef CRITICAL_SECTION mythread_mutex;
351 
352 #ifdef MYTHREAD_WIN95
353 typedef HANDLE mythread_cond;
354 #else
355 typedef CONDITION_VARIABLE mythread_cond;
356 #endif
357 
358 typedef struct {
359 	// Tick count (milliseconds) in the beginning of the timeout.
360 	// NOTE: This is 32 bits so it wraps around after 49.7 days.
361 	// Multi-day timeouts may not work as expected.
362 	DWORD start;
363 
364 	// Length of the timeout in milliseconds. The timeout expires
365 	// when the current tick count minus "start" is equal or greater
366 	// than "timeout".
367 	DWORD timeout;
368 } mythread_condtime;
369 
370 
371 // mythread_once() is only available with Vista threads.
372 #ifdef MYTHREAD_VISTA
373 #define mythread_once(func) \
374 	do { \
375 		static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \
376 		BOOL pending_; \
377 		if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \
378 			abort(); \
379 		if (pending_) { \
380 			func(); \
381 			if (!InitOnceComplete(&once, 0, NULL)) \
382 				abort(); \
383 		} \
384 	} while (0)
385 #endif
386 
387 
388 // mythread_sigmask() isn't available on Windows. Even a dummy version would
389 // make no sense because the other POSIX signal functions are missing anyway.
390 
391 
392 static inline int
393 mythread_create(mythread *thread,
394 		unsigned int (__stdcall *func)(void *arg), void *arg)
395 {
396 	uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL);
397 	if (ret == 0)
398 		return -1;
399 
400 	*thread = (HANDLE)ret;
401 	return 0;
402 }
403 
404 static inline int
405 mythread_join(mythread thread)
406 {
407 	int ret = 0;
408 
409 	if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
410 		ret = -1;
411 
412 	if (!CloseHandle(thread))
413 		ret = -1;
414 
415 	return ret;
416 }
417 
418 
419 static inline int
420 mythread_mutex_init(mythread_mutex *mutex)
421 {
422 	InitializeCriticalSection(mutex);
423 	return 0;
424 }
425 
426 static inline void
427 mythread_mutex_destroy(mythread_mutex *mutex)
428 {
429 	DeleteCriticalSection(mutex);
430 }
431 
432 static inline void
433 mythread_mutex_lock(mythread_mutex *mutex)
434 {
435 	EnterCriticalSection(mutex);
436 }
437 
438 static inline void
439 mythread_mutex_unlock(mythread_mutex *mutex)
440 {
441 	LeaveCriticalSection(mutex);
442 }
443 
444 
445 static inline int
446 mythread_cond_init(mythread_cond *cond)
447 {
448 #ifdef MYTHREAD_WIN95
449 	*cond = CreateEvent(NULL, FALSE, FALSE, NULL);
450 	return *cond == NULL ? -1 : 0;
451 #else
452 	InitializeConditionVariable(cond);
453 	return 0;
454 #endif
455 }
456 
457 static inline void
458 mythread_cond_destroy(mythread_cond *cond)
459 {
460 #ifdef MYTHREAD_WIN95
461 	CloseHandle(*cond);
462 #else
463 	(void)cond;
464 #endif
465 }
466 
467 static inline void
468 mythread_cond_signal(mythread_cond *cond)
469 {
470 #ifdef MYTHREAD_WIN95
471 	SetEvent(*cond);
472 #else
473 	WakeConditionVariable(cond);
474 #endif
475 }
476 
477 static inline void
478 mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
479 {
480 #ifdef MYTHREAD_WIN95
481 	LeaveCriticalSection(mutex);
482 	WaitForSingleObject(*cond, INFINITE);
483 	EnterCriticalSection(mutex);
484 #else
485 	BOOL ret = SleepConditionVariableCS(cond, mutex, INFINITE);
486 	assert(ret);
487 	(void)ret;
488 #endif
489 }
490 
491 static inline int
492 mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
493 		const mythread_condtime *condtime)
494 {
495 #ifdef MYTHREAD_WIN95
496 	LeaveCriticalSection(mutex);
497 #endif
498 
499 	DWORD elapsed = GetTickCount() - condtime->start;
500 	DWORD timeout = elapsed >= condtime->timeout
501 			? 0 : condtime->timeout - elapsed;
502 
503 #ifdef MYTHREAD_WIN95
504 	DWORD ret = WaitForSingleObject(*cond, timeout);
505 	assert(ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT);
506 
507 	EnterCriticalSection(mutex);
508 
509 	return ret == WAIT_TIMEOUT;
510 #else
511 	BOOL ret = SleepConditionVariableCS(cond, mutex, timeout);
512 	assert(ret || GetLastError() == ERROR_TIMEOUT);
513 	return !ret;
514 #endif
515 }
516 
517 static inline void
518 mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
519 		uint32_t timeout)
520 {
521 	(void)cond;
522 	condtime->start = GetTickCount();
523 	condtime->timeout = timeout;
524 }
525 
526 #endif
527 
528 #endif
529