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