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