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