2 * Copyright 2016-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 /* Stand-in for C++17 std::pmr::memory_resource */
22 #include <folly/experimental/hazptr/memory_resource.h>
27 /** hazptr_rec: Private class that contains hazard pointers. */
30 /** hazptr_obj: Private class for objects protected by hazard pointers. */
33 /** hazptr_obj_base: Base template for objects protected by hazard pointers. */
34 template <typename T, typename Deleter>
35 class hazptr_obj_base;
37 /** hazptr_obj_base_refcounted:
38 * Base template for reference counted objects protected by hazard pointers.
40 template <typename T, typename Deleter>
41 class hazptr_obj_base_refcounted;
43 /** hazptr_local: Optimized template for bulk construction and destruction of
48 /** hazptr_local: Optimized template for locally-used hazard pointers */
52 /** hazptr_domain: Class of hazard pointer domains. Each domain manages a set
53 * of hazard pointers and a set of retired objects. */
56 constexpr explicit hazptr_domain(
57 memory_resource* = get_default_resource()) noexcept;
60 hazptr_domain(const hazptr_domain&) = delete;
61 hazptr_domain(hazptr_domain&&) = delete;
62 hazptr_domain& operator=(const hazptr_domain&) = delete;
63 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 = {});
70 friend class hazptr_obj_batch;
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 struct hazptr_priv;
79 std::atomic<hazptr_rec*> hazptrs_ = {nullptr};
80 std::atomic<hazptr_obj*> retired_ = {nullptr};
81 std::atomic<int> hcount_ = {0};
82 std::atomic<int> rcount_ = {0};
83 /* Using signed int for rcount_ because it may transiently be
84 * negative. Using signed int for all integer variables that may be
85 * involved in calculations related to the value of rcount_. */
87 void objRetire(hazptr_obj*);
88 hazptr_rec* hazptrAcquire();
89 void hazptrRelease(hazptr_rec*) noexcept;
90 int pushRetired(hazptr_obj* head, hazptr_obj* tail, int count);
91 bool reachedThreshold(int rcount);
92 void tryBulkReclaim();
96 /** Get the default hazptr_domain */
97 hazptr_domain& default_hazptr_domain();
99 extern hazptr_domain default_domain_;
101 /** Free-function retire, that operates on the default domain */
102 template <typename T, typename D = std::default_delete<T>>
103 void hazptr_retire(T* obj, D reclaim = {});
105 /** Definition of hazptr_obj */
107 friend class hazptr_obj_batch;
108 friend class hazptr_domain;
109 template <typename, typename>
110 friend class hazptr_obj_base;
111 template <typename, typename>
112 friend class hazptr_obj_base_refcounted;
113 friend struct hazptr_priv;
115 void (*reclaim_)(hazptr_obj*);
116 hazptr_obj* next_{nullptr}; // nullptr for debugging
118 const void* getObjPtr() const;
121 /** Definition of hazptr_obj_base */
122 template <typename T, typename D = std::default_delete<T>>
123 class hazptr_obj_base : public hazptr_obj {
125 /* Retire a removed object and pass the responsibility for
126 * reclaiming it to the hazptr library */
127 void retire(hazptr_domain& domain = default_hazptr_domain(), D reclaim = {});
133 /** Definition of hazptr_recounted_obj_base */
134 template <typename T, typename D = std::default_delete<T>>
135 class hazptr_obj_base_refcounted : public hazptr_obj {
136 friend class hazptr_obj_batch;
139 /* Retire a removed object and pass the responsibility for
140 * reclaiming it to the hazptr library */
141 void retire(hazptr_domain& domain = default_hazptr_domain(), D reclaim = {});
143 /* aquire_ref() increments the reference count
145 * acquire_ref_safe() is the same as acquire_ref() except that in
146 * addition the caller guarantees that the call is made in a
147 * thread-safe context, e.g., the object is not yet shared. This is
148 * just an optimization to save an atomic operation.
150 * release_ref() decrements the reference count and returns true if
151 * the object is safe to reclaim.
154 void acquire_ref_safe();
158 void preRetire(D deleter);
160 std::atomic<uint32_t> refcount_{0};
164 /** hazptr_holder: Class for automatic acquisition and release of
165 * hazard pointers, and interface for hazard pointer operations. */
166 class hazptr_holder {
168 friend class hazptr_array;
170 friend class hazptr_local;
173 /* Constructor automatically acquires a hazard pointer. */
174 explicit hazptr_holder(hazptr_domain& domain = default_hazptr_domain());
175 /* Construct an empty hazptr_holder. */
176 // Note: This diverges from the proposal in P0233R4
177 explicit hazptr_holder(std::nullptr_t) noexcept;
179 /* Destructor automatically clears and releases the owned hazard pointer. */
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;
189 /** Hazard pointer operations */
190 /* Returns a protected pointer from the source */
191 template <typename T>
192 T* get_protected(const std::atomic<T*>& src) noexcept;
193 /* Returns a protected pointer from the source, filtering
194 the protected pointer through function Func. Useful for
195 stealing bits of the pointer word */
196 template <typename T, typename Func>
197 T* get_protected(const std::atomic<T*>& src, Func f) noexcept;
198 /* Return true if successful in protecting ptr if src == ptr after
199 * setting the hazard pointer. Otherwise sets ptr to src. */
200 template <typename T>
201 bool try_protect(T*& ptr, const std::atomic<T*>& src) noexcept;
202 /* Return true if successful in protecting ptr if src == ptr after
203 * setting the hazard pointer, filtering the pointer through Func.
204 * Otherwise sets ptr to src. */
205 template <typename T, typename Func>
206 bool try_protect(T*& ptr, const std::atomic<T*>& src, Func f) noexcept;
207 /* Set the hazard pointer to ptr */
208 template <typename T>
209 void reset(const T* ptr) noexcept;
210 /* Set the hazard pointer to nullptr */
211 void reset(std::nullptr_t = nullptr) noexcept;
213 /* Swap ownership of hazard pointers between hazptr_holder-s. */
214 /* Note: The owned hazard pointers remain unmodified during the swap
215 * and continue to protect the respective objects that they were
216 * protecting before the swap, if any. */
217 void swap(hazptr_holder&) noexcept;
220 hazptr_domain* domain_;
224 void swap(hazptr_holder&, hazptr_holder&) noexcept;
226 using aligned_hazptr_holder = typename std::
227 aligned_storage<sizeof(hazptr_holder), alignof(hazptr_holder)>::type;
230 * hazptr_array: Optimized for bulk construction and destruction of
233 * WARNING: Do not move from or to individual hazptr_holder-s.
234 * Only move the whole hazptr_array.
236 template <size_t M = 1>
238 static_assert(M > 0, "M must be a positive integer.");
242 explicit hazptr_array(std::nullptr_t) noexcept;
244 hazptr_array(const hazptr_array&) = delete;
245 hazptr_array& operator=(const hazptr_array&) = delete;
246 hazptr_array(hazptr_array&& other) noexcept;
247 hazptr_array& operator=(hazptr_array&& other) noexcept;
251 hazptr_holder& operator[](size_t i) noexcept;
254 aligned_hazptr_holder raw_[M];
259 * hazptr_local: Optimized for construction and destruction of
260 * one or more hazptr_holder-s with local scope.
262 * WARNING 1: Do not move from or to individual hazptr_holder-s.
264 * WARNING 2: There can only be one hazptr_local active for the same
265 * thread at any time. This is not tracked and checked by the
266 * implementation because it would negate the performance gains of
269 template <size_t M = 1>
271 static_assert(M > 0, "M must be a positive integer.");
275 hazptr_local(const hazptr_local&) = delete;
276 hazptr_local& operator=(const hazptr_local&) = delete;
277 hazptr_local(hazptr_local&&) = delete;
278 hazptr_local& operator=(hazptr_local&&) = delete;
282 hazptr_holder& operator[](size_t i) noexcept;
285 aligned_hazptr_holder raw_[M];
286 bool need_destruct_{false};
289 } // namespace hazptr
292 #include "hazptr-impl.h"