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