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