2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_THREAD_LOCAL_H_
18 #define incl_HPHP_THREAD_LOCAL_H_
22 #include <type_traits>
24 #include <folly/String.h>
26 #include "hphp/util/alloc.h"
27 #include "hphp/util/exception.h"
28 #include "hphp/util/type-scan.h"
32 // return the location of the current thread's tdata section
33 std::pair
<void*,size_t> getCppTdata();
36 extern "C" int _tls_index
;
39 inline uintptr_t tlsBase() {
41 #if defined(__x86_64__)
42 asm ("movq %%fs:0, %0" : "=r" (retval
));
43 #elif defined(__AARCH64EL__)
44 // mrs == "move register <-- system"
45 // tpidr_el0 == "thread process id register for exception level 0"
46 asm ("mrs %0, tpidr_el0" : "=r" (retval
));
47 #elif defined (__powerpc64__)
48 asm ("xor %0,%0,%0\n\t"
52 retval
= (uintptr_t)__readgsqword(88);
53 retval
= *(uintptr_t*)(retval
+ (_tls_index
* 8));
55 # error How do you access thread-local storage on this machine?
60 ///////////////////////////////////////////////////////////////////////////////
61 // gcc >= 4.3.0 supports the '__thread' keyword for thread locals
63 // Clang seems to have added this feature, or at the very least it is ignoring
64 // __thread keyword and compiling anyway
66 // On OSX, gcc does emulate TLS but in a manner that invalidates assumptions
67 // we have made about __thread and makes accessing thread-local variables in a
68 // JIT-friendly fashion difficult (as the compiler is doing a lot of magic that
69 // is not contractual or documented that we would need to duplicate in emitted
70 // code) so for now we're not going to use it. One possibility if we really
71 // want to do this is to generate functions that access variables of interest
72 // in ThreadLocal* (all of them are NoCheck right now) and use the bytes of
73 // gcc's compiled functions to find the values we would need to pass to
74 // __emutls_get_address.
76 // icc 13.0.0 appears to support it as well but we end up with
77 // assembler warnings of unknown importance about incorrect section
80 // __thread on cygwin and mingw uses pthreads emulation not native tls so
81 // the emulation for thread local must be used as well
83 // So we use __thread on gcc, icc and clang, and OSX, falling back to
84 // emulation on unsupported platforms. Use the THREAD_LOCAL() macro to
85 // hide the decl details.
87 // See thread-local-emulate.h for the emulated versions; they're in a
88 // separate header to avoid confusion; this is a long header file and it's
89 // easy to lose track of which version you're looking at.
91 #if !defined(NO_TLS) && \
92 ((__llvm__ && __clang__) || \
93 __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || \
94 __INTEL_COMPILER || defined(_MSC_VER))
95 #define USE_GCC_FAST_TLS
98 ///////////////////////////////////////////////////////////////////////////////
101 inline void ThreadLocalCheckReturn(int ret
, const char *funcName
) {
103 // This is used from global constructors so the safest thing to do is just
104 // print to stderr and exit().
105 fprintf(stderr
, "%s returned %d: %s", funcName
, ret
,
106 folly::errnoStr(ret
).c_str());
111 inline void ThreadLocalCreateKey(pthread_key_t
*key
, void (*del
)(void*)) {
112 int ret
= pthread_key_create(key
, del
);
113 ThreadLocalCheckReturn(ret
, "pthread_key_create");
116 inline void ThreadLocalSetValue(pthread_key_t key
, const void* value
) {
117 int ret
= pthread_setspecific(key
, value
);
118 ThreadLocalCheckReturn(ret
, "pthread_setspecific");
122 typedef struct __darwin_pthread_handler_rec darwin_pthread_handler
;
127 ///////////////////////////////////////////////////////////////////////////////
130 * A thread-local object is a "global" object within a thread. This is useful
131 * for writing apartment-threaded code, where nothing is actually shared
132 * between different threads (hence no locking) but those variables are not
133 * on stack in local scope. To use it, just do something like this,
135 * THREAD_LOCAL(MyClass, static_object);
136 * static_object->data_ = ...;
137 * static_object->doSomething();
139 * THREAD_LOCAL(int, static_number);
140 * int value = *static_number;
142 * So, syntax-wise it's similar to pointers. The type parameter can be a
143 * primitive types. If it's a class, there has to be a default constructor.
146 ///////////////////////////////////////////////////////////////////////////////
147 #if defined(USE_GCC_FAST_TLS)
151 * We keep a linked list of destructors in ThreadLocalManager to be called on
152 * thread exit. ThreadLocalNode is a node in this list.
154 template <typename T
>
155 struct ThreadLocalNode
{
157 void (*m_on_thread_exit_fn
)(void * p
);
158 void (*m_init_tyindex
)(void*);
161 type_scan::Index m_tyindex
;
162 static void init_tyindex(void* node_ptr
) {
163 auto node
= static_cast<ThreadLocalNode
<T
>*>(node_ptr
);
164 node
->m_tyindex
= type_scan::getIndexForScan
<T
>();
168 struct ThreadLocalManager
{
170 static void PushTop(ThreadLocalNode
<T
>& node
) {
171 static_assert(sizeof(T
) <= 0xffffffffu
, "");
172 PushTop(&node
, sizeof(T
), type_scan::getIndexForScan
<T
>());
173 node
.m_init_tyindex
= &node
.init_tyindex
;
175 template<class Fn
> void iterate(Fn fn
) const;
176 void initTypeIndices();
177 static ThreadLocalManager
& GetManager();
180 static void PushTop(void* node
, uint32_t size
, type_scan::Index
);
181 struct ThreadLocalList
{
185 darwin_pthread_handler handler
;
188 static ThreadLocalList
* getList(void* p
) {
189 return static_cast<ThreadLocalList
*>(p
);
191 ThreadLocalManager() : m_key(0) {
193 ThreadLocalCreateKey(&m_key
, nullptr);
195 ThreadLocalCreateKey(&m_key
, ThreadLocalManager::OnThreadExit
);
198 static void OnThreadExit(void *p
);
202 ///////////////////////////////////////////////////////////////////////////////
203 // ThreadLocal allocates in the local arena, created using local_malloc()
204 // followed by placement new and destroyed by calling destructor followed by
205 // local_free() delete.
208 void ThreadLocalOnThreadExit(void * p
) {
209 auto pNode
= (ThreadLocalNode
<T
>*)p
;
212 local_free(pNode
->m_p
);
213 pNode
->m_p
= nullptr;
218 * The USE_GCC_FAST_TLS implementation of ThreadLocal is just a lazy-initialized
219 * pointer wrapper. In this case, we have one ThreadLocal object per thread.
221 template<bool Check
, typename T
>
222 struct ThreadLocalImpl
{
224 NEVER_INLINE T
* getCheck() const {
228 T
* getNoCheck() const {
234 if (m_node
.m_p
== nullptr) {
235 const_cast<ThreadLocalImpl
<Check
,T
>*>(this)->create();
240 bool isNull() const { return m_node
.m_p
== nullptr; }
245 local_free(m_node
.m_p
);
246 m_node
.m_p
= nullptr;
251 m_node
.m_p
= nullptr;
254 T
* operator->() const {
255 return Check
? get() : getNoCheck();
258 T
& operator*() const {
259 return Check
? *get() : *getNoCheck();
262 static size_t node_ptr_offset() {
263 using Self
= ThreadLocalImpl
<Check
,T
>;
264 return offsetof(Self
, m_node
) + offsetof(ThreadLocalNode
<T
>, m_p
);
268 NEVER_INLINE
void create();
269 ThreadLocalNode
<T
> m_node
;
272 template<bool Check
, typename T
>
273 void ThreadLocalImpl
<Check
,T
>::create() {
274 if (m_node
.m_on_thread_exit_fn
== nullptr) {
275 m_node
.m_on_thread_exit_fn
= ThreadLocalOnThreadExit
<T
>;
276 ThreadLocalManager::PushTop(m_node
);
278 assert(m_node
.m_p
== nullptr);
279 m_node
.m_p
= new (local_malloc(sizeof(T
))) T();
282 template<typename T
> using ThreadLocal
= ThreadLocalImpl
<true,T
>;
283 template<typename T
> using ThreadLocalNoCheck
= ThreadLocalImpl
<false,T
>;
285 // Wraps a __thread storage instance of T with a similar api to
286 // ThreadLocalProxy. Importantly, inlining a method of T via operator-> or
287 // operator* allows the C++ compiler to emit direct access to fields of
288 // T using the native thread-local segment pointer.
290 struct ThreadLocalFlat
{
291 T
* get() const { return (T
*)&m_value
; }
292 bool isNull() const { return !m_node
.m_p
; }
293 T
* getNoCheck() const { return get(); }
294 T
* operator->() const { return get(); }
295 T
& operator*() const { return *get(); }
296 explicit operator bool() const { return !isNull(); }
297 static void onThreadExit(void* p
) {
298 auto node
= (ThreadLocalNode
<ThreadLocalFlat
<T
>>*)p
;
300 auto value
= (T
*)&node
->m_p
->m_value
;
307 if (!m_node
.m_on_thread_exit_fn
) {
308 m_node
.m_on_thread_exit_fn
= onThreadExit
;
309 ThreadLocalManager::PushTop(m_node
);
314 assert(m_node
.m_p
== this);
315 assert(m_node
.m_on_thread_exit_fn
);
323 m_node
.m_p
= nullptr;
327 // We manage initialization explicitly
328 typename
std::aligned_storage
<sizeof(T
),alignof(T
)>::type m_value
;
329 ThreadLocalNode
<ThreadLocalFlat
<T
>> m_node
;
331 scanner
.scan(*get());
336 * How to use the thread-local macros:
338 * Use THREAD_LOCAL to declare a *static* class field or global as thread local:
340 * static THREAD_LOCAL(SomeFieldType, field);
342 * extern THREAD_LOCAL(SomeGlobal, tl_myglobal);
344 * Use THREAD_LOCAL in the cpp file to implement the field:
345 * THREAD_LOCAL(SomeFieldType, SomeClass::f);
346 * THREAD_LOCAL(SomeGlobal, tl_myglobal);
349 #define THREAD_LOCAL(T, f) __thread HPHP::ThreadLocal<T> f
350 #define THREAD_LOCAL_NO_CHECK(T, f) __thread HPHP::ThreadLocalNoCheck<T> f
351 #define THREAD_LOCAL_FLAT(T, f) __thread HPHP::ThreadLocalFlat<T> f
355 #else /* USE_GCC_FAST_TLS */
356 #include "hphp/util/thread-local-emulate.h"
357 #endif /* USE_GCC_FAST_TLS */
359 ///////////////////////////////////////////////////////////////////////////////
361 #endif // incl_HPHP_THREAD_LOCAL_H_