tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ThreadLocal.h (6964B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* Cross-platform lightweight thread local data wrappers. */
      8 
      9 #ifndef mozilla_ThreadLocal_h
     10 #define mozilla_ThreadLocal_h
     11 
     12 #if !defined(XP_WIN) && !defined(__wasi__)
     13 #  include <pthread.h>
     14 #endif
     15 
     16 #include <type_traits>
     17 
     18 #include "mozilla/Assertions.h"
     19 
     20 namespace mozilla {
     21 
     22 namespace detail {
     23 
     24 #ifdef XP_MACOSX
     25 #  if defined(__has_feature)
     26 #    if __has_feature(cxx_thread_local)
     27 #      define MACOSX_HAS_THREAD_LOCAL
     28 #    endif
     29 #  endif
     30 #endif
     31 
     32 /*
     33 * Thread Local Storage helpers.
     34 *
     35 * Usage:
     36 *
     37 * Do not directly instantiate this class.  Instead, use the
     38 * MOZ_THREAD_LOCAL macro to declare or define instances.  The macro
     39 * takes a type name as its argument.
     40 *
     41 * Declare like this:
     42 * extern MOZ_THREAD_LOCAL(int) tlsInt;
     43 * Define like this:
     44 * MOZ_THREAD_LOCAL(int) tlsInt;
     45 * or:
     46 * static MOZ_THREAD_LOCAL(int) tlsInt;
     47 *
     48 * Only static-storage-duration (e.g. global variables, or static class members)
     49 * objects of this class should be instantiated. This class relies on
     50 * zero-initialization, which is implicit for static-storage-duration objects.
     51 * It doesn't have a custom default constructor, to avoid static initializers.
     52 *
     53 * API usage:
     54 *
     55 * // Create a TLS item.
     56 * //
     57 * // Note that init() should be invoked before the first use of set()
     58 * // or get().  It is ok to call it multiple times.  This must be
     59 * // called in a way that avoids possible races with other threads.
     60 * MOZ_THREAD_LOCAL(int) tlsKey;
     61 * if (!tlsKey.init()) {
     62 *   // deal with the error
     63 * }
     64 *
     65 * // Set the TLS value
     66 * tlsKey.set(123);
     67 *
     68 * // Get the TLS value
     69 * int value = tlsKey.get();
     70 */
     71 
     72 // Integral types narrower than void* must be extended to avoid
     73 // warnings from valgrind on some platforms.  This helper type
     74 // achieves that without penalizing the common case of ThreadLocals
     75 // instantiated using a pointer type.
     76 template <typename S>
     77 struct Helper {
     78  typedef uintptr_t Type;
     79 };
     80 
     81 template <typename S>
     82 struct Helper<S*> {
     83  typedef S* Type;
     84 };
     85 
     86 #if defined(XP_WIN)
     87 /*
     88 * ThreadLocalKeyStorage uses Thread Local APIs that are declared in
     89 * processthreadsapi.h. To use this class on Windows, include that file
     90 * (or windows.h) before including ThreadLocal.h.
     91 *
     92 * TLS_OUT_OF_INDEXES is a #define that is used to detect whether
     93 * an appropriate header has been included prior to this file
     94 */
     95 #  if defined(TLS_OUT_OF_INDEXES)
     96 /* Despite not being used for MOZ_THREAD_LOCAL, we expose an implementation for
     97 * Windows for cases where it's not desirable to use thread_local */
     98 template <typename T>
     99 class ThreadLocalKeyStorage {
    100 public:
    101  ThreadLocalKeyStorage() : mKey(TLS_OUT_OF_INDEXES) {}
    102 
    103  inline bool initialized() const { return mKey != TLS_OUT_OF_INDEXES; }
    104 
    105  inline void init() { mKey = TlsAlloc(); }
    106 
    107  inline T get() const {
    108    void* h = TlsGetValue(mKey);
    109    return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
    110  }
    111 
    112  inline bool set(const T aValue) {
    113    void* h = const_cast<void*>(reinterpret_cast<const void*>(
    114        static_cast<typename Helper<T>::Type>(aValue)));
    115    return TlsSetValue(mKey, h);
    116  }
    117 
    118 private:
    119  unsigned long mKey;
    120 };
    121 #  endif
    122 #elif defined(__wasi__)
    123 // There are no threads on WASI, so we just use a global variable.
    124 template <typename T>
    125 class ThreadLocalKeyStorage {
    126 public:
    127  constexpr ThreadLocalKeyStorage() : mInited(false) {}
    128 
    129  inline bool initialized() const { return mInited; }
    130 
    131  inline void init() { mInited = true; }
    132 
    133  inline T get() const { return mVal; }
    134 
    135  inline bool set(const T aValue) {
    136    mVal = aValue;
    137    return true;
    138  }
    139 
    140 private:
    141  bool mInited;
    142  T mVal;
    143 };
    144 #else
    145 template <typename T>
    146 class ThreadLocalKeyStorage {
    147 public:
    148  constexpr ThreadLocalKeyStorage() : mKey(0), mInited(false) {}
    149 
    150  inline bool initialized() const { return mInited; }
    151 
    152  inline void init() { mInited = !pthread_key_create(&mKey, nullptr); }
    153 
    154  inline T get() const {
    155    void* h = pthread_getspecific(mKey);
    156    return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
    157  }
    158 
    159  inline bool set(const T aValue) {
    160    const void* h = reinterpret_cast<const void*>(
    161        static_cast<typename Helper<T>::Type>(aValue));
    162    return !pthread_setspecific(mKey, h);
    163  }
    164 
    165 private:
    166  pthread_key_t mKey;
    167  bool mInited;
    168 };
    169 #endif
    170 
    171 template <typename T>
    172 class ThreadLocalNativeStorage {
    173 public:
    174  // __thread does not allow non-trivial constructors, but we can
    175  // instead rely on zero-initialization.
    176  inline bool initialized() const { return true; }
    177 
    178  inline void init() {}
    179 
    180  inline T get() const { return mValue; }
    181 
    182  inline bool set(const T aValue) {
    183    mValue = aValue;
    184    return true;
    185  }
    186 
    187 private:
    188  T mValue;
    189 };
    190 
    191 template <typename T, template <typename U> class Storage>
    192 class ThreadLocal : public Storage<T> {
    193 public:
    194  [[nodiscard]] inline bool init();
    195 
    196  void infallibleInit() {
    197    MOZ_RELEASE_ASSERT(init(), "Infallible TLS initialization failed");
    198  }
    199 
    200  inline T get() const;
    201 
    202  inline void set(const T aValue);
    203 
    204  using Type = T;
    205 };
    206 
    207 template <typename T, template <typename U> class Storage>
    208 inline bool ThreadLocal<T, Storage>::init() {
    209  static_assert(std::is_pointer_v<T> || std::is_integral_v<T>,
    210                "mozilla::ThreadLocal must be used with a pointer or "
    211                "integral type");
    212  static_assert(sizeof(T) <= sizeof(void*),
    213                "mozilla::ThreadLocal can't be used for types larger than "
    214                "a pointer");
    215 
    216  if (!Storage<T>::initialized()) {
    217    Storage<T>::init();
    218  }
    219  return Storage<T>::initialized();
    220 }
    221 
    222 template <typename T, template <typename U> class Storage>
    223 inline T ThreadLocal<T, Storage>::get() const {
    224  MOZ_ASSERT(Storage<T>::initialized());
    225  return Storage<T>::get();
    226 }
    227 
    228 template <typename T, template <typename U> class Storage>
    229 inline void ThreadLocal<T, Storage>::set(const T aValue) {
    230  MOZ_ASSERT(Storage<T>::initialized());
    231  bool succeeded = Storage<T>::set(aValue);
    232  if (!succeeded) {
    233    MOZ_CRASH();
    234  }
    235 }
    236 
    237 #if (defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)) && \
    238    !defined(__MINGW32__)
    239 #  define MOZ_THREAD_LOCAL(TYPE)                 \
    240    thread_local ::mozilla::detail::ThreadLocal< \
    241        TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
    242 #elif defined(HAVE_THREAD_TLS_KEYWORD) && !defined(MOZ_LINKER)
    243 #  define MOZ_THREAD_LOCAL(TYPE)             \
    244    __thread ::mozilla::detail::ThreadLocal< \
    245        TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
    246 #else
    247 #  define MOZ_THREAD_LOCAL(TYPE)         \
    248    ::mozilla::detail::ThreadLocal<TYPE, \
    249                                   ::mozilla::detail::ThreadLocalKeyStorage>
    250 #endif
    251 
    252 }  // namespace detail
    253 }  // namespace mozilla
    254 
    255 #endif /* mozilla_ThreadLocal_h */