ThreadLocal.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. /* Cross-platform lightweight thread local data wrappers. */
  7. #ifndef mozilla_ThreadLocal_h
  8. #define mozilla_ThreadLocal_h
  9. #if defined(XP_WIN)
  10. // This file will get included in any file that wants to add a profiler mark.
  11. // In order to not bring <windows.h> together we could include windef.h and
  12. // winbase.h which are sufficient to get the prototypes for the Tls* functions.
  13. // # include <windef.h>
  14. // # include <winbase.h>
  15. // Unfortunately, even including these headers causes us to add a bunch of ugly
  16. // stuff to our namespace e.g #define CreateEvent CreateEventW
  17. extern "C" {
  18. __declspec(dllimport) void* __stdcall TlsGetValue(unsigned long);
  19. __declspec(dllimport) int __stdcall TlsSetValue(unsigned long, void*);
  20. __declspec(dllimport) unsigned long __stdcall TlsAlloc();
  21. }
  22. #else
  23. # include <pthread.h>
  24. # include <signal.h>
  25. #endif
  26. #include "mozilla/Assertions.h"
  27. #include "mozilla/Attributes.h"
  28. #include "mozilla/TypeTraits.h"
  29. namespace mozilla {
  30. // sig_safe_t denotes an atomic type which can be read or stored in a single
  31. // instruction. This means that data of this type is safe to be manipulated
  32. // from a signal handler, or other similar asynchronous execution contexts.
  33. #if defined(XP_WIN)
  34. typedef unsigned long sig_safe_t;
  35. #else
  36. typedef sig_atomic_t sig_safe_t;
  37. #endif
  38. namespace detail {
  39. #if defined(HAVE_THREAD_TLS_KEYWORD)
  40. #define MOZ_HAS_THREAD_LOCAL
  41. #endif
  42. /*
  43. * Thread Local Storage helpers.
  44. *
  45. * Usage:
  46. *
  47. * Do not directly instantiate this class. Instead, use the
  48. * MOZ_THREAD_LOCAL macro to declare or define instances. The macro
  49. * takes a type name as its argument.
  50. *
  51. * Declare like this:
  52. * extern MOZ_THREAD_LOCAL(int) tlsInt;
  53. * Define like this:
  54. * MOZ_THREAD_LOCAL(int) tlsInt;
  55. * or:
  56. * static MOZ_THREAD_LOCAL(int) tlsInt;
  57. *
  58. * Only static-storage-duration (e.g. global variables, or static class members)
  59. * objects of this class should be instantiated. This class relies on
  60. * zero-initialization, which is implicit for static-storage-duration objects.
  61. * It doesn't have a custom default constructor, to avoid static initializers.
  62. *
  63. * API usage:
  64. *
  65. * // Create a TLS item.
  66. * //
  67. * // Note that init() should be invoked before the first use of set()
  68. * // or get(). It is ok to call it multiple times. This must be
  69. * // called in a way that avoids possible races with other threads.
  70. * MOZ_THREAD_LOCAL(int) tlsKey;
  71. * if (!tlsKey.init()) {
  72. * // deal with the error
  73. * }
  74. *
  75. * // Set the TLS value
  76. * tlsKey.set(123);
  77. *
  78. * // Get the TLS value
  79. * int value = tlsKey.get();
  80. */
  81. template<typename T>
  82. class ThreadLocal
  83. {
  84. #ifndef MOZ_HAS_THREAD_LOCAL
  85. #if defined(XP_WIN)
  86. typedef unsigned long key_t;
  87. #else
  88. typedef pthread_key_t key_t;
  89. #endif
  90. // Integral types narrower than void* must be extended to avoid
  91. // warnings from valgrind on some platforms. This helper type
  92. // achieves that without penalizing the common case of ThreadLocals
  93. // instantiated using a pointer type.
  94. template<typename S>
  95. struct Helper
  96. {
  97. typedef uintptr_t Type;
  98. };
  99. template<typename S>
  100. struct Helper<S *>
  101. {
  102. typedef S *Type;
  103. };
  104. #endif
  105. bool initialized() const {
  106. #ifdef MOZ_HAS_THREAD_LOCAL
  107. return true;
  108. #else
  109. return mInited;
  110. #endif
  111. }
  112. public:
  113. // __thread does not allow non-trivial constructors, but we can
  114. // instead rely on zero-initialization.
  115. #ifndef MOZ_HAS_THREAD_LOCAL
  116. ThreadLocal()
  117. : mKey(0), mInited(false)
  118. {}
  119. #endif
  120. MOZ_MUST_USE inline bool init();
  121. inline T get() const;
  122. inline void set(const T aValue);
  123. private:
  124. #ifdef MOZ_HAS_THREAD_LOCAL
  125. T mValue;
  126. #else
  127. key_t mKey;
  128. bool mInited;
  129. #endif
  130. };
  131. template<typename T>
  132. inline bool
  133. ThreadLocal<T>::init()
  134. {
  135. static_assert(mozilla::IsPointer<T>::value || mozilla::IsIntegral<T>::value,
  136. "mozilla::ThreadLocal must be used with a pointer or "
  137. "integral type");
  138. static_assert(sizeof(T) <= sizeof(void*),
  139. "mozilla::ThreadLocal can't be used for types larger than "
  140. "a pointer");
  141. #ifdef MOZ_HAS_THREAD_LOCAL
  142. return true;
  143. #else
  144. if (!initialized()) {
  145. #ifdef XP_WIN
  146. mKey = TlsAlloc();
  147. mInited = mKey != 0xFFFFFFFFUL; // TLS_OUT_OF_INDEXES
  148. #else
  149. mInited = !pthread_key_create(&mKey, nullptr);
  150. #endif
  151. }
  152. return mInited;
  153. #endif
  154. }
  155. template<typename T>
  156. inline T
  157. ThreadLocal<T>::get() const
  158. {
  159. #ifdef MOZ_HAS_THREAD_LOCAL
  160. return mValue;
  161. #else
  162. MOZ_ASSERT(initialized());
  163. void* h;
  164. #ifdef XP_WIN
  165. h = TlsGetValue(mKey);
  166. #else
  167. h = pthread_getspecific(mKey);
  168. #endif
  169. return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
  170. #endif
  171. }
  172. template<typename T>
  173. inline void
  174. ThreadLocal<T>::set(const T aValue)
  175. {
  176. #ifdef MOZ_HAS_THREAD_LOCAL
  177. mValue = aValue;
  178. #else
  179. MOZ_ASSERT(initialized());
  180. void* h = reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
  181. #ifdef XP_WIN
  182. bool succeeded = TlsSetValue(mKey, h);
  183. #else
  184. bool succeeded = !pthread_setspecific(mKey, h);
  185. #endif
  186. if (!succeeded) {
  187. MOZ_CRASH();
  188. }
  189. #endif
  190. }
  191. #ifdef MOZ_HAS_THREAD_LOCAL
  192. #define MOZ_THREAD_LOCAL(TYPE) __thread mozilla::detail::ThreadLocal<TYPE>
  193. #else
  194. #define MOZ_THREAD_LOCAL(TYPE) mozilla::detail::ThreadLocal<TYPE>
  195. #endif
  196. } // namespace detail
  197. } // namespace mozilla
  198. #endif /* mozilla_ThreadLocal_h */