aboutsummaryrefslogtreecommitdiff
path: root/src/common/mythread.h
blob: 637b8ac3b3252a97383ac2406890af942a78ed7f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
///////////////////////////////////////////////////////////////////////////////
//
/// \file       mythread.h
/// \brief      Some threading related helper macros and functions
//
//  Author:     Lasse Collin
//
//  This file has been put into the public domain.
//  You can do whatever you want with this file.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include "sysdefs.h"


#ifdef HAVE_PTHREAD

////////////////////
// Using pthreads //
////////////////////

#include <sys/time.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>


#ifdef __VMS
// Do nothing on OpenVMS. It doesn't have pthread_sigmask().
#define mythread_sigmask(how, set, oset) do { } while (0)
#else
/// \brief      Set the process signal mask
///
/// If threads are disabled, sigprocmask() is used instead
/// of pthread_sigmask().
#define mythread_sigmask(how, set, oset) \
	pthread_sigmask(how, set, oset)
#endif

/// \brief      Call the given function once
///
/// If threads are disabled, a thread-unsafe version is used.
#define mythread_once(func) \
do { \
	static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
	pthread_once(&once_, &func); \
} while (0)


/// \brief      Lock a mutex for a duration of a block
///
/// Perform pthread_mutex_lock(&mutex) in the beginning of a block
/// and pthread_mutex_unlock(&mutex) at the end of the block. "break"
/// may be used to unlock the mutex and jump out of the block.
/// mythread_sync blocks may be nested.
///
/// Example:
///
///     mythread_sync(mutex) {
///         foo();
///         if (some_error)
///             break; // Skips bar()
///         bar();
///     }
///
/// At least GCC optimizes the loops completely away so it doesn't slow
/// things down at all compared to plain pthread_mutex_lock(&mutex)
/// and pthread_mutex_unlock(&mutex) calls.
///
#define mythread_sync(mutex) mythread_sync_helper1(mutex, __LINE__)
#define mythread_sync_helper1(mutex, line) mythread_sync_helper2(mutex, line)
#define mythread_sync_helper2(mutex, line) \
	for (unsigned int mythread_i_ ## line = 0; \
			mythread_i_ ## line \
				? (pthread_mutex_unlock(&(mutex)), 0) \
				: (pthread_mutex_lock(&(mutex)), 1); \
			mythread_i_ ## line = 1) \
		for (unsigned int mythread_j_ ## line = 0; \
				!mythread_j_ ## line; \
				mythread_j_ ## line = 1)


typedef struct {
	/// Condition variable
	pthread_cond_t cond;

#ifdef HAVE_CLOCK_GETTIME
	/// Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
	/// the condition variable
	clockid_t clk_id;
#endif

} mythread_cond;


/// \brief      Initialize a condition variable to use CLOCK_MONOTONIC
///
/// Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the
/// timeout in pthread_cond_timedwait() work correctly also if system time
/// is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available
/// everywhere while the default CLOCK_REALTIME is, so the default is
/// used if CLOCK_MONOTONIC isn't available.
static inline int
mythread_cond_init(mythread_cond *mycond)
{
#ifdef HAVE_CLOCK_GETTIME
	// NOTE: HAVE_DECL_CLOCK_MONOTONIC is always defined to 0 or 1.
#	if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && HAVE_DECL_CLOCK_MONOTONIC
	struct timespec ts;
	pthread_condattr_t condattr;

	// POSIX doesn't seem to *require* that pthread_condattr_setclock()
	// will fail if given an unsupported clock ID. Test that
	// CLOCK_MONOTONIC really is supported using clock_gettime().
	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0
			&& pthread_condattr_init(&condattr) == 0) {
		int ret = pthread_condattr_setclock(
				&condattr, CLOCK_MONOTONIC);
		if (ret == 0)
			ret = pthread_cond_init(&mycond->cond, &condattr);

		pthread_condattr_destroy(&condattr);

		if (ret == 0) {
			mycond->clk_id = CLOCK_MONOTONIC;
			return 0;
		}
	}

	// If anything above fails, fall back to the default CLOCK_REALTIME.
#	endif

	mycond->clk_id = CLOCK_REALTIME;
#endif

	return pthread_cond_init(&mycond->cond, NULL);
}


/// \brief      Convert relative time to absolute time for use with timed wait
///
/// The current time of the clock associated with the condition variable
/// is added to the relative time in *ts.
static inline void
mythread_cond_abstime(const mythread_cond *mycond, struct timespec *ts)
{
#ifdef HAVE_CLOCK_GETTIME
	struct timespec now;
	clock_gettime(mycond->clk_id, &now);

	ts->tv_sec += now.tv_sec;
	ts->tv_nsec += now.tv_nsec;
#else
	(void)mycond;

	struct timeval now;
	gettimeofday(&now, NULL);

	ts->tv_sec += now.tv_sec;
	ts->tv_nsec += now.tv_usec * 1000L;
#endif

	// tv_nsec must stay in the range [0, 999_999_999].
	if (ts->tv_nsec >= 1000000000L) {
		ts->tv_nsec -= 1000000000L;
		++ts->tv_sec;
	}

	return;
}


#define mythread_cond_wait(mycondptr, mutexptr) \
	pthread_cond_wait(&(mycondptr)->cond, mutexptr)

#define mythread_cond_timedwait(mycondptr, mutexptr, abstimeptr) \
	pthread_cond_timedwait(&(mycondptr)->cond, mutexptr, abstimeptr)

#define mythread_cond_signal(mycondptr) \
	pthread_cond_signal(&(mycondptr)->cond)

#define mythread_cond_broadcast(mycondptr) \
	pthread_cond_broadcast(&(mycondptr)->cond)

#define mythread_cond_destroy(mycondptr) \
	pthread_cond_destroy(&(mycondptr)->cond)


/// \brief      Create a thread with all signals blocked
static inline int
mythread_create(pthread_t *thread, void *(*func)(void *arg), void *arg)
{
	sigset_t old;
	sigset_t all;
	sigfillset(&all);

	pthread_sigmask(SIG_SETMASK, &all, &old);
	const int ret = pthread_create(thread, NULL, func, arg);
	pthread_sigmask(SIG_SETMASK, &old, NULL);

	return ret;
}

#else

//////////////////
// No threading //
//////////////////

#define mythread_sigmask(how, set, oset) \
	sigprocmask(how, set, oset)


#define mythread_once(func) \
do { \
	static bool once_ = false; \
	if (!once_) { \
		func(); \
		once_ = true; \
	} \
} while (0)

#endif

#endif