Line data Source code
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)
13 : # include <pthread.h>
14 : #endif
15 :
16 : #include "mozilla/Assertions.h"
17 : #include "mozilla/Attributes.h"
18 : #include "mozilla/TypeTraits.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 : #if defined(HAVE_THREAD_TLS_KEYWORD) || defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)
33 : #define MOZ_HAS_THREAD_LOCAL
34 : #endif
35 :
36 : /*
37 : * Thread Local Storage helpers.
38 : *
39 : * Usage:
40 : *
41 : * Do not directly instantiate this class. Instead, use the
42 : * MOZ_THREAD_LOCAL macro to declare or define instances. The macro
43 : * takes a type name as its argument.
44 : *
45 : * Declare like this:
46 : * extern MOZ_THREAD_LOCAL(int) tlsInt;
47 : * Define like this:
48 : * MOZ_THREAD_LOCAL(int) tlsInt;
49 : * or:
50 : * static MOZ_THREAD_LOCAL(int) tlsInt;
51 : *
52 : * Only static-storage-duration (e.g. global variables, or static class members)
53 : * objects of this class should be instantiated. This class relies on
54 : * zero-initialization, which is implicit for static-storage-duration objects.
55 : * It doesn't have a custom default constructor, to avoid static initializers.
56 : *
57 : * API usage:
58 : *
59 : * // Create a TLS item.
60 : * //
61 : * // Note that init() should be invoked before the first use of set()
62 : * // or get(). It is ok to call it multiple times. This must be
63 : * // called in a way that avoids possible races with other threads.
64 : * MOZ_THREAD_LOCAL(int) tlsKey;
65 : * if (!tlsKey.init()) {
66 : * // deal with the error
67 : * }
68 : *
69 : * // Set the TLS value
70 : * tlsKey.set(123);
71 : *
72 : * // Get the TLS value
73 : * int value = tlsKey.get();
74 : */
75 : template<typename T>
76 : class ThreadLocal
77 : {
78 : #ifndef MOZ_HAS_THREAD_LOCAL
79 : typedef pthread_key_t key_t;
80 :
81 : // Integral types narrower than void* must be extended to avoid
82 : // warnings from valgrind on some platforms. This helper type
83 : // achieves that without penalizing the common case of ThreadLocals
84 : // instantiated using a pointer type.
85 : template<typename S>
86 : struct Helper
87 : {
88 : typedef uintptr_t Type;
89 : };
90 :
91 : template<typename S>
92 : struct Helper<S *>
93 : {
94 : typedef S *Type;
95 : };
96 : #endif
97 :
98 : bool initialized() const {
99 : #ifdef MOZ_HAS_THREAD_LOCAL
100 : return true;
101 : #else
102 : return mInited;
103 : #endif
104 : }
105 :
106 : public:
107 : // __thread does not allow non-trivial constructors, but we can
108 : // instead rely on zero-initialization.
109 : #ifndef MOZ_HAS_THREAD_LOCAL
110 : ThreadLocal()
111 : : mKey(0), mInited(false)
112 : {}
113 : #endif
114 :
115 : MOZ_MUST_USE inline bool init();
116 :
117 : inline T get() const;
118 :
119 : inline void set(const T aValue);
120 :
121 : private:
122 : #ifdef MOZ_HAS_THREAD_LOCAL
123 : T mValue;
124 : #else
125 : key_t mKey;
126 : bool mInited;
127 : #endif
128 : };
129 :
130 : template<typename T>
131 : inline bool
132 358986 : ThreadLocal<T>::init()
133 : {
134 : static_assert(mozilla::IsPointer<T>::value || mozilla::IsIntegral<T>::value,
135 : "mozilla::ThreadLocal must be used with a pointer or "
136 : "integral type");
137 : static_assert(sizeof(T) <= sizeof(void*),
138 : "mozilla::ThreadLocal can't be used for types larger than "
139 : "a pointer");
140 :
141 : #ifdef MOZ_HAS_THREAD_LOCAL
142 358986 : return true;
143 : #else
144 : if (!initialized()) {
145 : mInited = !pthread_key_create(&mKey, nullptr);
146 : }
147 : return mInited;
148 : #endif
149 : }
150 :
151 : template<typename T>
152 : inline T
153 125212708 : ThreadLocal<T>::get() const
154 : {
155 : #ifdef MOZ_HAS_THREAD_LOCAL
156 125212708 : return mValue;
157 : #else
158 : MOZ_ASSERT(initialized());
159 : void* h;
160 : h = pthread_getspecific(mKey);
161 : return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
162 : #endif
163 : }
164 :
165 : template<typename T>
166 : inline void
167 24337 : ThreadLocal<T>::set(const T aValue)
168 : {
169 : #ifdef MOZ_HAS_THREAD_LOCAL
170 24337 : mValue = aValue;
171 : #else
172 : MOZ_ASSERT(initialized());
173 : void* h = reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
174 : bool succeeded = !pthread_setspecific(mKey, h);
175 : if (!succeeded) {
176 : MOZ_CRASH();
177 : }
178 : #endif
179 24337 : }
180 :
181 : #ifdef MOZ_HAS_THREAD_LOCAL
182 : #if defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)
183 : #define MOZ_THREAD_LOCAL(TYPE) thread_local mozilla::detail::ThreadLocal<TYPE>
184 : #else
185 : #define MOZ_THREAD_LOCAL(TYPE) __thread mozilla::detail::ThreadLocal<TYPE>
186 : #endif
187 : #else
188 : #define MOZ_THREAD_LOCAL(TYPE) mozilla::detail::ThreadLocal<TYPE>
189 : #endif
190 :
191 : } // namespace detail
192 : } // namespace mozilla
193 :
194 : #endif /* mozilla_ThreadLocal_h */
|