2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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_
21 #include "hphp/util/exception.h"
23 #include "hphp/util/util.h"
24 #include "folly/String.h"
25 #include <boost/aligned_storage.hpp>
29 ///////////////////////////////////////////////////////////////////////////////
30 // gcc >= 4.3.0 supports the '__thread' keyword for thread locals
32 // Clang seems to have added this feature, or at the very least it is ignoring
33 // __thread keyword and compiling anyway
35 // On OSX, gcc does emulate TLS but in a manner that invalidates assumptions
36 // we have made about __thread and makes accessing thread-local variables in a
37 // JIT-friendly fashion difficult (as the compiler is doing a lot of magic that
38 // is not contractual or documented that we would need to duplicate in emitted
39 // code) so for now we're not going to use it. One possibility if we really
40 // want to do this is to generate functions that access variables of interest
41 // in ThreadLocal* (all of them are NoCheck right now) and use the bytes of
42 // gcc's compiled functions to find the values we would need to pass to
43 // __emutls_get_address.
45 // icc 13.0.0 appears to support it as well but we end up with
46 // assembler warnings of unknown importance about incorrect section
49 #if !defined(NO_TLS) && !defined(__APPLE__) && \
50 ((__llvm__ && __clang__) || \
51 __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || \
53 #define USE_GCC_FAST_TLS
56 ///////////////////////////////////////////////////////////////////////////////
59 inline void ThreadLocalCheckReturn(int ret
, const char *funcName
) {
61 // This is used from global constructors so the safest thing to do is just
62 // print to stderr and exit().
63 fprintf(stderr
, "%s returned %d: %s", funcName
, ret
,
64 folly::errnoStr(ret
).c_str());
69 inline void ThreadLocalCreateKey(pthread_key_t
*key
, void (*del
)(void*)) {
70 int ret
= pthread_key_create(key
, del
);
71 ThreadLocalCheckReturn(ret
, "pthread_key_create");
74 inline void ThreadLocalSetValue(pthread_key_t key
, const void* value
) {
75 int ret
= pthread_setspecific(key
, value
);
76 ThreadLocalCheckReturn(ret
, "pthread_setspecific");
79 ///////////////////////////////////////////////////////////////////////////////
82 * A thread-local object is a "global" object within a thread. This is useful
83 * for writing apartment-threaded code, where nothing is actually shared
84 * between different threads (hence no locking) but those variables are not
85 * on stack in local scope. To use it, just do something like this,
87 * ThreadLocal<MyClass> static_object;
88 * static_object->data_ = ...;
89 * static_object->doSomething();
91 * ThreadLocal<int> static_number;
92 * int value = *static_number;
94 * So, syntax-wise it's similar to pointers. T can be primitive types, and if
95 * it's a class, there has to be a default constructor.
98 ///////////////////////////////////////////////////////////////////////////////
99 #if defined(USE_GCC_FAST_TLS)
101 template <typename T
>
102 struct ThreadLocalNode
{
104 void (*m_on_thread_exit_fn
)(void * p
);
108 struct ThreadLocalManager
{
109 ThreadLocalManager() : m_key(0) {
110 ThreadLocalCreateKey(&m_key
, ThreadLocalManager::OnThreadExit
);
113 return pthread_getspecific(m_key
);
115 void setTop(void * p
) {
116 ThreadLocalSetValue(m_key
, p
);
118 static void OnThreadExit(void *p
);
121 static ThreadLocalManager s_manager
;
124 ///////////////////////////////////////////////////////////////////////////////
125 // ThreadLocal allocates by calling new without parameters and frees by calling
129 void ThreadLocalOnThreadExit(void * p
) {
130 ThreadLocalNode
<T
> * pNode
= (ThreadLocalNode
<T
>*)p
;
132 pNode
->m_p
= nullptr;
138 if (m_node
.m_p
== nullptr) {
139 const_cast<ThreadLocal
<T
>*>(this)->create();
144 void create() NEVER_INLINE
;
146 bool isNull() const { return m_node
.m_p
== nullptr; }
150 m_node
.m_p
= nullptr;
154 m_node
.m_p
= nullptr;
157 T
*operator->() const {
161 T
&operator*() const {
165 ThreadLocalNode
<T
> m_node
;
169 void ThreadLocal
<T
>::create() {
170 if (m_node
.m_on_thread_exit_fn
== nullptr) {
171 m_node
.m_on_thread_exit_fn
= ThreadLocalOnThreadExit
<T
>;
172 m_node
.m_next
= ThreadLocalManager::s_manager
.getTop();
173 ThreadLocalManager::s_manager
.setTop((void*)(&m_node
));
175 assert(m_node
.m_p
== nullptr);
176 m_node
.m_p
= new T();
180 struct ThreadLocalNoCheck
{
181 T
*getCheck() const ATTRIBUTE_COLD NEVER_INLINE
;
182 T
* getNoCheck() const {
187 void create() NEVER_INLINE
;
189 bool isNull() const { return m_node
.m_p
== nullptr; }
193 m_node
.m_p
= nullptr;
196 T
*operator->() const {
200 T
&operator*() const {
201 return *getNoCheck();
204 ThreadLocalNode
<T
> m_node
;
208 void ThreadLocalNoCheck
<T
>::create() {
209 if (m_node
.m_on_thread_exit_fn
== nullptr) {
210 m_node
.m_on_thread_exit_fn
= ThreadLocalOnThreadExit
<T
>;
211 m_node
.m_next
= ThreadLocalManager::s_manager
.getTop();
212 ThreadLocalManager::s_manager
.setTop((void*)(&m_node
));
214 assert(m_node
.m_p
== nullptr);
215 m_node
.m_p
= new T();
218 T
*ThreadLocalNoCheck
<T
>::getCheck() const {
219 if (m_node
.m_p
== nullptr) {
220 const_cast<ThreadLocalNoCheck
<T
>*>(this)->create();
226 ///////////////////////////////////////////////////////////////////////////////
227 // Singleton thread-local storage for T
230 void ThreadLocalSingletonOnThreadExit(void *obj
) {
231 T::OnThreadExit((T
*)obj
);
234 // ThreadLocalSingleton has NoCheck property
235 template <typename T
>
236 class ThreadLocalSingleton
{
238 ThreadLocalSingleton() { s_inited
= true; }
240 static T
*getCheck() ATTRIBUTE_COLD NEVER_INLINE
;
242 static T
* getNoCheck() {
244 assert(s_singleton
== (T
*)&s_storage
);
245 return (T
*)&s_storage
;
248 static bool isNull() { return s_singleton
== nullptr; }
250 static void destroy() {
251 assert(!s_singleton
|| s_singleton
== (T
*)&s_storage
);
255 s_singleton
= nullptr;
259 T
*operator->() const {
263 T
&operator*() const {
264 return *getNoCheck();
268 static __thread T
*s_singleton
;
269 typedef typename
boost::aligned_storage
<sizeof(T
), sizeof(void*)>::type
271 static __thread StorageType s_storage
;
272 static bool s_inited
; // no-fast-TLS requires construction so be consistent
276 bool ThreadLocalSingleton
<T
>::s_inited
= false;
279 T
*ThreadLocalSingleton
<T
>::getCheck() {
282 T
* p
= (T
*) &s_storage
;
289 template<typename T
> __thread T
*ThreadLocalSingleton
<T
>::s_singleton
;
290 template<typename T
> __thread typename ThreadLocalSingleton
<T
>::StorageType
291 ThreadLocalSingleton
<T
>::s_storage
;
294 ///////////////////////////////////////////////////////////////////////////////
295 // some classes don't need new/delete at all
297 template<typename T
, bool throwOnNull
= true>
298 struct ThreadLocalProxy
{
300 if (m_p
== nullptr && throwOnNull
) {
301 throw Exception("ThreadLocalProxy::get() called before set()");
310 bool isNull() const { return m_p
== nullptr; }
316 T
*operator->() const {
320 T
&operator*() const {
328 * How to use the thread-local macros:
330 * Use DECLARE_THREAD_LOCAL to declare a *static* class field as thread local:
332 * static DECLARE_THREAD_LOCAL(SomeFieldType, f);
335 * Use IMPLEMENT_THREAD_LOCAL in the cpp file to implement the field:
336 * IMPLEMENT_THREAD_LOCAL(SomeFieldType, SomeClass::f);
338 * Remember: *Never* write IMPLEMENT_THREAD_LOCAL in a header file.
341 #define DECLARE_THREAD_LOCAL(T, f) \
342 __thread ThreadLocal<T> f
343 #define IMPLEMENT_THREAD_LOCAL(T, f) \
344 __thread HPHP::ThreadLocal<T> f
346 #define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) \
347 __thread ThreadLocalNoCheck<T> f
348 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) \
349 __thread ThreadLocalNoCheck<T> f
351 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(T, f) \
352 __attribute((section(".tbss.hot"))) \
353 __thread ThreadLocalNoCheck<T> f
355 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(T, f) \
356 __attribute((section(".tbss.hot,"))) \
357 __thread ThreadLocalNoCheck<T> f
360 #define DECLARE_THREAD_LOCAL_PROXY(T, N, f) \
361 __thread ThreadLocalProxy<T, N> f
362 #define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) \
363 __thread ThreadLocalProxy<T, N> f
365 #else /* USE_GCC_FAST_TLS */
367 ///////////////////////////////////////////////////////////////////////////////
368 // ThreadLocal allocates by calling new() without parameters
371 void ThreadLocalOnThreadExit(void *p
) {
379 * Constructor that has to be called from a thread-neutral place.
381 ThreadLocal() : m_key(0) {
382 ThreadLocalCreateKey(&m_key
, ThreadLocalOnThreadExit
<T
>);
386 T
*obj
= (T
*)pthread_getspecific(m_key
);
387 if (obj
== nullptr) {
389 ThreadLocalSetValue(m_key
, obj
);
394 bool isNull() const { return pthread_getspecific(m_key
) == nullptr; }
397 delete (T
*)pthread_getspecific(m_key
);
398 ThreadLocalSetValue(m_key
, nullptr);
402 ThreadLocalSetValue(m_key
, nullptr);
406 * Access object's member or method through this operator overload.
408 T
*operator->() const {
412 T
&operator*() const {
421 class ThreadLocalNoCheck
{
424 * Constructor that has to be called from a thread-neutral place.
426 ThreadLocalNoCheck() : m_key(0) {
427 ThreadLocalCreateKey(&m_key
, ThreadLocalOnThreadExit
<T
>);
430 T
*getCheck() const ATTRIBUTE_COLD NEVER_INLINE
;
432 T
* getNoCheck() const {
433 T
*obj
= (T
*)pthread_getspecific(m_key
);
438 bool isNull() const { return pthread_getspecific(m_key
) == nullptr; }
441 delete (T
*)pthread_getspecific(m_key
);
442 ThreadLocalSetValue(m_key
, nullptr);
446 * Access object's member or method through this operator overload.
448 T
*operator->() const {
452 T
&operator*() const {
453 return *getNoCheck();
461 T
*ThreadLocalNoCheck
<T
>::getCheck() const {
462 T
*obj
= (T
*)pthread_getspecific(m_key
);
463 if (obj
== nullptr) {
465 ThreadLocalSetValue(m_key
, obj
);
470 ///////////////////////////////////////////////////////////////////////////////
471 // Singleton thread-local storage for T
474 void ThreadLocalSingletonOnThreadExit(void *obj
) {
475 T::OnThreadExit((T
*)obj
);
479 // ThreadLocalSingleton has NoCheck property
481 class ThreadLocalSingleton
{
483 ThreadLocalSingleton() { getKey(); }
485 static T
*getCheck() ATTRIBUTE_COLD NEVER_INLINE
;
486 static T
* getNoCheck() {
488 T
*obj
= (T
*)pthread_getspecific(s_key
);
493 static bool isNull() { return pthread_getspecific(s_key
) == nullptr; }
495 static void destroy() {
496 void* p
= pthread_getspecific(s_key
);
499 ThreadLocalSetValue(s_key
, nullptr);
502 T
*operator->() const {
506 T
&operator*() const {
507 return *getNoCheck();
511 static pthread_key_t s_key
;
512 static bool s_inited
; // pthread_key_t has no portable valid sentinel
514 static pthread_key_t
getKey() {
517 ThreadLocalCreateKey(&s_key
, ThreadLocalSingletonOnThreadExit
<T
>);
524 T
*ThreadLocalSingleton
<T
>::getCheck() {
526 T
*obj
= (T
*)pthread_getspecific(s_key
);
527 if (obj
== nullptr) {
528 obj
= (T
*)malloc(sizeof(T
));
530 ThreadLocalSetValue(s_key
, obj
);
536 pthread_key_t ThreadLocalSingleton
<T
>::s_key
;
538 bool ThreadLocalSingleton
<T
>::s_inited
= false;
540 ///////////////////////////////////////////////////////////////////////////////
541 // some classes don't need new/delete at all
543 template<typename T
, bool throwOnNull
= true>
544 class ThreadLocalProxy
{
547 * Constructor that has to be called from a thread-neutral place.
549 ThreadLocalProxy() : m_key(0) {
550 ThreadLocalCreateKey(&m_key
, nullptr);
554 T
*obj
= (T
*)pthread_getspecific(m_key
);
555 if (obj
== nullptr && throwOnNull
) {
556 throw Exception("ThreadLocalProxy::get() called before set()");
562 ThreadLocalSetValue(m_key
, obj
);
565 bool isNull() const { return pthread_getspecific(m_key
) == nullptr; }
568 ThreadLocalSetValue(m_key
, nullptr);
572 * Access object's member or method through this operator overload.
574 T
*operator->() const {
578 T
&operator*() const {
586 #define DECLARE_THREAD_LOCAL(T, f) ThreadLocal<T> f
587 #define IMPLEMENT_THREAD_LOCAL(T, f) ThreadLocal<T> f
589 #define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) ThreadLocalNoCheck<T> f
590 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) ThreadLocalNoCheck<T> f
591 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(T, f) ThreadLocalNoCheck<T> f
593 #define DECLARE_THREAD_LOCAL_PROXY(T, N, f) ThreadLocalProxy<T, N> f
594 #define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) ThreadLocalProxy<T, N> f
596 #endif /* USE_GCC_FAST_TLS */
598 ///////////////////////////////////////////////////////////////////////////////
601 #endif // incl_HPHP_THREAD_LOCAL_H_