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 <errno.h> 31#include <stdint.h> 32#include <stdbool.h> 33#include <string.h> 34 35#include "c11/threads.h" 36#include "detect_os.h" 37#include "macros.h" 38 39#ifdef HAVE_PTHREAD 40#include <signal.h> 41#ifdef HAVE_PTHREAD_NP_H 42#include <pthread_np.h> 43#endif 44#endif 45 46#ifdef __HAIKU__ 47#include <OS.h> 48#endif 49 50#if DETECT_OS_LINUX && !defined(ANDROID) 51#include <sched.h> 52#elif defined(_WIN32) && !defined(__CYGWIN__) && _WIN32_WINNT >= 0x0600 53#ifndef WIN32_LEAN_AND_MEAN 54#define WIN32_LEAN_AND_MEAN 1 55#endif 56#include <windows.h> 57#endif 58 59#ifdef __FreeBSD__ 60/* pthread_np.h -> sys/param.h -> machine/param.h 61 * - defines ALIGN which clashes with our ALIGN 62 */ 63#undef ALIGN 64#define cpu_set_t cpuset_t 65#endif 66 67/* For util_set_thread_affinity to size the mask. */ 68#define UTIL_MAX_CPUS 1024 /* this should be enough */ 69#define UTIL_MAX_L3_CACHES UTIL_MAX_CPUS 70 71/* Some highly performance-sensitive thread-local variables like the current GL 72 * context are declared with the initial-exec model on Linux. glibc allocates a 73 * fixed number of extra slots for initial-exec TLS variables at startup, and 74 * Mesa relies on (even if it's dlopen()ed after init) being able to fit into 75 * those. This model saves the call to look up the address of the TLS variable. 76 * 77 * However, if we don't have this TLS model available on the platform, then we 78 * still want to use normal TLS (which involves a function call, but not the 79 * expensive pthread_getspecific() or its equivalent). 80 */ 81#if DETECT_OS_APPLE 82/* Apple Clang emits wrappers when using thread_local that break module linkage, 83 * but not with __thread 84 */ 85#define __THREAD_INITIAL_EXEC __thread 86#elif defined(__GLIBC__) 87#define __THREAD_INITIAL_EXEC thread_local __attribute__((tls_model("initial-exec"))) 88#define REALLY_INITIAL_EXEC 89#else 90#define __THREAD_INITIAL_EXEC thread_local 91#endif 92 93static inline int 94util_get_current_cpu(void) 95{ 96#if DETECT_OS_LINUX && !defined(ANDROID) 97 return sched_getcpu(); 98 99#elif defined(_WIN32) && !defined(__CYGWIN__) && _WIN32_WINNT >= 0x0600 100 return GetCurrentProcessorNumber(); 101 102#else 103 return -1; 104#endif 105} 106 107static inline int u_thread_create(thrd_t *thrd, int (*routine)(void *), void *param) 108{ 109 int ret = thrd_error; 110#ifdef HAVE_PTHREAD 111 sigset_t saved_set, new_set; 112 113 sigfillset(&new_set); 114 sigdelset(&new_set, SIGSYS); 115 116 /* SIGSEGV is commonly used by Vulkan API tracing layers in order to track 117 * accesses in device memory mapped to user space. Blocking the signal hinders 118 * that tracking mechanism. 119 */ 120 sigdelset(&new_set, SIGSEGV); 121 pthread_sigmask(SIG_BLOCK, &new_set, &saved_set); 122 ret = thrd_create(thrd, routine, param); 123 pthread_sigmask(SIG_SETMASK, &saved_set, NULL); 124#else 125 ret = thrd_create(thrd, routine, param); 126#endif 127 128 return ret; 129} 130 131static inline void u_thread_setname( const char *name ) 132{ 133#if defined(HAVE_PTHREAD) 134#if DETECT_OS_LINUX || DETECT_OS_CYGWIN || DETECT_OS_SOLARIS 135 int ret = pthread_setname_np(pthread_self(), name); 136 if (ret == ERANGE) { 137 char buf[16]; 138 const size_t len = MIN2(strlen(name), ARRAY_SIZE(buf) - 1); 139 memcpy(buf, name, len); 140 buf[len] = '\0'; 141 pthread_setname_np(pthread_self(), buf); 142 } 143#elif DETECT_OS_FREEBSD || DETECT_OS_OPENBSD 144 pthread_set_name_np(pthread_self(), name); 145#elif DETECT_OS_NETBSD 146 pthread_setname_np(pthread_self(), "%s", (void *)name); 147#elif DETECT_OS_APPLE 148 pthread_setname_np(name); 149#elif DETECT_OS_HAIKU 150 rename_thread(find_thread(NULL), name); 151#else 152#warning Not sure how to call pthread_setname_np 153#endif 154#endif 155 (void)name; 156} 157 158/** 159 * Set thread affinity. 160 * 161 * \param thread Thread 162 * \param mask Set this affinity mask 163 * \param old_mask Previous affinity mask returned if not NULL 164 * \param num_mask_bits Number of bits in both masks 165 * \return true on success 166 */ 167static inline bool 168util_set_thread_affinity(thrd_t thread, 169 const uint32_t *mask, 170 uint32_t *old_mask, 171 unsigned num_mask_bits) 172{ 173#if defined(HAVE_PTHREAD_SETAFFINITY) 174 cpu_set_t cpuset; 175 176 if (old_mask) { 177 if (pthread_getaffinity_np(thread, sizeof(cpuset), &cpuset) != 0) 178 return false; 179 180 memset(old_mask, 0, num_mask_bits / 8); 181 for (unsigned i = 0; i < num_mask_bits && i < CPU_SETSIZE; i++) { 182 if (CPU_ISSET(i, &cpuset)) 183 old_mask[i / 32] |= 1u << (i % 32); 184 } 185 } 186 187 CPU_ZERO(&cpuset); 188 for (unsigned i = 0; i < num_mask_bits && i < CPU_SETSIZE; i++) { 189 if (mask[i / 32] & (1u << (i % 32))) 190 CPU_SET(i, &cpuset); 191 } 192 return pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset) == 0; 193 194#elif defined(_WIN32) && !defined(__CYGWIN__) 195 DWORD_PTR m = mask[0]; 196 197 if (sizeof(m) > 4 && num_mask_bits > 32) 198 m |= (uint64_t)mask[1] << 32; 199 200 m = SetThreadAffinityMask(thread, m); 201 if (!m) 202 return false; 203 204 if (old_mask) { 205 memset(old_mask, 0, num_mask_bits / 8); 206 207 old_mask[0] = m; 208#ifdef _WIN64 209 old_mask[1] = m >> 32; 210#endif 211 } 212 213 return true; 214#else 215 return false; 216#endif 217} 218 219static inline bool 220util_set_current_thread_affinity(const uint32_t *mask, 221 uint32_t *old_mask, 222 unsigned num_mask_bits) 223{ 224#if defined(HAVE_PTHREAD_SETAFFINITY) 225 return util_set_thread_affinity(pthread_self(), mask, old_mask, 226 num_mask_bits); 227 228#elif defined(_WIN32) && !defined(__CYGWIN__) 229 /* The GetCurrentThreadId() handle is only valid within the current thread. */ 230 return util_set_thread_affinity(GetCurrentThread(), mask, old_mask, 231 num_mask_bits); 232 233#else 234 return false; 235#endif 236} 237 238 239/* 240 * Thread statistics. 241 */ 242 243/* Return the time of a thread's CPU time clock. */ 244static inline int64_t 245util_thread_get_time_nano(thrd_t thread) 246{ 247#if defined(HAVE_PTHREAD) && !defined(__APPLE__) && !defined(__HAIKU__) 248 struct timespec ts; 249 clockid_t cid; 250 251 pthread_getcpuclockid(thread, &cid); 252 clock_gettime(cid, &ts); 253 return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; 254#else 255 (void)thread; 256 return 0; 257#endif 258} 259 260/* Return the time of the current thread's CPU time clock. */ 261static inline int64_t 262util_current_thread_get_time_nano(void) 263{ 264#if defined(HAVE_PTHREAD) 265 return util_thread_get_time_nano(pthread_self()); 266 267#elif defined(_WIN32) && !defined(__CYGWIN__) 268 /* The GetCurrentThreadId() handle is only valid within the current thread. */ 269 return util_thread_get_time_nano(GetCurrentThread()); 270 271#else 272 return 0; 273#endif 274} 275 276static inline bool u_thread_is_self(thrd_t thread) 277{ 278#if defined(HAVE_PTHREAD) 279 return pthread_equal(pthread_self(), thread); 280#endif 281 return false; 282} 283 284/* 285 * util_barrier 286 */ 287 288#if defined(HAVE_PTHREAD) && !defined(__APPLE__) && !defined(__HAIKU__) 289 290typedef pthread_barrier_t util_barrier; 291 292static inline void util_barrier_init(util_barrier *barrier, unsigned count) 293{ 294 pthread_barrier_init(barrier, NULL, count); 295} 296 297static inline void util_barrier_destroy(util_barrier *barrier) 298{ 299 pthread_barrier_destroy(barrier); 300} 301 302static inline bool util_barrier_wait(util_barrier *barrier) 303{ 304 return pthread_barrier_wait(barrier) == PTHREAD_BARRIER_SERIAL_THREAD; 305} 306 307 308#else /* If the OS doesn't have its own, implement barriers using a mutex and a condvar */ 309 310typedef struct { 311 unsigned count; 312 unsigned waiters; 313 uint64_t sequence; 314 mtx_t mutex; 315 cnd_t condvar; 316} util_barrier; 317 318static inline void util_barrier_init(util_barrier *barrier, unsigned count) 319{ 320 barrier->count = count; 321 barrier->waiters = 0; 322 barrier->sequence = 0; 323 (void) mtx_init(&barrier->mutex, mtx_plain); 324 cnd_init(&barrier->condvar); 325} 326 327static inline void util_barrier_destroy(util_barrier *barrier) 328{ 329 assert(barrier->waiters == 0); 330 mtx_destroy(&barrier->mutex); 331 cnd_destroy(&barrier->condvar); 332} 333 334static inline bool util_barrier_wait(util_barrier *barrier) 335{ 336 mtx_lock(&barrier->mutex); 337 338 assert(barrier->waiters < barrier->count); 339 barrier->waiters++; 340 341 if (barrier->waiters < barrier->count) { 342 uint64_t sequence = barrier->sequence; 343 344 do { 345 cnd_wait(&barrier->condvar, &barrier->mutex); 346 } while (sequence == barrier->sequence); 347 } else { 348 barrier->waiters = 0; 349 barrier->sequence++; 350 cnd_broadcast(&barrier->condvar); 351 } 352 353 mtx_unlock(&barrier->mutex); 354 355 return true; 356} 357 358#endif 359 360/* 361 * Thread-id's. 362 * 363 * thrd_current() is not portable to windows (or at least not in a desirable 364 * way), so thread_id's provide an alternative mechanism 365 */ 366 367#ifdef _WIN32 368typedef DWORD thread_id; 369#else 370typedef thrd_t thread_id; 371#endif 372 373static inline thread_id 374util_get_thread_id(void) 375{ 376 /* 377 * XXX: Callers of of this function assume it is a lightweight function. 378 * But unfortunately C11's thrd_current() gives no such guarantees. In 379 * fact, it's pretty hard to have a compliant implementation of 380 * thrd_current() on Windows with such characteristics. So for now, we 381 * side-step this mess and use Windows thread primitives directly here. 382 */ 383#ifdef _WIN32 384 return GetCurrentThreadId(); 385#else 386 return thrd_current(); 387#endif 388} 389 390 391static inline int 392util_thread_id_equal(thread_id t1, thread_id t2) 393{ 394#ifdef _WIN32 395 return t1 == t2; 396#else 397 return thrd_equal(t1, t2); 398#endif 399} 400 401#endif /* U_THREAD_H_ */ 402