hazptr.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * Copyright 2016-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #define HAZPTR_H
  18. #include <atomic>
  19. /* Stand-in for C++17 std::pmr::memory_resource */
  20. #include <folly/experimental/hazptr/memory_resource.h>
  21. namespace folly {
  22. namespace hazptr {
  23. /** hazptr_rec: Private class that contains hazard pointers. */
  24. class hazptr_rec;
  25. /** hazptr_obj: Private class for objects protected by hazard pointers. */
  26. class hazptr_obj;
  27. /** hazptr_obj_base: Base template for objects protected by hazard pointers. */
  28. template <typename T, typename Deleter>
  29. class hazptr_obj_base;
  30. /** hazptr_obj_base_refcounted:
  31. * Base template for reference counted objects protected by hazard pointers.
  32. */
  33. template <typename T, typename Deleter>
  34. class hazptr_obj_base_refcounted;
  35. /** hazptr_local: Optimized template for bulk construction and destruction of
  36. * hazard pointers */
  37. template <size_t M>
  38. class hazptr_array;
  39. /** hazptr_local: Optimized template for locally-used hazard pointers */
  40. template <size_t M>
  41. class hazptr_local;
  42. /** hazptr_priv: Per-thread list of retired objects pushed in bulk to domain */
  43. class hazptr_priv;
  44. /** hazptr_domain: Class of hazard pointer domains. Each domain manages a set
  45. * of hazard pointers and a set of retired objects. */
  46. class hazptr_domain {
  47. memory_resource* mr_;
  48. std::atomic<hazptr_rec*> hazptrs_ = {nullptr};
  49. std::atomic<hazptr_obj*> retired_ = {nullptr};
  50. /* Using signed int for rcount_ because it may transiently be
  51. * negative. Using signed int for all integer variables that may be
  52. * involved in calculations related to the value of rcount_. */
  53. std::atomic<int> hcount_ = {0};
  54. std::atomic<int> rcount_ = {0};
  55. static constexpr uint64_t syncTimePeriod_{2000000000}; // in ns
  56. std::atomic<uint64_t> syncTime_{0};
  57. public:
  58. constexpr explicit hazptr_domain(
  59. memory_resource* = get_default_resource()) noexcept;
  60. ~hazptr_domain();
  61. hazptr_domain(const hazptr_domain&) = delete;
  62. hazptr_domain(hazptr_domain&&) = delete;
  63. hazptr_domain& operator=(const hazptr_domain&) = delete;
  64. hazptr_domain& operator=(hazptr_domain&&) = delete;
  65. /** Free-function retire. May allocate memory */
  66. template <typename T, typename D = std::default_delete<T>>
  67. void retire(T* obj, D reclaim = {});
  68. void cleanup();
  69. void tryTimedCleanup();
  70. private:
  71. friend class hazptr_holder;
  72. template <typename, typename>
  73. friend class hazptr_obj_base;
  74. template <typename, typename>
  75. friend class hazptr_obj_base_refcounted;
  76. friend class hazptr_priv;
  77. void objRetire(hazptr_obj*);
  78. hazptr_rec* hazptrAcquire();
  79. void hazptrRelease(hazptr_rec*) noexcept;
  80. int pushRetired(hazptr_obj* head, hazptr_obj* tail, int count);
  81. bool reachedThreshold(int rcount);
  82. void tryBulkReclaim();
  83. void bulkReclaim();
  84. };
  85. /** Get the default hazptr_domain */
  86. hazptr_domain& default_hazptr_domain();
  87. extern hazptr_domain default_domain_;
  88. /** Free-function retire, that operates on the default domain */
  89. template <typename T, typename D = std::default_delete<T>>
  90. void hazptr_retire(T* obj, D reclaim = {});
  91. /** hazptr_cleanup
  92. * Reclaims all reclaimable objects retired to the domain before this call.
  93. */
  94. void hazptr_cleanup(hazptr_domain& domain = default_hazptr_domain());
  95. /** Definition of hazptr_obj */
  96. class hazptr_obj {
  97. friend class hazptr_domain;
  98. template <typename, typename>
  99. friend class hazptr_obj_base;
  100. template <typename, typename>
  101. friend class hazptr_obj_base_refcounted;
  102. friend class hazptr_priv;
  103. void (*reclaim_)(hazptr_obj*);
  104. hazptr_obj* next_;
  105. public:
  106. // All constructors set next_ to this in order to catch misuse bugs like
  107. // double retire.
  108. hazptr_obj() noexcept : next_(this) {}
  109. hazptr_obj(const hazptr_obj&) noexcept : next_(this) {}
  110. hazptr_obj(hazptr_obj&&) noexcept : next_(this) {}
  111. hazptr_obj& operator=(const hazptr_obj&) {
  112. return *this;
  113. }
  114. hazptr_obj& operator=(hazptr_obj&&) {
  115. return *this;
  116. }
  117. private:
  118. void set_next(hazptr_obj* obj) {
  119. next_ = obj;
  120. }
  121. void retireCheck() {
  122. // Only for catching misuse bugs like double retire
  123. if (next_ != this) {
  124. retireCheckFail();
  125. }
  126. }
  127. FOLLY_NOINLINE void retireCheckFail() {
  128. CHECK_EQ(next_, this);
  129. }
  130. const void* getObjPtr() const;
  131. };
  132. /** Definition of hazptr_obj_base */
  133. template <typename T, typename D = std::default_delete<T>>
  134. class hazptr_obj_base : public hazptr_obj {
  135. public:
  136. /* Retire a removed object and pass the responsibility for
  137. * reclaiming it to the hazptr library */
  138. void retire(hazptr_domain& domain = default_hazptr_domain(), D reclaim = {});
  139. private:
  140. D deleter_;
  141. };
  142. /** Definition of hazptr_recounted_obj_base */
  143. template <typename T, typename D = std::default_delete<T>>
  144. class hazptr_obj_base_refcounted : public hazptr_obj {
  145. public:
  146. /* Retire a removed object and pass the responsibility for
  147. * reclaiming it to the hazptr library */
  148. void retire(hazptr_domain& domain = default_hazptr_domain(), D reclaim = {});
  149. /* aquire_ref() increments the reference count
  150. *
  151. * acquire_ref_safe() is the same as acquire_ref() except that in
  152. * addition the caller guarantees that the call is made in a
  153. * thread-safe context, e.g., the object is not yet shared. This is
  154. * just an optimization to save an atomic operation.
  155. *
  156. * release_ref() decrements the reference count and returns true if
  157. * the object is safe to reclaim.
  158. */
  159. void acquire_ref();
  160. void acquire_ref_safe();
  161. bool release_ref();
  162. private:
  163. void preRetire(D deleter);
  164. std::atomic<uint32_t> refcount_{0};
  165. D deleter_;
  166. };
  167. /** hazptr_holder: Class for automatic acquisition and release of
  168. * hazard pointers, and interface for hazard pointer operations. */
  169. class hazptr_holder {
  170. template <size_t M>
  171. friend class hazptr_array;
  172. template <size_t M>
  173. friend class hazptr_local;
  174. public:
  175. /* Constructor automatically acquires a hazard pointer. */
  176. explicit hazptr_holder(hazptr_domain& domain = default_hazptr_domain());
  177. /* Construct an empty hazptr_holder. */
  178. // Note: This diverges from the proposal in P0233R4
  179. explicit hazptr_holder(std::nullptr_t) noexcept;
  180. /* Destructor automatically clears and releases the owned hazard pointer. */
  181. ~hazptr_holder();
  182. hazptr_holder(const hazptr_holder&) = delete;
  183. hazptr_holder& operator=(const hazptr_holder&) = delete;
  184. // Note: This diverges from the proposal in P0233R4 which disallows
  185. // move constructor and assignment operator.
  186. hazptr_holder(hazptr_holder&&) noexcept;
  187. hazptr_holder& operator=(hazptr_holder&&) noexcept;
  188. /** Hazard pointer operations */
  189. /* Returns a protected pointer from the source */
  190. template <typename T>
  191. T* get_protected(const std::atomic<T*>& src) noexcept;
  192. /* Returns a protected pointer from the source, filtering
  193. the protected pointer through function Func. Useful for
  194. stealing bits of the pointer word */
  195. template <typename T, typename Func>
  196. T* get_protected(const std::atomic<T*>& src, Func f) noexcept;
  197. /* Return true if successful in protecting ptr if src == ptr after
  198. * setting the hazard pointer. Otherwise sets ptr to src. */
  199. template <typename T>
  200. bool try_protect(T*& ptr, const std::atomic<T*>& src) noexcept;
  201. /* Return true if successful in protecting ptr if src == ptr after
  202. * setting the hazard pointer, filtering the pointer through Func.
  203. * Otherwise sets ptr to src. */
  204. template <typename T, typename Func>
  205. bool try_protect(T*& ptr, const std::atomic<T*>& src, Func f) noexcept;
  206. /* Set the hazard pointer to ptr */
  207. template <typename T>
  208. void reset(const T* ptr) noexcept;
  209. /* Set the hazard pointer to nullptr */
  210. void reset(std::nullptr_t = nullptr) noexcept;
  211. /* Swap ownership of hazard pointers between hazptr_holder-s. */
  212. /* Note: The owned hazard pointers remain unmodified during the swap
  213. * and continue to protect the respective objects that they were
  214. * protecting before the swap, if any. */
  215. void swap(hazptr_holder&) noexcept;
  216. private:
  217. hazptr_domain* domain_;
  218. hazptr_rec* hazptr_;
  219. };
  220. void swap(hazptr_holder&, hazptr_holder&) noexcept;
  221. using aligned_hazptr_holder = typename std::
  222. aligned_storage<sizeof(hazptr_holder), alignof(hazptr_holder)>::type;
  223. /**
  224. * hazptr_array: Optimized for bulk construction and destruction of
  225. * hazptr_holder-s.
  226. *
  227. * WARNING: Do not move from or to individual hazptr_holder-s.
  228. * Only move the whole hazptr_array.
  229. */
  230. template <size_t M = 1>
  231. class hazptr_array {
  232. static_assert(M > 0, "M must be a positive integer.");
  233. public:
  234. hazptr_array();
  235. explicit hazptr_array(std::nullptr_t) noexcept;
  236. hazptr_array(const hazptr_array&) = delete;
  237. hazptr_array& operator=(const hazptr_array&) = delete;
  238. hazptr_array(hazptr_array&& other) noexcept;
  239. hazptr_array& operator=(hazptr_array&& other) noexcept;
  240. ~hazptr_array();
  241. hazptr_holder& operator[](size_t i) noexcept;
  242. private:
  243. aligned_hazptr_holder raw_[M];
  244. bool empty_{false};
  245. };
  246. /**
  247. * hazptr_local: Optimized for construction and destruction of
  248. * one or more hazptr_holder-s with local scope.
  249. *
  250. * WARNING 1: Do not move from or to individual hazptr_holder-s.
  251. *
  252. * WARNING 2: There can only be one hazptr_local active for the same
  253. * thread at any time. This is not tracked and checked by the
  254. * implementation because it would negate the performance gains of
  255. * this class.
  256. */
  257. template <size_t M = 1>
  258. class hazptr_local {
  259. static_assert(M > 0, "M must be a positive integer.");
  260. public:
  261. hazptr_local();
  262. hazptr_local(const hazptr_local&) = delete;
  263. hazptr_local& operator=(const hazptr_local&) = delete;
  264. hazptr_local(hazptr_local&&) = delete;
  265. hazptr_local& operator=(hazptr_local&&) = delete;
  266. ~hazptr_local();
  267. hazptr_holder& operator[](size_t i) noexcept;
  268. private:
  269. aligned_hazptr_holder raw_[M];
  270. bool slow_path_{false};
  271. };
  272. } // namespace hazptr
  273. } // namespace folly
  274. #include <folly/experimental/hazptr/hazptr-impl.h>