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