Bug 1836072 [wpt PR 40324] - Fix scrollbar-width repaint issues on non viewports...
[gecko.git] / mfbt / ThreadLocal.h
blob55c9fbcac69c029d8b7aa9a7dbd68b498366a666
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/. */
7 /* Cross-platform lightweight thread local data wrappers. */
9 #ifndef mozilla_ThreadLocal_h
10 #define mozilla_ThreadLocal_h
12 #if !defined(XP_WIN) && !defined(__wasi__)
13 # include <pthread.h>
14 #endif
16 #include <type_traits>
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Attributes.h"
21 namespace mozilla {
23 namespace detail {
25 #ifdef XP_MACOSX
26 # if defined(__has_feature)
27 # if __has_feature(cxx_thread_local)
28 # define MACOSX_HAS_THREAD_LOCAL
29 # endif
30 # endif
31 #endif
34 * Thread Local Storage helpers.
36 * Usage:
38 * Do not directly instantiate this class. Instead, use the
39 * MOZ_THREAD_LOCAL macro to declare or define instances. The macro
40 * takes a type name as its argument.
42 * Declare like this:
43 * extern MOZ_THREAD_LOCAL(int) tlsInt;
44 * Define like this:
45 * MOZ_THREAD_LOCAL(int) tlsInt;
46 * or:
47 * static MOZ_THREAD_LOCAL(int) tlsInt;
49 * Only static-storage-duration (e.g. global variables, or static class members)
50 * objects of this class should be instantiated. This class relies on
51 * zero-initialization, which is implicit for static-storage-duration objects.
52 * It doesn't have a custom default constructor, to avoid static initializers.
54 * API usage:
56 * // Create a TLS item.
57 * //
58 * // Note that init() should be invoked before the first use of set()
59 * // or get(). It is ok to call it multiple times. This must be
60 * // called in a way that avoids possible races with other threads.
61 * MOZ_THREAD_LOCAL(int) tlsKey;
62 * if (!tlsKey.init()) {
63 * // deal with the error
64 * }
66 * // Set the TLS value
67 * tlsKey.set(123);
69 * // Get the TLS value
70 * int value = tlsKey.get();
73 // Integral types narrower than void* must be extended to avoid
74 // warnings from valgrind on some platforms. This helper type
75 // achieves that without penalizing the common case of ThreadLocals
76 // instantiated using a pointer type.
77 template <typename S>
78 struct Helper {
79 typedef uintptr_t Type;
82 template <typename S>
83 struct Helper<S*> {
84 typedef S* Type;
87 #if defined(XP_WIN)
89 * ThreadLocalKeyStorage uses Thread Local APIs that are declared in
90 * processthreadsapi.h. To use this class on Windows, include that file
91 * (or windows.h) before including ThreadLocal.h.
93 * TLS_OUT_OF_INDEXES is a #define that is used to detect whether
94 * an appropriate header has been included prior to this file
96 # if defined(TLS_OUT_OF_INDEXES)
97 /* Despite not being used for MOZ_THREAD_LOCAL, we expose an implementation for
98 * Windows for cases where it's not desirable to use thread_local */
99 template <typename T>
100 class ThreadLocalKeyStorage {
101 public:
102 ThreadLocalKeyStorage() : mKey(TLS_OUT_OF_INDEXES) {}
104 inline bool initialized() const { return mKey != TLS_OUT_OF_INDEXES; }
106 inline void init() { mKey = TlsAlloc(); }
108 inline T get() const {
109 void* h = TlsGetValue(mKey);
110 return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
113 inline bool set(const T aValue) {
114 void* h = const_cast<void*>(reinterpret_cast<const void*>(
115 static_cast<typename Helper<T>::Type>(aValue)));
116 return TlsSetValue(mKey, h);
119 private:
120 unsigned long mKey;
122 # endif
123 #elif defined(__wasi__)
124 // There are no threads on WASI, so we just use a global variable.
125 template <typename T>
126 class ThreadLocalKeyStorage {
127 public:
128 constexpr ThreadLocalKeyStorage() : mInited(false) {}
130 inline bool initialized() const { return mInited; }
132 inline void init() { mInited = true; }
134 inline T get() const { return mVal; }
136 inline bool set(const T aValue) {
137 mVal = aValue;
138 return true;
141 private:
142 bool mInited;
143 T mVal;
145 #else
146 template <typename T>
147 class ThreadLocalKeyStorage {
148 public:
149 constexpr ThreadLocalKeyStorage() : mKey(0), mInited(false) {}
151 inline bool initialized() const { return mInited; }
153 inline void init() { mInited = !pthread_key_create(&mKey, nullptr); }
155 inline T get() const {
156 void* h = pthread_getspecific(mKey);
157 return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
160 inline bool set(const T aValue) {
161 const void* h = reinterpret_cast<const void*>(
162 static_cast<typename Helper<T>::Type>(aValue));
163 return !pthread_setspecific(mKey, h);
166 private:
167 pthread_key_t mKey;
168 bool mInited;
170 #endif
172 template <typename T>
173 class ThreadLocalNativeStorage {
174 public:
175 // __thread does not allow non-trivial constructors, but we can
176 // instead rely on zero-initialization.
177 inline bool initialized() const { return true; }
179 inline void init() {}
181 inline T get() const { return mValue; }
183 inline bool set(const T aValue) {
184 mValue = aValue;
185 return true;
188 private:
189 T mValue;
192 template <typename T, template <typename U> class Storage>
193 class ThreadLocal : public Storage<T> {
194 public:
195 [[nodiscard]] inline bool init();
197 void infallibleInit() {
198 MOZ_RELEASE_ASSERT(init(), "Infallible TLS initialization failed");
201 inline T get() const;
203 inline void set(const T aValue);
205 using Type = T;
208 template <typename T, template <typename U> class Storage>
209 inline bool ThreadLocal<T, Storage>::init() {
210 static_assert(std::is_pointer_v<T> || std::is_integral_v<T>,
211 "mozilla::ThreadLocal must be used with a pointer or "
212 "integral type");
213 static_assert(sizeof(T) <= sizeof(void*),
214 "mozilla::ThreadLocal can't be used for types larger than "
215 "a pointer");
217 if (!Storage<T>::initialized()) {
218 Storage<T>::init();
220 return Storage<T>::initialized();
223 template <typename T, template <typename U> class Storage>
224 inline T ThreadLocal<T, Storage>::get() const {
225 MOZ_ASSERT(Storage<T>::initialized());
226 return Storage<T>::get();
229 template <typename T, template <typename U> class Storage>
230 inline void ThreadLocal<T, Storage>::set(const T aValue) {
231 MOZ_ASSERT(Storage<T>::initialized());
232 bool succeeded = Storage<T>::set(aValue);
233 if (!succeeded) {
234 MOZ_CRASH();
238 #if (defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)) && \
239 !defined(__MINGW32__)
240 # define MOZ_THREAD_LOCAL(TYPE) \
241 thread_local ::mozilla::detail::ThreadLocal< \
242 TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
243 #elif defined(HAVE_THREAD_TLS_KEYWORD)
244 # define MOZ_THREAD_LOCAL(TYPE) \
245 __thread ::mozilla::detail::ThreadLocal< \
246 TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
247 #else
248 # define MOZ_THREAD_LOCAL(TYPE) \
249 ::mozilla::detail::ThreadLocal<TYPE, \
250 ::mozilla::detail::ThreadLocalKeyStorage>
251 #endif
253 } // namespace detail
254 } // namespace mozilla
256 #endif /* mozilla_ThreadLocal_h */