1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. #ifndef incl_HPHP_THREAD_LOCAL_H_
    
  9. #define incl_HPHP_THREAD_LOCAL_H_
    
  10. 
    
  11. #include <assert.h>
    
  12. #include <stdio.h>
    
  13. #include <stdlib.h>
    
  14. #include <stdint.h>
    
  15. #include <pthread.h>
    
  16. #include <errno.h>
    
  17. #include <stdexcept>
    
  18. #include <type_traits>
    
  19. #include <utility>
    
  20. #include "portability.h"
    
  21. 
    
  22. namespace HPHP {
    
  23. 
    
  24. // return the location of the current thread's tdata section
    
  25. std::pair<void*,size_t> getCppTdata();
    
  26. 
    
  27. inline uintptr_t tlsBase() {
    
  28.   uintptr_t retval;
    
  29. #if defined(__x86_64__)
    
  30.   asm ("movq %%fs:0, %0" : "=r" (retval));
    
  31. #elif defined(__AARCH64EL__)
    
  32.   // mrs == "move register <-- system"
    
  33.   // tpidr_el0 == "thread process id register for exception level 0"
    
  34.   asm ("mrs %0, tpidr_el0" : "=r" (retval));
    
  35. #elif defined (__powerpc64__)
    
  36.   asm ("xor %0,%0,%0\n\t"
    
  37.        "or  %0,%0,13\n\t"
    
  38.       : "=r" (retval));
    
  39. #elif defined(_M_X64)
    
  40.   retval = (uintptr_t)_readfsbase_u64();
    
  41.   retval = *(uintptr_t*)(retval + 88);
    
  42. #else
    
  43. # error How do you access thread-local storage on this machine?
    
  44. #endif
    
  45.   return retval;
    
  46. }
    
  47. 
    
  48. ///////////////////////////////////////////////////////////////////////////////
    
  49. // gcc >= 4.3.0 supports the '__thread' keyword for thread locals
    
  50. //
    
  51. // Clang seems to have added this feature, or at the very least it is ignoring
    
  52. // __thread keyword and compiling anyway
    
  53. //
    
  54. // On OSX, gcc does emulate TLS but in a manner that invalidates assumptions
    
  55. // we have made about __thread and makes accessing thread-local variables in a
    
  56. // JIT-friendly fashion difficult (as the compiler is doing a lot of magic that
    
  57. // is not contractual or documented that we would need to duplicate in emitted
    
  58. // code) so for now we're not going to use it. One possibility if we really
    
  59. // want to do this is to generate functions that access variables of interest
    
  60. // in ThreadLocal* (all of them are NoCheck right now) and use the bytes of
    
  61. // gcc's compiled functions to find the values we would need to pass to
    
  62. // __emutls_get_address.
    
  63. //
    
  64. // icc 13.0.0 appears to support it as well but we end up with
    
  65. // assembler warnings of unknown importance about incorrect section
    
  66. // types
    
  67. //
    
  68. // __thread on cygwin and mingw uses pthreads emulation not native tls so
    
  69. // the emulation for thread local must be used as well
    
  70. //
    
  71. // So we use __thread on gcc, icc and clang, unless we are on OSX. On OSX, we
    
  72. // use our own emulation. Use the DECLARE_THREAD_LOCAL() and
    
  73. // IMPLEMENT_THREAD_LOCAL() macros to access either __thread or the emulation
    
  74. // as appropriate.
    
  75. 
    
  76. #if !defined(NO_TLS) &&                                       \
    
  77.     !defined(__CYGWIN__) && !defined(__MINGW__) &&            \
    
  78.    ((__llvm__ && __clang__) ||                                \
    
  79.    __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) ||   \
    
  80.    __INTEL_COMPILER || defined(_MSC_VER))
    
  81. #define USE_GCC_FAST_TLS
    
  82. #endif
    
  83. 
    
  84. ///////////////////////////////////////////////////////////////////////////////
    
  85. // helper
    
  86. 
    
  87. inline void ThreadLocalCheckReturn(int ret, const char *funcName) {
    
  88.   if (ret != 0) {
    
  89.     // This is used from global constructors so the safest thing to do is just
    
  90.     // print to stderr and exit().
    
  91.     fprintf(stderr, "%s returned %d", funcName, ret);
    
  92.     exit(1);
    
  93.   }
    
  94. }
    
  95. 
    
  96. inline void ThreadLocalCreateKey(pthread_key_t *key, void (*del)(void*)) {
    
  97.   int ret = pthread_key_create(key, del);
    
  98.   ThreadLocalCheckReturn(ret, "pthread_key_create");
    
  99. }
    
  100. 
    
  101. inline void ThreadLocalSetValue(pthread_key_t key, const void* value) {
    
  102.   int ret = pthread_setspecific(key, value);
    
  103.   ThreadLocalCheckReturn(ret, "pthread_setspecific");
    
  104. }
    
  105. 
    
  106. #ifdef __APPLE__
    
  107. typedef struct __darwin_pthread_handler_rec darwin_pthread_handler;
    
  108. #endif
    
  109. 
    
  110. ///////////////////////////////////////////////////////////////////////////////
    
  111. 
    
  112. /**
    
  113.  * A thread-local object is a "global" object within a thread. This is useful
    
  114.  * for writing apartment-threaded code, where nothing is actually shared
    
  115.  * between different threads (hence no locking) but those variables are not
    
  116.  * on stack in local scope. To use it, just do something like this,
    
  117.  *
    
  118.  *   IMPLEMENT_THREAD_LOCAL(MyClass, static_object);
    
  119.  *     static_object->data_ = ...;
    
  120.  *     static_object->doSomething();
    
  121.  *
    
  122.  *   IMPLEMENT_THREAD_LOCAL(int, static_number);
    
  123.  *     int value = *static_number;
    
  124.  *
    
  125.  * So, syntax-wise it's similar to pointers. The type parameter can be a
    
  126.  * primitive types. If it's a class, there has to be a default constructor.
    
  127.  */
    
  128. 
    
  129. ///////////////////////////////////////////////////////////////////////////////
    
  130. #if defined(USE_GCC_FAST_TLS)
    
  131. 
    
  132. /**
    
  133.  * We keep a linked list of destructors in ThreadLocalManager to be called on
    
  134.  * thread exit. ThreadLocalNode is a node in this list.
    
  135.  */
    
  136. template <typename T>
    
  137. struct ThreadLocalNode {
    
  138.   T * m_p;
    
  139.   void (*m_on_thread_exit_fn)(void * p);
    
  140.   void * m_next;
    
  141.   size_t m_size;
    
  142. };
    
  143. 
    
  144. struct ThreadLocalManager {
    
  145.   template<class T>
    
  146.   static void PushTop(ThreadLocalNode<T>& node) {
    
  147.     PushTop(&node, sizeof(T));
    
  148.   }
    
  149.   template<class F> void scan(F& mark) const;
    
  150. 
    
  151. private:
    
  152.   static void PushTop(void* node, size_t size);
    
  153.   struct ThreadLocalList {
    
  154.     void* head{nullptr};
    
  155. #ifdef __APPLE__
    
  156.     ThreadLocalList();
    
  157.     darwin_pthread_handler handler;
    
  158. #endif
    
  159.   };
    
  160.   static ThreadLocalList* getList(void* p) {
    
  161.     return static_cast<ThreadLocalList*>(p);
    
  162.   }
    
  163.   ThreadLocalManager() : m_key(0) {
    
  164. #ifdef __APPLE__
    
  165.     ThreadLocalCreateKey(&m_key, nullptr);
    
  166. #else
    
  167.     ThreadLocalCreateKey(&m_key, ThreadLocalManager::OnThreadExit);
    
  168. #endif
    
  169.   };
    
  170.   static void OnThreadExit(void *p);
    
  171.   pthread_key_t m_key;
    
  172. 
    
  173.   static ThreadLocalManager& GetManager();
    
  174. };
    
  175. 
    
  176. ///////////////////////////////////////////////////////////////////////////////
    
  177. // ThreadLocal allocates by calling new without parameters and frees by calling
    
  178. // delete
    
  179. 
    
  180. template<typename T>
    
  181. void ThreadLocalOnThreadExit(void * p) {
    
  182.   ThreadLocalNode<T> * pNode = (ThreadLocalNode<T>*)p;
    
  183.   delete pNode->m_p;
    
  184.   pNode->m_p = nullptr;
    
  185. }
    
  186. 
    
  187. /**
    
  188.  * The USE_GCC_FAST_TLS implementation of ThreadLocal is just a lazy-initialized
    
  189.  * pointer wrapper. In this case, we have one ThreadLocal object per thread.
    
  190.  */
    
  191. template<typename T>
    
  192. struct ThreadLocal {
    
  193.   T *get() const {
    
  194.     if (m_node.m_p == nullptr) {
    
  195.       const_cast<ThreadLocal<T>*>(this)->create();
    
  196.     }
    
  197.     return m_node.m_p;
    
  198.   }
    
  199. 
    
  200.   NEVER_INLINE void create();
    
  201. 
    
  202.   bool isNull() const { return m_node.m_p == nullptr; }
    
  203. 
    
  204.   void destroy() {
    
  205.     delete m_node.m_p;
    
  206.     m_node.m_p = nullptr;
    
  207.   }
    
  208. 
    
  209.   void nullOut() {
    
  210.     m_node.m_p = nullptr;
    
  211.   }
    
  212. 
    
  213.   T *operator->() const {
    
  214.     return get();
    
  215.   }
    
  216. 
    
  217.   T &operator*() const {
    
  218.     return *get();
    
  219.   }
    
  220. 
    
  221.   ThreadLocalNode<T> m_node;
    
  222. };
    
  223. 
    
  224. template<typename T>
    
  225. void ThreadLocal<T>::create() {
    
  226.   if (m_node.m_on_thread_exit_fn == nullptr) {
    
  227.     m_node.m_on_thread_exit_fn = ThreadLocalOnThreadExit<T>;
    
  228.     ThreadLocalManager::PushTop(m_node);
    
  229.   }
    
  230.   assert(m_node.m_p == nullptr);
    
  231.   m_node.m_p = new T();
    
  232. }
    
  233. 
    
  234. /**
    
  235.  * ThreadLocalNoCheck is a pointer wrapper like ThreadLocal, except that it is
    
  236.  * explicitly initialized with getCheck(), rather than being initialized when
    
  237.  * it is first dereferenced.
    
  238.  */
    
  239. template<typename T>
    
  240. struct ThreadLocalNoCheck {
    
  241.   NEVER_INLINE T *getCheck() const;
    
  242.   T* getNoCheck() const {
    
  243.     assert(m_node.m_p);
    
  244.     return m_node.m_p;
    
  245.   }
    
  246. 
    
  247.   NEVER_INLINE void create();
    
  248. 
    
  249.   bool isNull() const { return m_node.m_p == nullptr; }
    
  250. 
    
  251.   void destroy() {
    
  252.     delete m_node.m_p;
    
  253.     m_node.m_p = nullptr;
    
  254.   }
    
  255. 
    
  256.   T *operator->() const {
    
  257.     return getNoCheck();
    
  258.   }
    
  259. 
    
  260.   T &operator*() const {
    
  261.     return *getNoCheck();
    
  262.   }
    
  263. 
    
  264.   ThreadLocalNode<T> m_node;
    
  265. private:
    
  266.   void setNull() { m_node.m_p = nullptr; }
    
  267. };
    
  268. 
    
  269. template<typename T>
    
  270. void ThreadLocalNoCheck<T>::create() {
    
  271.   if (m_node.m_on_thread_exit_fn == nullptr) {
    
  272.     m_node.m_on_thread_exit_fn = ThreadLocalOnThreadExit<T>;
    
  273.     ThreadLocalManager::PushTop(m_node);
    
  274.   }
    
  275.   assert(m_node.m_p == nullptr);
    
  276.   m_node.m_p = new T();
    
  277. }
    
  278. template<typename T>
    
  279. T *ThreadLocalNoCheck<T>::getCheck() const {
    
  280.   if (m_node.m_p == nullptr) {
    
  281.     const_cast<ThreadLocalNoCheck<T>*>(this)->create();
    
  282.   }
    
  283.   return m_node.m_p;
    
  284. }
    
  285. 
    
  286. 
    
  287. ///////////////////////////////////////////////////////////////////////////////
    
  288. // Singleton thread-local storage for T
    
  289. 
    
  290. template<typename T>
    
  291. void ThreadLocalSingletonOnThreadExit(void *obj) {
    
  292.   T::OnThreadExit((T*)obj);
    
  293. }
    
  294. 
    
  295. // ThreadLocalSingleton has NoCheck property
    
  296. template <typename T>
    
  297. class ThreadLocalSingleton {
    
  298. public:
    
  299.   ThreadLocalSingleton() { s_inited = true; }
    
  300. 
    
  301.   NEVER_INLINE static T *getCheck();
    
  302. 
    
  303.   static T* getNoCheck() {
    
  304.     assert(s_inited);
    
  305.     assert(s_singleton == (T*)&s_storage);
    
  306.     return (T*)&s_storage;
    
  307.   }
    
  308. 
    
  309.   static bool isNull() { return s_singleton == nullptr; }
    
  310. 
    
  311.   static void destroy() {
    
  312.     assert(!s_singleton || s_singleton == (T*)&s_storage);
    
  313.     T* p = s_singleton;
    
  314.     if (p) {
    
  315.       T::Delete(p);
    
  316.       s_singleton = nullptr;
    
  317.     }
    
  318.   }
    
  319. 
    
  320.   T *operator->() const {
    
  321.     return getNoCheck();
    
  322.   }
    
  323. 
    
  324.   T &operator*() const {
    
  325.     return *getNoCheck();
    
  326.   }
    
  327. 
    
  328. private:
    
  329.   static __thread T *s_singleton;
    
  330.   typedef typename std::aligned_storage<sizeof(T), sizeof(void*)>::type
    
  331.           StorageType;
    
  332.   static __thread StorageType s_storage;
    
  333.   static bool s_inited; // no-fast-TLS requires construction so be consistent
    
  334. };
    
  335. 
    
  336. template<typename T>
    
  337. bool ThreadLocalSingleton<T>::s_inited = false;
    
  338. 
    
  339. template<typename T>
    
  340. T *ThreadLocalSingleton<T>::getCheck() {
    
  341.   assert(s_inited);
    
  342.   if (!s_singleton) {
    
  343.     T* p = (T*) &s_storage;
    
  344.     T::Create(p);
    
  345.     s_singleton = p;
    
  346.   }
    
  347.   return s_singleton;
    
  348. }
    
  349. 
    
  350. template<typename T> __thread T *ThreadLocalSingleton<T>::s_singleton;
    
  351. template<typename T> __thread typename ThreadLocalSingleton<T>::StorageType
    
  352.                               ThreadLocalSingleton<T>::s_storage;
    
  353. 
    
  354. 
    
  355. ///////////////////////////////////////////////////////////////////////////////
    
  356. // some classes don't need new/delete at all
    
  357. 
    
  358. template<typename T, bool throwOnNull = true>
    
  359. struct ThreadLocalProxy {
    
  360.   T *get() const {
    
  361.     if (m_p == nullptr && throwOnNull) {
    
  362.       throw std::runtime_error("ThreadLocalProxy::get() called before set()");
    
  363.     }
    
  364.     return m_p;
    
  365.   }
    
  366. 
    
  367.   void set(T* obj) {
    
  368.     m_p = obj;
    
  369.   }
    
  370. 
    
  371.   bool isNull() const { return m_p == nullptr; }
    
  372. 
    
  373.   void destroy() {
    
  374.     m_p = nullptr;
    
  375.   }
    
  376. 
    
  377.   T *operator->() const {
    
  378.     return get();
    
  379.   }
    
  380. 
    
  381.   T &operator*() const {
    
  382.     return *get();
    
  383.   }
    
  384. 
    
  385.   T * m_p;
    
  386. };
    
  387. 
    
  388. /*
    
  389.  * How to use the thread-local macros:
    
  390.  *
    
  391.  * Use DECLARE_THREAD_LOCAL to declare a *static* class field as thread local:
    
  392.  *   class SomeClass {
    
  393.  *     static DECLARE_THREAD_LOCAL(SomeFieldType, f);
    
  394.  *   }
    
  395.  *
    
  396.  * Use IMPLEMENT_THREAD_LOCAL in the cpp file to implement the field:
    
  397.  *   IMPLEMENT_THREAD_LOCAL(SomeFieldType, SomeClass::f);
    
  398.  *
    
  399.  * Remember: *Never* write IMPLEMENT_THREAD_LOCAL in a header file.
    
  400.  */
    
  401. 
    
  402. #define DECLARE_THREAD_LOCAL(T, f) \
    
  403.   __thread HPHP::ThreadLocal<T> f
    
  404. #define IMPLEMENT_THREAD_LOCAL(T, f) \
    
  405.   __thread HPHP::ThreadLocal<T> f
    
  406. 
    
  407. #define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) \
    
  408.   __thread HPHP::ThreadLocalNoCheck<T> f
    
  409. #define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) \
    
  410.   __thread HPHP::ThreadLocalNoCheck<T> f
    
  411. 
    
  412. #define DECLARE_THREAD_LOCAL_PROXY(T, N, f) \
    
  413.   __thread HPHP::ThreadLocalProxy<T, N> f
    
  414. #define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) \
    
  415.   __thread HPHP::ThreadLocalProxy<T, N> f
    
  416. 
    
  417. #else /* USE_GCC_FAST_TLS */
    
  418. 
    
  419. ///////////////////////////////////////////////////////////////////////////////
    
  420. // ThreadLocal allocates by calling new() without parameters
    
  421. 
    
  422. template<typename T>
    
  423. void ThreadLocalOnThreadExit(void *p) {
    
  424.   delete (T*)p;
    
  425. }
    
  426. 
    
  427. #ifdef __APPLE__
    
  428. // The __thread variables in class T will be freed when pthread calls
    
  429. // the destructor function on Mac. We can register a handler in
    
  430. // pthread_t->__cleanup_stack similar to pthread_cleanup_push(). The handler
    
  431. // will be called earlier so the __thread variables will still exist in the
    
  432. // handler when the thread exits.
    
  433. //
    
  434. // See the details at:
    
  435. // https://github.com/facebook/hhvm/issues/4444#issuecomment-92497582
    
  436. typedef struct __darwin_pthread_handler_rec darwin_pthread_handler;
    
  437. 
    
  438. template<typename T>
    
  439. void ThreadLocalOnThreadCleanup(void *key) {
    
  440.   void *obj = pthread_getspecific((pthread_key_t)key);
    
  441.   if (obj) {
    
  442.     ThreadLocalOnThreadExit<T>(obj);
    
  443.   }
    
  444. }
    
  445. 
    
  446. inline void ThreadLocalSetCleanupHandler(pthread_key_t cleanup_key,
    
  447.                                          pthread_key_t key,
    
  448.                                          void (*del)(void*)) {
    
  449.   // Prevent from adding the handler for multiple times.
    
  450.   darwin_pthread_handler *handler =
    
  451.       (darwin_pthread_handler*)pthread_getspecific(cleanup_key);
    
  452.   if (handler)
    
  453.     return;
    
  454. 
    
  455.   pthread_t self = pthread_self();
    
  456. 
    
  457.   handler = new darwin_pthread_handler();
    
  458.   handler->__routine = del;
    
  459.   handler->__arg = (void*)key;
    
  460.   handler->__next = self->__cleanup_stack;
    
  461.   self->__cleanup_stack = handler;
    
  462. 
    
  463.   ThreadLocalSetValue(cleanup_key, handler);
    
  464. }
    
  465. #endif
    
  466. 
    
  467. /**
    
  468.  * This is the emulation version of ThreadLocal. In this case, the ThreadLocal
    
  469.  * object is a true global, and the get() method returns a thread-dependent
    
  470.  * pointer from pthread's thread-specific data management.
    
  471.  */
    
  472. template<typename T>
    
  473. class ThreadLocal {
    
  474. public:
    
  475.   /**
    
  476.    * Constructor that has to be called from a thread-neutral place.
    
  477.    */
    
  478.   ThreadLocal() : m_key(0) {
    
  479. #ifdef __APPLE__
    
  480.     ThreadLocalCreateKey(&m_key, nullptr);
    
  481.     ThreadLocalCreateKey(&m_cleanup_key,
    
  482.                          ThreadLocalOnThreadExit<darwin_pthread_handler>);
    
  483. #else
    
  484.     ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
    
  485. #endif
    
  486.   }
    
  487. 
    
  488.   T *get() const {
    
  489.     T *obj = (T*)pthread_getspecific(m_key);
    
  490.     if (obj == nullptr) {
    
  491.       obj = new T();
    
  492.       ThreadLocalSetValue(m_key, obj);
    
  493. #ifdef __APPLE__
    
  494.       ThreadLocalSetCleanupHandler(m_cleanup_key, m_key,
    
  495.                                    ThreadLocalOnThreadCleanup<T>);
    
  496. #endif
    
  497.     }
    
  498.     return obj;
    
  499.   }
    
  500. 
    
  501.   bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
    
  502. 
    
  503.   void destroy() {
    
  504.     delete (T*)pthread_getspecific(m_key);
    
  505.     ThreadLocalSetValue(m_key, nullptr);
    
  506.   }
    
  507. 
    
  508.   void nullOut() {
    
  509.     ThreadLocalSetValue(m_key, nullptr);
    
  510.   }
    
  511. 
    
  512.   /**
    
  513.    * Access object's member or method through this operator overload.
    
  514.    */
    
  515.   T *operator->() const {
    
  516.     return get();
    
  517.   }
    
  518. 
    
  519.   T &operator*() const {
    
  520.     return *get();
    
  521.   }
    
  522. 
    
  523. private:
    
  524.   pthread_key_t m_key;
    
  525. 
    
  526. #ifdef __APPLE__
    
  527.   pthread_key_t m_cleanup_key;
    
  528. #endif
    
  529. };
    
  530. 
    
  531. template<typename T>
    
  532. class ThreadLocalNoCheck {
    
  533. public:
    
  534.   /**
    
  535.    * Constructor that has to be called from a thread-neutral place.
    
  536.    */
    
  537.   ThreadLocalNoCheck() : m_key(0) {
    
  538. #ifdef __APPLE__
    
  539.     ThreadLocalCreateKey(&m_key, nullptr);
    
  540.     ThreadLocalCreateKey(&m_cleanup_key,
    
  541.                          ThreadLocalOnThreadExit<darwin_pthread_handler>);
    
  542. #else
    
  543.     ThreadLocalCreateKey(&m_key, ThreadLocalOnThreadExit<T>);
    
  544. #endif
    
  545.   }
    
  546. 
    
  547.   NEVER_INLINE T *getCheck() const;
    
  548. 
    
  549.   T* getNoCheck() const {
    
  550.     T *obj = (T*)pthread_getspecific(m_key);
    
  551.     assert(obj);
    
  552.     return obj;
    
  553.   }
    
  554. 
    
  555.   bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
    
  556. 
    
  557.   void destroy() {
    
  558.     delete (T*)pthread_getspecific(m_key);
    
  559.     ThreadLocalSetValue(m_key, nullptr);
    
  560.   }
    
  561. 
    
  562.   /**
    
  563.    * Access object's member or method through this operator overload.
    
  564.    */
    
  565.   T *operator->() const {
    
  566.     return getNoCheck();
    
  567.   }
    
  568. 
    
  569.   T &operator*() const {
    
  570.     return *getNoCheck();
    
  571.   }
    
  572. 
    
  573. public:
    
  574.   void setNull() { ThreadLocalSetValue(m_key, nullptr); }
    
  575.   pthread_key_t m_key;
    
  576. 
    
  577. #ifdef __APPLE__
    
  578.   pthread_key_t m_cleanup_key;
    
  579. #endif
    
  580. };
    
  581. 
    
  582. template<typename T>
    
  583. T *ThreadLocalNoCheck<T>::getCheck() const {
    
  584.   T *obj = (T*)pthread_getspecific(m_key);
    
  585.   if (obj == nullptr) {
    
  586.     obj = new T();
    
  587.     ThreadLocalSetValue(m_key, obj);
    
  588. #ifdef __APPLE__
    
  589.     ThreadLocalSetCleanupHandler(m_cleanup_key, m_key,
    
  590.                                  ThreadLocalOnThreadCleanup<T>);
    
  591. #endif
    
  592.   }
    
  593.   return obj;
    
  594. }
    
  595. 
    
  596. ///////////////////////////////////////////////////////////////////////////////
    
  597. // Singleton thread-local storage for T
    
  598. 
    
  599. template<typename T>
    
  600. void ThreadLocalSingletonOnThreadExit(void *obj) {
    
  601.   T::OnThreadExit((T*)obj);
    
  602.   free(obj);
    
  603. }
    
  604. 
    
  605. #ifdef __APPLE__
    
  606. template<typename T>
    
  607. void ThreadLocalSingletonOnThreadCleanup(void *key) {
    
  608.   void *obj = pthread_getspecific((pthread_key_t)key);
    
  609.   if (obj) {
    
  610.     ThreadLocalSingletonOnThreadExit<T>(obj);
    
  611.   }
    
  612. }
    
  613. #endif
    
  614. 
    
  615. // ThreadLocalSingleton has NoCheck property
    
  616. template<typename T>
    
  617. class ThreadLocalSingleton {
    
  618. public:
    
  619.   ThreadLocalSingleton() { getKey(); }
    
  620. 
    
  621.   NEVER_INLINE static T *getCheck();
    
  622.   static T* getNoCheck() {
    
  623.     assert(s_inited);
    
  624.     T *obj = (T*)pthread_getspecific(s_key);
    
  625.     assert(obj);
    
  626.     return obj;
    
  627.   }
    
  628. 
    
  629.   static bool isNull() {
    
  630.     return !s_inited || pthread_getspecific(s_key) == nullptr;
    
  631.   }
    
  632. 
    
  633.   static void destroy() {
    
  634.     void* p = pthread_getspecific(s_key);
    
  635.     T::Delete((T*)p);
    
  636.     free(p);
    
  637.     ThreadLocalSetValue(s_key, nullptr);
    
  638.   }
    
  639. 
    
  640.   T *operator->() const {
    
  641.     return getNoCheck();
    
  642.   }
    
  643. 
    
  644.   T &operator*() const {
    
  645.     return *getNoCheck();
    
  646.   }
    
  647. 
    
  648. private:
    
  649.   static pthread_key_t s_key;
    
  650.   static bool s_inited; // pthread_key_t has no portable valid sentinel
    
  651. 
    
  652. #ifdef __APPLE__
    
  653.   static pthread_key_t s_cleanup_key;
    
  654. #endif
    
  655. 
    
  656.   static pthread_key_t getKey() {
    
  657.     if (!s_inited) {
    
  658.       s_inited = true;
    
  659. #ifdef __APPLE__
    
  660.       ThreadLocalCreateKey(&s_key, nullptr);
    
  661.       ThreadLocalCreateKey(&s_cleanup_key,
    
  662.                            ThreadLocalOnThreadExit<darwin_pthread_handler>);
    
  663. #else
    
  664.       ThreadLocalCreateKey(&s_key, ThreadLocalSingletonOnThreadExit<T>);
    
  665. #endif
    
  666.     }
    
  667.     return s_key;
    
  668.   }
    
  669. };
    
  670. 
    
  671. template<typename T>
    
  672. T *ThreadLocalSingleton<T>::getCheck() {
    
  673.   assert(s_inited);
    
  674.   T *obj = (T*)pthread_getspecific(s_key);
    
  675.   if (obj == nullptr) {
    
  676.     obj = (T*)malloc(sizeof(T));
    
  677.     T::Create(obj);
    
  678.     ThreadLocalSetValue(s_key, obj);
    
  679. #ifdef __APPLE__
    
  680.     ThreadLocalSetCleanupHandler(s_cleanup_key, s_key,
    
  681.                                  ThreadLocalSingletonOnThreadCleanup<T>);
    
  682. #endif
    
  683.   }
    
  684.   return obj;
    
  685. }
    
  686. 
    
  687. template<typename T>
    
  688. pthread_key_t ThreadLocalSingleton<T>::s_key;
    
  689. template<typename T>
    
  690. bool ThreadLocalSingleton<T>::s_inited = false;
    
  691. 
    
  692. #ifdef __APPLE__
    
  693. template<typename T>
    
  694. pthread_key_t ThreadLocalSingleton<T>::s_cleanup_key;
    
  695. #endif
    
  696. 
    
  697. ///////////////////////////////////////////////////////////////////////////////
    
  698. // some classes don't need new/delete at all
    
  699. 
    
  700. template<typename T, bool throwOnNull = true>
    
  701. class ThreadLocalProxy {
    
  702. public:
    
  703.   /**
    
  704.    * Constructor that has to be called from a thread-neutral place.
    
  705.    */
    
  706.   ThreadLocalProxy() : m_key(0) {
    
  707.     ThreadLocalCreateKey(&m_key, nullptr);
    
  708.   }
    
  709. 
    
  710.   T *get() const {
    
  711.     T *obj = (T*)pthread_getspecific(m_key);
    
  712.     if (obj == nullptr && throwOnNull) {
    
  713.       throw std::runtime_error("ThreadLocalProxy::get() called before set()");
    
  714.     }
    
  715.     return obj;
    
  716.   }
    
  717. 
    
  718.   void set(T* obj) {
    
  719.     ThreadLocalSetValue(m_key, obj);
    
  720.   }
    
  721. 
    
  722.   bool isNull() const { return pthread_getspecific(m_key) == nullptr; }
    
  723. 
    
  724.   void destroy() {
    
  725.     ThreadLocalSetValue(m_key, nullptr);
    
  726.   }
    
  727. 
    
  728.   /**
    
  729.    * Access object's member or method through this operator overload.
    
  730.    */
    
  731.   T *operator->() const {
    
  732.     return get();
    
  733.   }
    
  734. 
    
  735.   T &operator*() const {
    
  736.     return *get();
    
  737.   }
    
  738. 
    
  739. public:
    
  740.   pthread_key_t m_key;
    
  741. };
    
  742. 
    
  743. /**
    
  744.  * The emulation version of the thread-local macros
    
  745.  */
    
  746. #define DECLARE_THREAD_LOCAL(T, f) HPHP::ThreadLocal<T> f
    
  747. #define IMPLEMENT_THREAD_LOCAL(T, f) HPHP::ThreadLocal<T> f
    
  748. 
    
  749. #define DECLARE_THREAD_LOCAL_NO_CHECK(T, f) HPHP::ThreadLocalNoCheck<T> f
    
  750. #define IMPLEMENT_THREAD_LOCAL_NO_CHECK(T, f) HPHP::ThreadLocalNoCheck<T> f
    
  751. 
    
  752. #define DECLARE_THREAD_LOCAL_PROXY(T, N, f) HPHP::ThreadLocalProxy<T, N> f
    
  753. #define IMPLEMENT_THREAD_LOCAL_PROXY(T, N, f) HPHP::ThreadLocalProxy<T, N> f
    
  754. 
    
  755. #endif /* USE_GCC_FAST_TLS */
    
  756. 
    
  757. ///////////////////////////////////////////////////////////////////////////////
    
  758. }
    
  759. 
    
  760. #endif // incl_HPHP_THREAD_LOCAL_H_