6 #ifndef XENIUM_HAZARD_ERAS_HPP
7 #define XENIUM_HAZARD_ERAS_HPP
9 #include <xenium/reclamation/detail/allocation_tracker.hpp>
10 #include <xenium/reclamation/detail/concurrent_ptr.hpp>
11 #include <xenium/reclamation/detail/deletable_object.hpp>
12 #include <xenium/reclamation/detail/guard_ptr.hpp>
13 #include <xenium/reclamation/detail/thread_block_list.hpp>
15 #include <xenium/acquire_guard.hpp>
16 #include <xenium/parameter.hpp>
17 #include <xenium/policy.hpp>
22 namespace xenium::reclamation {
30 using std::runtime_error::runtime_error;
34 struct deletable_object_with_eras;
36 template <
class Strategy,
class Derived>
37 struct basic_he_thread_control_block;
39 template <
size_t K_,
size_t A,
size_t B,
template <
class>
class ThreadControlBlock>
40 struct generic_hazard_era_allocation_strategy {
41 static constexpr
size_t K = K_;
43 static size_t retired_nodes_threshold() {
return A * number_of_active_hazard_eras() + B; }
45 static size_t number_of_active_hazard_eras() {
return number_of_active_hes.load(std::memory_order_relaxed); }
47 using thread_control_block = ThreadControlBlock<generic_hazard_era_allocation_strategy>;
50 friend thread_control_block;
51 friend basic_he_thread_control_block<generic_hazard_era_allocation_strategy, thread_control_block>;
53 inline static std::atomic<size_t> number_of_active_hes{0};
56 template <
class Strategy>
57 struct static_he_thread_control_block;
59 template <
class Strategy>
60 struct dynamic_he_thread_control_block;
63 namespace he_allocation {
79 template <
size_t K = 2,
size_t A = 2,
size_t B = 100>
81 detail::generic_hazard_era_allocation_strategy<K, A, B, detail::static_he_thread_control_block> {};
104 template <
size_t K = 2,
size_t A = 2,
size_t B = 100>
106 detail::generic_hazard_era_allocation_strategy<K, A, B, detail::dynamic_he_thread_control_block> {};
109 template <
class AllocationStrategy = he_allocation::static_strategy<3>>
110 struct hazard_era_traits {
111 using allocation_strategy = AllocationStrategy;
113 template <
class... Policies>
135 template <
class Traits = hazard_era_traits<>>
137 using allocation_strategy =
typename Traits::allocation_strategy;
138 using thread_control_block =
typename allocation_strategy::thread_control_block;
139 friend detail::basic_he_thread_control_block<allocation_strategy, thread_control_block>;
141 template <
class T,
class MarkedPtr>
155 template <
class... Policies>
158 template <
class T, std::
size_t N = 0,
class Deleter = std::default_delete<T>>
159 class enable_concurrent_ptr;
161 class region_guard {};
163 template <
class T, std::
size_t N = T::number_of_mark_bits>
171 friend struct detail::deletable_object_with_eras;
173 using era_t = uint64_t;
174 inline static std::atomic<era_t> era_clock{1};
175 inline static detail::thread_block_list<thread_control_block, detail::deletable_object_with_eras>
176 global_thread_block_list{};
177 static thread_data& local_thread_data();
179 ALLOCATION_TRACKING_FUNCTIONS;
182 template <
class Traits>
183 template <
class T, std::
size_t N,
class Deleter>
184 class hazard_eras<Traits>::enable_concurrent_ptr :
185 public detail::deletable_object_impl<T, Deleter, detail::deletable_object_with_eras>,
186 private detail::tracked_object<hazard_eras> {
188 static constexpr std::size_t number_of_mark_bits = N;
191 enable_concurrent_ptr() noexcept { this->construction_era = era_clock.load(std::memory_order_relaxed); }
192 enable_concurrent_ptr(
const enable_concurrent_ptr&) noexcept =
default;
193 enable_concurrent_ptr(enable_concurrent_ptr&&) noexcept = default;
194 enable_concurrent_ptr& operator=(const enable_concurrent_ptr&) noexcept = default;
195 enable_concurrent_ptr& operator=(enable_concurrent_ptr&&) noexcept = default;
196 ~enable_concurrent_ptr() noexcept override = default;
199 friend detail::deletable_object_impl<T, Deleter>;
201 template <class, class>
202 friend class guard_ptr;
205 template <class Traits>
206 template <class T, class MarkedPtr>
207 class hazard_eras<Traits>::guard_ptr : public detail::guard_ptr<T, MarkedPtr, guard_ptr<T, MarkedPtr>> {
208 using base = detail::guard_ptr<T, MarkedPtr, guard_ptr>;
209 using Deleter =
typename T::Deleter;
212 guard_ptr() noexcept = default;
215 explicit guard_ptr(const MarkedPtr& p);
216 guard_ptr(const guard_ptr& p);
217 guard_ptr(guard_ptr&& p) noexcept;
219 guard_ptr& operator=(const guard_ptr& p) noexcept;
220 guard_ptr& operator=(guard_ptr&& p) noexcept;
223 void acquire(const concurrent_ptr<T>& p, std::memory_order order = std::memory_order_seq_cst);
226 bool acquire_if_equal(const concurrent_ptr<T>& p,
227 const MarkedPtr& expected,
228 std::memory_order order = std::memory_order_seq_cst);
231 void reset() noexcept;
234 void reclaim(Deleter d = Deleter()) noexcept;
237 using enable_concurrent_ptr = hazard_eras::enable_concurrent_ptr<T, MarkedPtr::number_of_mark_bits, Deleter>;
240 void do_swap(guard_ptr& g) noexcept;
242 typename thread_control_block::hazard_era* he =
nullptr;
246 struct deletable_object_with_eras {
247 virtual void delete_self() = 0;
248 deletable_object_with_eras* next =
nullptr;
251 virtual ~deletable_object_with_eras() =
default;
252 using era_t = size_t;
253 era_t construction_era{};
254 era_t retirement_era{};
256 friend class hazard_eras;
259 #pragma clang diagnostic push
261 #pragma clang diagnostic ignored "-Wunsupported-friend"
264 friend struct hazard_eras<T>::thread_data;
266 #pragma clang diagnostic pop
272 #define HAZARD_ERAS_IMPL
273 #include <xenium/reclamation/impl/hazard_eras.hpp>
274 #undef HAZARD_ERAS_IMPL