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 */