PageRenderTime 25ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/util/lock-free-ptr-wrapper.h

https://github.com/facebook/hhvm
C Header | 301 lines | 183 code | 34 blank | 84 comment | 12 complexity | 9bd698dcc0b47e6ccd05d1017e9df14d MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, MIT, LGPL-2.0, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  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. +----------------------------------------------------------------------+
  15. */
  16. #pragma once
  17. #include <atomic>
  18. #include <type_traits>
  19. #include "hphp/util/assertions.h"
  20. #include "hphp/util/low-ptr.h"
  21. #include "hphp/util/smalllocks.h"
  22. namespace HPHP {
  23. //////////////////////////////////////////////////////////////////////
  24. /*
  25. * Implements a wrapper around a pointer-like thing such that readers
  26. * can read the pointer without locking (and without writing memory at
  27. * all).
  28. *
  29. * A single writer will use a compare-and-swap followed by an exchange
  30. * to update the pointer; multiple concurrent writers will wait on a
  31. * futex.
  32. *
  33. * For this to work, T's behavior must be fully determined by its bit
  34. * pattern (ie it can't care about its address), so that after:
  35. *
  36. * auto bits = *(uintptr_t*)&t;
  37. * auto &t2 = *(T*)&bits;
  38. *
  39. * t and t2 can be used interchangeably.
  40. *
  41. * All pointer types, and CompactVector in particular, satisfy this.
  42. */
  43. template<class T>
  44. struct LockFreePtrWrapper {
  45. static_assert((sizeof(T) == sizeof(uintptr_t)) ||
  46. (sizeof(T) == sizeof(uint32_t)),
  47. "LockFreePtrWrapper operates on pointers, or "
  48. "classes that wrap pointers (with no other data)");
  49. using raw_type =
  50. typename std::conditional<sizeof(T) == 4, uint32_t, uintptr_t>::type;
  51. LockFreePtrWrapper() : val{} { assertx(notLocked()); }
  52. LockFreePtrWrapper(const T& v) : val{v} { assertx(notLocked()); }
  53. ~LockFreePtrWrapper() {
  54. assertx(notLocked());
  55. val.~T();
  56. }
  57. LockFreePtrWrapper(const LockFreePtrWrapper<T>&) = delete;
  58. LockFreePtrWrapper<T>& operator=(const LockFreePtrWrapper<T>&) = delete;
  59. /*
  60. * We can't return a T* to callers because a valid T doesn't always exist in
  61. * LockFreePtrWrapper. Holder is a convenience wrapper around a bitwise copy
  62. * of T that avoids calling T's constructor or destructor, giving nearly the
  63. * same effect as returning a T*.
  64. */
  65. struct Holder {
  66. friend struct LockFreePtrWrapper;
  67. auto get() { return getter(val, false); }
  68. auto operator->() { return get(); }
  69. Holder(const Holder& h) : bits{h.bits} { assertx(!(bits & ~kPtrMask)); }
  70. Holder(raw_type bits) : bits{bits} { assertx(!(bits & ~kPtrMask)); }
  71. Holder(T&& val) : val{std::move(val)} { assertx(!(bits & ~kPtrMask)); }
  72. ~Holder() {}
  73. private:
  74. union {
  75. raw_type bits;
  76. T val;
  77. };
  78. template<typename U>
  79. static const U* getter(const U& p, int f) { return &p; }
  80. template<typename U>
  81. static auto getter(const U& p, bool f) -> decltype(p.operator->(), p) {
  82. return p;
  83. }
  84. };
  85. // Get a bitwise copy of the current value
  86. auto get() const {
  87. return getImpl<T>();
  88. }
  89. auto operator->() const {
  90. return get();
  91. }
  92. T copy() const {
  93. return copyImpl<T>();
  94. }
  95. /*
  96. * Get an exclusive lock on the wrapped value. Other threads can
  97. * still read its current value via get() or copy(). After calling
  98. * this, you must unlock it either with update_and_unlock (if you
  99. * want to change the value), or unlock (if you don't).
  100. */
  101. void lock_for_update();
  102. /*
  103. * Like lock_for_update(), but returns false if it fails to acquire
  104. * the lock rather than blocking. Returns true on success.
  105. */
  106. bool try_lock_for_update();
  107. /*
  108. * Unlock it.
  109. */
  110. void unlock();
  111. /*
  112. * Update the wrapped value, and return the old value. The old value
  113. * will typically need to be destroyed via a treadmill-like
  114. * mechanism, because other threads may have read the old value just
  115. * prior to the update (and still be using it).
  116. */
  117. T update_and_unlock(T&& v);
  118. protected:
  119. // Constructor that accepts raw bits, for use in the unsafe version.
  120. LockFreePtrWrapper(raw_type rawBits) : bits(rawBits) {
  121. assertx(notLocked());
  122. }
  123. raw_type raw() const { return bits.load(std::memory_order_relaxed); }
  124. const bool notLocked() {
  125. return !(low_bits.load(std::memory_order_relaxed) & ~kPtrMask);
  126. }
  127. template<typename U>
  128. typename std::enable_if<std::is_same<T,U>::value &&
  129. std::is_pointer<U>::value,U>::type
  130. getImpl() const {
  131. return reinterpret_cast<T>(unlocked());
  132. }
  133. template<typename U>
  134. typename std::enable_if<std::is_same<T,U>::value && is_lowptr_v<U>,
  135. typename lowptr_traits<U>::pointer>::type
  136. getImpl() const {
  137. auto p = unlocked();
  138. return reinterpret_cast<T*>(&p)->get();
  139. }
  140. template<typename U>
  141. typename std::enable_if<std::is_same<T,U>::value &&
  142. !std::is_pointer<U>::value &&
  143. !is_lowptr_v<U>, Holder>::type
  144. getImpl() const {
  145. return Holder { unlocked() };
  146. }
  147. template<typename U>
  148. typename std::enable_if<std::is_same<T,U>::value &&
  149. (std::is_pointer<U>::value || is_lowptr_v<U>),
  150. T>::type
  151. copyImpl() const {
  152. return get();
  153. }
  154. template<typename U>
  155. typename std::enable_if<std::is_same<T,U>::value &&
  156. !std::is_pointer<U>::value &&
  157. !is_lowptr_v<U>, T>::type
  158. copyImpl() const {
  159. // We need to force a copy, rather than a move from get().val. If you
  160. // change this, make sure you know what you're doing.
  161. auto const& x = get();
  162. return x.val;
  163. }
  164. raw_type unlock_helper(raw_type rep) {
  165. auto const c = bits.exchange(rep, std::memory_order_release);
  166. if (c & kLockWithWaitersBit) {
  167. futex_wake(&low_bits, 1);
  168. } else {
  169. assertx(c & kLockNoWaitersBit);
  170. }
  171. return c & kPtrMask;
  172. }
  173. raw_type unlocked() const {
  174. return bits.load(std::memory_order_acquire) & kPtrMask;
  175. }
  176. union {
  177. std::atomic<raw_type> bits;
  178. std::atomic<uint32_t> low_bits;
  179. T val;
  180. };
  181. static_assert(
  182. folly::kIsLittleEndian,
  183. "The low bits of low_bits must correspond to the low bits of bits"
  184. );
  185. static constexpr raw_type kPtrMask = static_cast<raw_type>(-4);
  186. static constexpr raw_type kLockNoWaitersBit = 1;
  187. static constexpr raw_type kLockWithWaitersBit = 2;
  188. };
  189. /*
  190. * The unsafe-version of LockFreePtrWrapper, provides copy constructor and
  191. * copy assignment operator, to be used when other mechanisms provide guarantee
  192. * that no concurrent access happens during the unsafe copying.
  193. *
  194. * Warning: a thorough idea on when and in which threads all accesses happen is
  195. * needed to safely use this.
  196. */
  197. template<class T>
  198. struct UnsafeLockFreePtrWrapper : LockFreePtrWrapper<T> {
  199. using LockFreePtrWrapper<T>::notLocked;
  200. UnsafeLockFreePtrWrapper() : LockFreePtrWrapper<T>() {}
  201. UnsafeLockFreePtrWrapper(const T& v) : LockFreePtrWrapper<T>(v) {}
  202. UnsafeLockFreePtrWrapper(const UnsafeLockFreePtrWrapper& o)
  203. : LockFreePtrWrapper<T>(o.raw()) {}
  204. auto const& operator=(const UnsafeLockFreePtrWrapper& o) {
  205. assertx(notLocked());
  206. LockFreePtrWrapper<T>::bits.store(o.raw(), std::memory_order_relaxed);
  207. assertx(notLocked());
  208. return *this;
  209. }
  210. };
  211. static_assert(!std::is_copy_constructible<LockFreePtrWrapper<int*>>::value, "");
  212. static_assert(std::is_copy_constructible<UnsafeLockFreePtrWrapper<int*>>::
  213. value, "");
  214. //////////////////////////////////////////////////////////////////////
  215. template<class T>
  216. void LockFreePtrWrapper<T>::lock_for_update() {
  217. auto lockBit = kLockNoWaitersBit;
  218. while (true) {
  219. auto c = raw() & kPtrMask;
  220. // writing is expected to be unusual, so start by assuming the low
  221. // two bits are clear, and attempt to set the appropriate low bit.
  222. if (bits.compare_exchange_weak(c, c + lockBit, std::memory_order_relaxed)) {
  223. return;
  224. }
  225. // We didn't get the lock, so someone else had it. c holds the
  226. // value we found there.
  227. if (c & kLockNoWaitersBit) {
  228. auto const desired = (c & kPtrMask) + kLockWithWaitersBit;
  229. if (!bits.compare_exchange_weak(c, desired, std::memory_order_relaxed)) {
  230. // We failed, so someone else got in before us. start over.
  231. continue;
  232. }
  233. // compare_exchange_weak only upates c when it fails, so set it
  234. // to the value we actually wrote to memory.
  235. c = desired;
  236. }
  237. assertx(!(c & kLockNoWaitersBit));
  238. if (c & kLockWithWaitersBit) {
  239. futex_wait(&low_bits, static_cast<uint32_t>(c));
  240. // If we were waiting on the futex, others might have been
  241. // waiting too (but we can't tell), so when we grab the lock, we
  242. // have to record that fact.
  243. lockBit = kLockWithWaitersBit;
  244. }
  245. }
  246. }
  247. template<typename T>
  248. bool LockFreePtrWrapper<T>::try_lock_for_update() {
  249. auto c = raw() & kPtrMask;
  250. return bits.compare_exchange_weak(
  251. c,
  252. c + kLockNoWaitersBit,
  253. std::memory_order_relaxed
  254. );
  255. }
  256. template<class T>
  257. void LockFreePtrWrapper<T>::unlock() {
  258. unlock_helper(raw() & kPtrMask);
  259. }
  260. template<class T>
  261. T LockFreePtrWrapper<T>::update_and_unlock(T&& v) {
  262. Holder h{std::move(v)};
  263. h.bits = unlock_helper(h.bits);
  264. return std::move(h.val);
  265. }
  266. //////////////////////////////////////////////////////////////////////
  267. }