make (array) of ArrayObject return the contents
[hiphop-php.git] / hphp / util / thread-local.h
blob41a73d3b0859b548740600de0cf803e0fef521c7
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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_
20 #include <pthread.h>
21 #include "hphp/util/exception.h"
22 #include <errno.h>
23 #include "hphp/util/util.h"
24 #include "folly/String.h"
25 #include <boost/aligned_storage.hpp>
27 namespace HPHP {
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
47 // types
49 #if !defined(NO_TLS) && !defined(__APPLE__) && \
50 ((__llvm__ && __clang__) || \
51 __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || \
52 __INTEL_COMPILER)
53 #define USE_GCC_FAST_TLS
54 #endif
56 ///////////////////////////////////////////////////////////////////////////////
57 // helper
59 inline void ThreadLocalCheckReturn(int ret, const char *funcName) {
60 if (ret != 0) {
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());
65 exit(1);
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 ///////////////////////////////////////////////////////////////////////////////
81 /**
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 {
103 T * m_p;
104 void (*m_on_thread_exit_fn)(void * p);
105 void * m_next;
108 struct ThreadLocalManager {
109 ThreadLocalManager() : m_key(0) {
110 ThreadLocalCreateKey(&m_key, ThreadLocalManager::OnThreadExit);
112 void * getTop() {
113 return pthread_getspecific(m_key);
115 void setTop(void * p) {
116 ThreadLocalSetValue(m_key, p);
118 static void OnThreadExit(void *p);
119 pthread_key_t m_key;
121 static ThreadLocalManager s_manager;
124 ///////////////////////////////////////////////////////////////////////////////
125 // ThreadLocal allocates by calling new without parameters and frees by calling
126 // delete
128 template<typename T>
129 void ThreadLocalOnThreadExit(void * p) {
130 ThreadLocalNode<T> * pNode = (ThreadLocalNode<T>*)p;
131 delete pNode->m_p;
132 pNode->m_p = nullptr;
135 template<typename T>
136 struct ThreadLocal {
137 T *get() const {
138 if (m_node.m_p == nullptr) {
139 const_cast<ThreadLocal<T>*>(this)->create();
141 return m_node.m_p;
144 void create() NEVER_INLINE;
146 bool isNull() const { return m_node.m_p == nullptr; }
148 void destroy() {
149 delete m_node.m_p;
150 m_node.m_p = nullptr;
153 void nullOut() {
154 m_node.m_p = nullptr;
157 T *operator->() const {
158 return get();
161 T &operator*() const {
162 return *get();
165 ThreadLocalNode<T> m_node;
168 template<typename T>
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();
179 template<typename T>
180 struct ThreadLocalNoCheck {
181 T *getCheck() const ATTRIBUTE_COLD NEVER_INLINE;
182 T* getNoCheck() const {
183 assert(m_node.m_p);
184 return m_node.m_p;
187 void create() NEVER_INLINE;
189 bool isNull() const { return m_node.m_p == nullptr; }
191 void destroy() {
192 delete m_node.m_p;
193 m_node.m_p = nullptr;
196 T *operator->() const {
197 return getNoCheck();
200 T &operator*() const {
201 return *getNoCheck();
204 ThreadLocalNode<T> m_node;
207 template<typename T>
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();
217 template<typename T>
218 T *ThreadLocalNoCheck<T>::getCheck() const {
219 if (m_node.m_p == nullptr) {
220 const_cast<ThreadLocalNoCheck<T>*>(this)->create();
222 return m_node.m_p;
226 ///////////////////////////////////////////////////////////////////////////////
227 // Singleton thread-local storage for T
229 template<typename T>
230 void ThreadLocalSingletonOnThreadExit(void *obj) {
231 T::OnThreadExit((T*)obj);
234 // ThreadLocalSingleton has NoCheck property
235 template <typename T>
236 class ThreadLocalSingleton {
237 public:
238 ThreadLocalSingleton() { s_inited = true; }
240 static T *getCheck() ATTRIBUTE_COLD NEVER_INLINE;
242 static T* getNoCheck() {
243 assert(s_inited);
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);
252 T* p = s_singleton;
253 if (p) {
254 T::Delete(p);
255 s_singleton = nullptr;
259 T *operator->() const {
260 return getNoCheck();
263 T &operator*() const {
264 return *getNoCheck();
267 private:
268 static __thread T *s_singleton;
269 typedef typename boost::aligned_storage<sizeof(T), sizeof(void*)>::type
270 StorageType;
271 static __thread StorageType s_storage;
272 static bool s_inited; // no-fast-TLS requires construction so be consistent
275 template<typename T>
276 bool ThreadLocalSingleton<T>::s_inited = false;
278 template<typename T>
279 T *ThreadLocalSingleton<T>::getCheck() {
280 assert(s_inited);
281 if (!s_singleton) {
282 T* p = (T*) &s_storage;
283 T::Create(p);
284 s_singleton = p;
286 return s_singleton;
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 {
299 T *get() const {
300 if (m_p == nullptr && throwOnNull) {
301 throw Exception("ThreadLocalProxy::get() called before set()");
303 return m_p;
306 void set(T* obj) {
307 m_p = obj;
310 bool isNull() const { return m_p == nullptr; }
312 void destroy() {
313 m_p = nullptr;
316 T *operator->() const {
317 return get();
320 T &operator*() const {
321 return *get();
324 T * m_p;
328 * How to use the thread-local macros:
330 * Use DECLARE_THREAD_LOCAL to declare a *static* class field as thread local:
331 * class SomeClass {
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
350 #ifndef __APPLE__
351 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(T, f) \
352 __attribute((section(".tbss.hot"))) \
353 __thread ThreadLocalNoCheck<T> f
354 #else
355 #define IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(T, f) \
356 __attribute((section(".tbss.hot,"))) \
357 __thread ThreadLocalNoCheck<T> f
358 #endif
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
370 template<typename T>
371 void ThreadLocalOnThreadExit(void *p) {
372 delete (T*)p;
375 template<typename T>
376 class ThreadLocal {
377 public:
379 * Constructor that has to be called from a thread-neutral place.
381 ThreadLocal() : m_key(0) {
382 ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
385 T *get() const {
386 T *obj = (T*)pthread_getspecific(m_key);
387 if (obj == nullptr) {
388 obj = new T();
389 ThreadLocalSetValue(m_key, obj);
391 return obj;
394 bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
396 void destroy() {
397 delete (T*)pthread_getspecific(m_key);
398 ThreadLocalSetValue(m_key, nullptr);
401 void nullOut() {
402 ThreadLocalSetValue(m_key, nullptr);
406 * Access object's member or method through this operator overload.
408 T *operator->() const {
409 return get();
412 T &operator*() const {
413 return *get();
416 private:
417 pthread_key_t m_key;
420 template<typename T>
421 class ThreadLocalNoCheck {
422 public:
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);
434 assert(obj);
435 return obj;
438 bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
440 void destroy() {
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 {
449 return getNoCheck();
452 T &operator*() const {
453 return *getNoCheck();
456 public:
457 pthread_key_t m_key;
460 template<typename T>
461 T *ThreadLocalNoCheck<T>::getCheck() const {
462 T *obj = (T*)pthread_getspecific(m_key);
463 if (obj == nullptr) {
464 obj = new T();
465 ThreadLocalSetValue(m_key, obj);
467 return obj;
470 ///////////////////////////////////////////////////////////////////////////////
471 // Singleton thread-local storage for T
473 template<typename T>
474 void ThreadLocalSingletonOnThreadExit(void *obj) {
475 T::OnThreadExit((T*)obj);
476 free(obj);
479 // ThreadLocalSingleton has NoCheck property
480 template<typename T>
481 class ThreadLocalSingleton {
482 public:
483 ThreadLocalSingleton() { getKey(); }
485 static T *getCheck() ATTRIBUTE_COLD NEVER_INLINE;
486 static T* getNoCheck() {
487 assert(s_inited);
488 T *obj = (T*)pthread_getspecific(s_key);
489 assert(obj);
490 return obj;
493 static bool isNull() { return pthread_getspecific(s_key) == nullptr; }
495 static void destroy() {
496 void* p = pthread_getspecific(s_key);
497 T::Delete((T*)p);
498 free(p);
499 ThreadLocalSetValue(s_key, nullptr);
502 T *operator->() const {
503 return getNoCheck();
506 T &operator*() const {
507 return *getNoCheck();
510 private:
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() {
515 if (!s_inited) {
516 s_inited = true;
517 ThreadLocalCreateKey(&s_key, ThreadLocalSingletonOnThreadExit<T>);
519 return s_key;
523 template<typename T>
524 T *ThreadLocalSingleton<T>::getCheck() {
525 assert(s_inited);
526 T *obj = (T*)pthread_getspecific(s_key);
527 if (obj == nullptr) {
528 obj = (T*)malloc(sizeof(T));
529 T::Create(obj);
530 ThreadLocalSetValue(s_key, obj);
532 return obj;
535 template<typename T>
536 pthread_key_t ThreadLocalSingleton<T>::s_key;
537 template<typename T>
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 {
545 public:
547 * Constructor that has to be called from a thread-neutral place.
549 ThreadLocalProxy() : m_key(0) {
550 ThreadLocalCreateKey(&m_key, nullptr);
553 T *get() const {
554 T *obj = (T*)pthread_getspecific(m_key);
555 if (obj == nullptr && throwOnNull) {
556 throw Exception("ThreadLocalProxy::get() called before set()");
558 return obj;
561 void set(T* obj) {
562 ThreadLocalSetValue(m_key, obj);
565 bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
567 void destroy() {
568 ThreadLocalSetValue(m_key, nullptr);
572 * Access object's member or method through this operator overload.
574 T *operator->() const {
575 return get();
578 T &operator*() const {
579 return *get();
582 public:
583 pthread_key_t m_key;
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_