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
16 #include <type_traits>
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Attributes.h"
26 # if defined(__has_feature)
27 # if __has_feature(cxx_thread_local)
28 # define MACOSX_HAS_THREAD_LOCAL
34 * Thread Local Storage helpers.
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.
43 * extern MOZ_THREAD_LOCAL(int) tlsInt;
45 * MOZ_THREAD_LOCAL(int) tlsInt;
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.
56 * // Create a TLS item.
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
66 * // Set the TLS value
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.
79 typedef uintptr_t Type
;
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 */
100 class ThreadLocalKeyStorage
{
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
);
124 template <typename T
>
125 class ThreadLocalKeyStorage
{
127 constexpr ThreadLocalKeyStorage() : mKey(0), mInited(false) {}
129 inline bool initialized() const { return mInited
; }
131 inline void init() { mInited
= !pthread_key_create(&mKey
, nullptr); }
133 inline T
get() const {
134 void* h
= pthread_getspecific(mKey
);
135 return static_cast<T
>(reinterpret_cast<typename Helper
<T
>::Type
>(h
));
138 inline bool set(const T aValue
) {
139 const void* h
= reinterpret_cast<const void*>(
140 static_cast<typename Helper
<T
>::Type
>(aValue
));
141 return !pthread_setspecific(mKey
, h
);
150 template <typename T
>
151 class ThreadLocalNativeStorage
{
153 // __thread does not allow non-trivial constructors, but we can
154 // instead rely on zero-initialization.
155 inline bool initialized() const { return true; }
157 inline void init() {}
159 inline T
get() const { return mValue
; }
161 inline bool set(const T aValue
) {
170 template <typename T
, template <typename U
> class Storage
>
171 class ThreadLocal
: public Storage
<T
> {
173 MOZ_MUST_USE
inline bool init();
175 void infallibleInit() {
176 MOZ_RELEASE_ASSERT(init(), "Infallible TLS initialization failed");
179 inline T
get() const;
181 inline void set(const T aValue
);
186 template <typename T
, template <typename U
> class Storage
>
187 inline bool ThreadLocal
<T
, Storage
>::init() {
188 static_assert(std::is_pointer_v
<T
> || std::is_integral_v
<T
>,
189 "mozilla::ThreadLocal must be used with a pointer or "
191 static_assert(sizeof(T
) <= sizeof(void*),
192 "mozilla::ThreadLocal can't be used for types larger than "
195 if (!Storage
<T
>::initialized()) {
198 return Storage
<T
>::initialized();
201 template <typename T
, template <typename U
> class Storage
>
202 inline T ThreadLocal
<T
, Storage
>::get() const {
203 MOZ_ASSERT(Storage
<T
>::initialized());
204 return Storage
<T
>::get();
207 template <typename T
, template <typename U
> class Storage
>
208 inline void ThreadLocal
<T
, Storage
>::set(const T aValue
) {
209 MOZ_ASSERT(Storage
<T
>::initialized());
210 bool succeeded
= Storage
<T
>::set(aValue
);
216 #if (defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)) && \
217 !defined(__MINGW32__)
218 # define MOZ_THREAD_LOCAL(TYPE) \
219 thread_local ::mozilla::detail::ThreadLocal< \
220 TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
221 #elif defined(HAVE_THREAD_TLS_KEYWORD)
222 # define MOZ_THREAD_LOCAL(TYPE) \
223 __thread ::mozilla::detail::ThreadLocal< \
224 TYPE, ::mozilla::detail::ThreadLocalNativeStorage>
226 # define MOZ_THREAD_LOCAL(TYPE) \
227 ::mozilla::detail::ThreadLocal<TYPE, \
228 ::mozilla::detail::ThreadLocalKeyStorage>
231 } // namespace detail
232 } // namespace mozilla
234 #endif /* mozilla_ThreadLocal_h */