HazptrObjLinked.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /*
  2. * Copyright 2018-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. #include <folly/synchronization/Hazptr-fwd.h>
  18. #include <folly/synchronization/HazptrObj.h>
  19. #include <glog/logging.h>
  20. #include <atomic>
  21. #include <stack>
  22. ///
  23. /// Classes related to link counted objects and automatic retirement.
  24. ///
  25. namespace folly {
  26. /**
  27. * hazptr_root
  28. *
  29. * Link to counted objects. When destroyed unlinks the linked object
  30. * if any.
  31. *
  32. * Template parameter T must support a member function unlink(),
  33. * inherited from hazptr_obj_base_linked.
  34. *
  35. * Use example: Bucket heads in ConcurrentHashMap.
  36. */
  37. template <typename T, template <typename> class Atom>
  38. class hazptr_root {
  39. Atom<T*> link_;
  40. public:
  41. explicit hazptr_root(T* p = nullptr) noexcept : link_(p) {}
  42. ~hazptr_root() {
  43. auto p = link_.load(std::memory_order_relaxed);
  44. if (p) {
  45. p->unlink();
  46. }
  47. }
  48. const Atom<T*>& operator()() const noexcept {
  49. return link_;
  50. }
  51. Atom<T*>& operator()() noexcept {
  52. return link_;
  53. }
  54. }; // hazptr_root
  55. /**
  56. * hazptr_obj_linked
  57. *
  58. * Base class template for link counted objects.
  59. * Supports:
  60. * - Protecting descendants of protected objects.
  61. * - One-pass reclamation of long immutable chains of objects.
  62. * - Automatic reclamation of acyclic structures.
  63. *
  64. * Two inbound link counts are maintained per object:
  65. * - Link count: Represents the number of links from mutable paths.
  66. * - Ref count: Represents the number of links from immutable paths.
  67. * [Note: The ref count is one less than such links plus one if
  68. * the object hasn't gone through matching with hazard pointers
  69. * without finding a match. That is, a new object without inbound
  70. * links has a ref count of 0 and an about-to-be-reclaimed object
  71. * can be viewed to have a ref count of -1.]
  72. *
  73. * User code can increment the link and ref counts by calling
  74. * acquire_link and acquire_ref or their variants that require the
  75. * user to guarantee thread safety. There are no public functions to
  76. * decrement the counts explicitly. Counts are decremented implicitly
  77. * as described in hazptr_obj_base_linked.
  78. */
  79. template <template <typename> class Atom>
  80. class hazptr_obj_linked : public hazptr_obj<Atom> {
  81. using Count = uint32_t;
  82. static constexpr Count kRef = 1u;
  83. static constexpr Count kLink = 1u << 16;
  84. static constexpr Count kRefMask = kLink - 1u;
  85. static constexpr Count kLinkMask = ~kRefMask;
  86. Atom<Count> count_{0};
  87. public:
  88. void acquire_link() noexcept {
  89. count_inc(kLink);
  90. }
  91. void acquire_link_safe() noexcept {
  92. count_inc_safe(kLink);
  93. }
  94. void acquire_ref() noexcept {
  95. count_inc(kRef);
  96. }
  97. void acquire_ref_safe() noexcept {
  98. count_inc_safe(kRef);
  99. }
  100. private:
  101. template <typename, template <typename> class, typename>
  102. friend class hazptr_obj_base_linked;
  103. Count count() const noexcept {
  104. return count_.load(std::memory_order_acquire);
  105. }
  106. void count_set(Count val) noexcept {
  107. count_.store(val, std::memory_order_release);
  108. }
  109. void count_inc(Count add) noexcept {
  110. auto oldval = count_.fetch_add(add, std::memory_order_acq_rel);
  111. DCHECK_LT(oldval & kLinkMask, kLinkMask);
  112. DCHECK_LT(oldval & kRefMask, kRefMask);
  113. }
  114. void count_inc_safe(Count add) noexcept {
  115. auto oldval = count();
  116. count_set(oldval + add);
  117. DCHECK_LT(oldval & kLinkMask, kLinkMask);
  118. DCHECK_LT(oldval & kRefMask, kRefMask);
  119. }
  120. bool count_cas(Count& oldval, Count newval) noexcept {
  121. return count_.compare_exchange_weak(
  122. oldval, newval, std::memory_order_acq_rel, std::memory_order_acquire);
  123. }
  124. bool release_link() noexcept {
  125. auto sub = kLink;
  126. auto oldval = count();
  127. while (true) {
  128. DCHECK_GT(oldval & kLinkMask, 0u);
  129. if (oldval == kLink) {
  130. count_set(0u);
  131. return true;
  132. }
  133. if (count_cas(oldval, oldval - sub)) {
  134. return false;
  135. }
  136. }
  137. }
  138. bool release_ref() noexcept {
  139. auto sub = kRef;
  140. auto oldval = count();
  141. while (true) {
  142. if (oldval == 0u) {
  143. if (kIsDebug) {
  144. count_set(kRefMask);
  145. }
  146. return true;
  147. }
  148. DCHECK_GT(oldval & kRefMask, 0u);
  149. if (count_cas(oldval, oldval - sub)) {
  150. return false;
  151. }
  152. }
  153. }
  154. bool downgrade_link() noexcept {
  155. auto oldval = count();
  156. auto sub = kLink - kRef;
  157. while (true) {
  158. if (oldval == kLink) {
  159. count_set(kRef);
  160. return true;
  161. }
  162. if (count_cas(oldval, oldval - sub)) {
  163. return (oldval & kLinkMask) == kLink;
  164. }
  165. }
  166. }
  167. }; // hazptr_obj_linked
  168. /**
  169. * hazptr_obj_base_linked
  170. *
  171. * Base class template for link counted objects.
  172. *
  173. * Supports both *explicit* and *implicit* object retirement, depending
  174. * on whether object removal is *certain* or *uncertain*.
  175. *
  176. * A derived object's removal is certain when it is always possible
  177. * to reason based only on the local state of user code when an
  178. * object is removed, i.e., becomes unreachable from static
  179. * roots. Otherwise, removal is uncertain.
  180. *
  181. * For example, Removal in UnboundedQueue is certain, whereas removal
  182. * is ConcurrentHashMap is uncertain.
  183. *
  184. * If removal is certain, user code can call retire() explicitly.
  185. * Otherwise, user code should call unlink() whenever an inbound
  186. * link to the object is changed. Calls to unlink() automatically
  187. * retire the object when the link count is decremented to 0. [Note:
  188. * A ref count greater than 0 does not delay retiring an object.]
  189. *
  190. * Derived type T must define a member function template
  191. * template <typename S>
  192. * void push_links(bool m, S& s) {
  193. * if (m) { // m stands mutable links
  194. * // for each outbound mutable pointer p call
  195. * // s.push(p);
  196. * } else {
  197. * // for each outbound immutable pointer p call
  198. * // s.push(p);
  199. * }
  200. * }
  201. *
  202. * T may have both, either, or none of the two types of outbound
  203. * links. For example, UnboundedQueue Segment has an immutable
  204. * link, and ConcurrentHashMap NodeT has a mutable link.
  205. */
  206. template <typename T, template <typename> class Atom, typename D>
  207. class hazptr_obj_base_linked : public hazptr_obj_linked<Atom>,
  208. public hazptr_deleter<T, D> {
  209. using Stack = std::stack<hazptr_obj_base_linked<T, Atom, D>*>;
  210. public:
  211. void retire() {
  212. this->pre_retire_check(); // defined in hazptr_obj
  213. set_reclaim();
  214. this->push_to_retired(
  215. default_hazptr_domain<Atom>()); // defined in hazptr_obj
  216. }
  217. /* unlink: Retire object if last link is released. */
  218. void unlink() {
  219. if (this->release_link()) { // defined in hazptr_obj_linked
  220. downgrade_retire_immutable_descendants();
  221. retire();
  222. }
  223. }
  224. /* unlink_and_reclaim_unchecked: Reclaim object if the last link is
  225. released, without checking hazard pointers. To be called only
  226. when the object cannot possibly be protected by any hazard
  227. pointers. */
  228. void unlink_and_reclaim_unchecked() {
  229. if (this->release_link()) { // defined in hazptr_obj_linked
  230. DCHECK_EQ(this->count(), 0u);
  231. delete_self();
  232. }
  233. }
  234. private:
  235. void set_reclaim() noexcept {
  236. this->reclaim_ = [](hazptr_obj<Atom>* p, hazptr_obj_list<Atom>& l) {
  237. auto obj = static_cast<hazptr_obj_base_linked<T, Atom, D>*>(p);
  238. if (obj->release_ref()) { // defined in hazptr_obj_linked
  239. obj->release_delete_immutable_descendants();
  240. obj->release_retire_mutable_children(l);
  241. obj->delete_self();
  242. }
  243. };
  244. }
  245. void downgrade_retire_immutable_descendants() {
  246. Stack s;
  247. call_push_links(false, s);
  248. while (!s.empty()) {
  249. auto p = s.top();
  250. s.pop();
  251. if (p && p->downgrade_link()) {
  252. p->call_push_links(false, s);
  253. p->retire();
  254. }
  255. }
  256. }
  257. void release_delete_immutable_descendants() {
  258. Stack s;
  259. call_push_links(false, s);
  260. while (!s.empty()) {
  261. auto p = s.top();
  262. s.pop();
  263. if (p && p->release_ref()) {
  264. p->call_push_links(false, s);
  265. p->delete_self();
  266. }
  267. }
  268. }
  269. void release_retire_mutable_children(hazptr_obj_list<Atom>& l) {
  270. Stack s;
  271. call_push_links(true, s);
  272. while (!s.empty()) {
  273. auto p = s.top();
  274. s.pop();
  275. if (p->release_link()) {
  276. p->pre_retire_check(); // defined in hazptr_obj
  277. p->set_reclaim();
  278. l.push(p); // treated as if retired immediately
  279. }
  280. }
  281. }
  282. void call_push_links(bool m, Stack& s) {
  283. static_cast<T*>(this)->push_links(m, s); // to be defined in T
  284. }
  285. void delete_self() {
  286. this->delete_obj(static_cast<T*>(this)); // defined in hazptr_deleter
  287. }
  288. }; // hazptr_obj_base_linked
  289. } // namespace folly