1 /**************************************************************************
2 *
3 * Copyright 1999-2006 Brian Paul
4 * Copyright 2008 VMware, Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #ifndef U_THREAD_H_
28 #define U_THREAD_H_
29
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <string.h>
33
34 #include "c11/threads.h"
35 #include "detect_os.h"
36 #include "macros.h"
37
38 #ifdef HAVE_PTHREAD
39 #include <signal.h>
40 #ifdef HAVE_PTHREAD_NP_H
41 #include <pthread_np.h>
42 #endif
43 #endif
44
45 #ifdef __HAIKU__
46 #include <OS.h>
47 #endif
48
49 #if DETECT_OS_LINUX && !defined(ANDROID)
50 #include <sched.h>
51 #elif defined(_WIN32) && !defined(__CYGWIN__) && _WIN32_WINNT >= 0x0600
52 #include <windows.h>
53 #endif
54
55 #ifdef __FreeBSD__
56 /* pthread_np.h -> sys/param.h -> machine/param.h
57 * - defines ALIGN which clashes with our ALIGN
58 */
59 #undef ALIGN
60 #define cpu_set_t cpuset_t
61 #endif
62
63 /* For util_set_thread_affinity to size the mask. */
64 #define UTIL_MAX_CPUS 1024 /* this should be enough */
65 #define UTIL_MAX_L3_CACHES UTIL_MAX_CPUS
66
67 static inline int
util_get_current_cpu(void)68 util_get_current_cpu(void)
69 {
70 #if DETECT_OS_LINUX && !defined(ANDROID)
71 return sched_getcpu();
72
73 #elif defined(_WIN32) && !defined(__CYGWIN__) && _WIN32_WINNT >= 0x0600
74 return GetCurrentProcessorNumber();
75
76 #else
77 return -1;
78 #endif
79 }
80
u_thread_create(int (* routine)(void *),void * param)81 static inline thrd_t u_thread_create(int (*routine)(void *), void *param)
82 {
83 thrd_t thread;
84 #ifdef HAVE_PTHREAD
85 sigset_t saved_set, new_set;
86 int ret;
87
88 sigfillset(&new_set);
89 sigdelset(&new_set, SIGSYS);
90 pthread_sigmask(SIG_BLOCK, &new_set, &saved_set);
91 ret = thrd_create( &thread, routine, param );
92 pthread_sigmask(SIG_SETMASK, &saved_set, NULL);
93 #else
94 int ret;
95 ret = thrd_create( &thread, routine, param );
96 #endif
97 if (ret)
98 return 0;
99
100 return thread;
101 }
102
u_thread_setname(const char * name)103 static inline void u_thread_setname( const char *name )
104 {
105 #if defined(HAVE_PTHREAD)
106 #if DETECT_OS_LINUX || DETECT_OS_CYGWIN || DETECT_OS_SOLARIS
107 pthread_setname_np(pthread_self(), name);
108 #elif DETECT_OS_FREEBSD || DETECT_OS_OPENBSD
109 pthread_set_name_np(pthread_self(), name);
110 #elif DETECT_OS_NETBSD
111 pthread_setname_np(pthread_self(), "%s", (void *)name);
112 #elif DETECT_OS_APPLE
113 pthread_setname_np(name);
114 #elif DETECT_OS_HAIKU
115 rename_thread(find_thread(NULL), name);
116 #else
117 #warning Not sure how to call pthread_setname_np
118 #endif
119 #endif
120 (void)name;
121 }
122
123 /**
124 * Set thread affinity.
125 *
126 * \param thread Thread
127 * \param mask Set this affinity mask
128 * \param old_mask Previous affinity mask returned if not NULL
129 * \param num_mask_bits Number of bits in both masks
130 * \return true on success
131 */
132 static inline bool
util_set_thread_affinity(thrd_t thread,const uint32_t * mask,uint32_t * old_mask,unsigned num_mask_bits)133 util_set_thread_affinity(thrd_t thread,
134 const uint32_t *mask,
135 uint32_t *old_mask,
136 unsigned num_mask_bits)
137 {
138 #if defined(HAVE_PTHREAD_SETAFFINITY)
139 cpu_set_t cpuset;
140
141 if (old_mask) {
142 if (pthread_getaffinity_np(thread, sizeof(cpuset), &cpuset) != 0)
143 return false;
144
145 memset(old_mask, 0, num_mask_bits / 32);
146 for (unsigned i = 0; i < num_mask_bits && i < CPU_SETSIZE; i++) {
147 if (CPU_ISSET(i, &cpuset))
148 old_mask[i / 32] |= 1u << (i % 32);
149 }
150 }
151
152 CPU_ZERO(&cpuset);
153 for (unsigned i = 0; i < num_mask_bits && i < CPU_SETSIZE; i++) {
154 if (mask[i / 32] & (1u << (i % 32)))
155 CPU_SET(i, &cpuset);
156 }
157 return pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset) == 0;
158
159 #elif defined(_WIN32) && !defined(__CYGWIN__)
160 DWORD_PTR m = mask[0];
161
162 if (sizeof(m) > 4 && num_mask_bits > 32)
163 m |= (uint64_t)mask[1] << 32;
164
165 m = SetThreadAffinityMask(thread, m);
166 if (!m)
167 return false;
168
169 if (old_mask) {
170 memset(old_mask, 0, num_mask_bits / 32);
171
172 old_mask[0] = m;
173 if (sizeof(m) > 4)
174 old_mask[1] = m >> 32;
175 }
176
177 return true;
178 #else
179 return false;
180 #endif
181 }
182
183 static inline bool
util_set_current_thread_affinity(const uint32_t * mask,uint32_t * old_mask,unsigned num_mask_bits)184 util_set_current_thread_affinity(const uint32_t *mask,
185 uint32_t *old_mask,
186 unsigned num_mask_bits)
187 {
188 #if defined(HAVE_PTHREAD_SETAFFINITY)
189 return util_set_thread_affinity(pthread_self(), mask, old_mask,
190 num_mask_bits);
191
192 #elif defined(_WIN32) && !defined(__CYGWIN__)
193 /* The GetCurrentThreadId() handle is only valid within the current thread. */
194 return util_set_thread_affinity(GetCurrentThread(), mask, old_mask,
195 num_mask_bits);
196
197 #else
198 return false;
199 #endif
200 }
201
202
203 /*
204 * Thread statistics.
205 */
206
207 /* Return the time of a thread's CPU time clock. */
208 static inline int64_t
util_thread_get_time_nano(thrd_t thread)209 util_thread_get_time_nano(thrd_t thread)
210 {
211 #if defined(HAVE_PTHREAD) && !defined(__APPLE__) && !defined(__HAIKU__)
212 struct timespec ts;
213 clockid_t cid;
214
215 pthread_getcpuclockid(thread, &cid);
216 clock_gettime(cid, &ts);
217 return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
218 #else
219 return 0;
220 #endif
221 }
222
223 /* Return the time of the current thread's CPU time clock. */
224 static inline int64_t
util_current_thread_get_time_nano(void)225 util_current_thread_get_time_nano(void)
226 {
227 #if defined(HAVE_PTHREAD)
228 return util_thread_get_time_nano(pthread_self());
229
230 #elif defined(_WIN32) && !defined(__CYGWIN__)
231 /* The GetCurrentThreadId() handle is only valid within the current thread. */
232 return util_thread_get_time_nano(GetCurrentThread());
233
234 #else
235 return 0;
236 #endif
237 }
238
u_thread_is_self(thrd_t thread)239 static inline bool u_thread_is_self(thrd_t thread)
240 {
241 #if defined(HAVE_PTHREAD)
242 return pthread_equal(pthread_self(), thread);
243 #endif
244 return false;
245 }
246
247 /*
248 * util_barrier
249 */
250
251 #if defined(HAVE_PTHREAD) && !defined(__APPLE__)
252
253 typedef pthread_barrier_t util_barrier;
254
util_barrier_init(util_barrier * barrier,unsigned count)255 static inline void util_barrier_init(util_barrier *barrier, unsigned count)
256 {
257 pthread_barrier_init(barrier, NULL, count);
258 }
259
util_barrier_destroy(util_barrier * barrier)260 static inline void util_barrier_destroy(util_barrier *barrier)
261 {
262 pthread_barrier_destroy(barrier);
263 }
264
util_barrier_wait(util_barrier * barrier)265 static inline void util_barrier_wait(util_barrier *barrier)
266 {
267 pthread_barrier_wait(barrier);
268 }
269
270
271 #else /* If the OS doesn't have its own, implement barriers using a mutex and a condvar */
272
273 typedef struct {
274 unsigned count;
275 unsigned waiters;
276 uint64_t sequence;
277 mtx_t mutex;
278 cnd_t condvar;
279 } util_barrier;
280
util_barrier_init(util_barrier * barrier,unsigned count)281 static inline void util_barrier_init(util_barrier *barrier, unsigned count)
282 {
283 barrier->count = count;
284 barrier->waiters = 0;
285 barrier->sequence = 0;
286 (void) mtx_init(&barrier->mutex, mtx_plain);
287 cnd_init(&barrier->condvar);
288 }
289
util_barrier_destroy(util_barrier * barrier)290 static inline void util_barrier_destroy(util_barrier *barrier)
291 {
292 assert(barrier->waiters == 0);
293 mtx_destroy(&barrier->mutex);
294 cnd_destroy(&barrier->condvar);
295 }
296
util_barrier_wait(util_barrier * barrier)297 static inline void util_barrier_wait(util_barrier *barrier)
298 {
299 mtx_lock(&barrier->mutex);
300
301 assert(barrier->waiters < barrier->count);
302 barrier->waiters++;
303
304 if (barrier->waiters < barrier->count) {
305 uint64_t sequence = barrier->sequence;
306
307 do {
308 cnd_wait(&barrier->condvar, &barrier->mutex);
309 } while (sequence == barrier->sequence);
310 } else {
311 barrier->waiters = 0;
312 barrier->sequence++;
313 cnd_broadcast(&barrier->condvar);
314 }
315
316 mtx_unlock(&barrier->mutex);
317 }
318
319 #endif
320
321 #endif /* U_THREAD_H_ */
322