1 #ifndef CYCLE_PTR_DETAIL_HAZARD_H 2 #define CYCLE_PTR_DETAIL_HAZARD_H 10 #include <type_traits> 12 #include <cycle_ptr/detail/intrusive_ptr.h> 14 namespace cycle_ptr::detail {
38 #if __cpp_lib_hardware_interference_size >= 201703 39 struct alignas(std::hardware_destructive_interference_size) data {
40 static_assert(
sizeof(std::atomic<T*>) < std::hardware_destructive_interference_size,
41 "Cycle_ptr did not expect a platform where cache line is less than or equal to a pointer.");
43 std::atomic<T*> ptr =
nullptr;
44 [[maybe_unused]]
char pad_[std::hardware_destructive_interference_size -
sizeof(std::atomic<T*>)];
46 #elif defined(__amd64__) || defined(__x86_64__) 47 struct alignas(64) data {
48 static_assert(
sizeof(std::atomic<T*>) < 64,
49 "Cycle_ptr did not expect a platform where cache line is less than or equal to a pointer.");
51 std::atomic<T*> ptr =
nullptr;
52 [[maybe_unused]]
char pad_[64 -
sizeof(std::atomic<T*>)];
55 # error No fallback for your architecture, sorry. 66 using ptr_set = std::array<data, 4096u /
sizeof(data)>;
86 return (*
this)(ptr, ptr.load(std::memory_order_relaxed));
93 auto operator()(
const std::atomic<T*>& ptr, T* target)
98 if (target ==
nullptr)
return nullptr;
103 if (d_.ptr.compare_exchange_strong(
106 std::memory_order_acquire,
107 std::memory_order_relaxed)) [[likely]] {
114 T*
const tmp = ptr.load(std::memory_order_acquire);
115 if (tmp != target) [[unlikely]] {
118 if (!d_.ptr.compare_exchange_strong(
121 std::memory_order_acq_rel,
122 std::memory_order_acquire)) {
128 if (ptr.load(std::memory_order_relaxed) == target) [[unlikely]] {
148 if (!d_.ptr.compare_exchange_strong(
151 std::memory_order_acq_rel,
152 std::memory_order_acquire)) {
182 if (ptr ==
nullptr)
return;
184 bool two_refs =
false;
185 for (data& d : ptr_set_()) {
186 if (!std::exchange(two_refs,
true))
190 if (d.ptr.compare_exchange_strong(
193 std::memory_order_release,
194 std::memory_order_relaxed)) {
200 if (std::exchange(two_refs,
false)) release_(ptr);
213 static auto reset(std::atomic<T*>& ptr)
216 release(ptr.exchange(
nullptr, std::memory_order_release));
234 release(ptr.exchange(new_value.detach(), std::memory_order_release));
259 static auto exchange(std::atomic<T*>& ptr, std::nullptr_t new_value)
262 T*
const rv = ptr.exchange(
nullptr, std::memory_order_acq_rel);
275 T*
const rv = ptr.exchange(new_value.detach(), std::memory_order_acq_rel);
306 T* expect = expected.get();
307 if (ptr.compare_exchange_weak(
310 std::memory_order_acq_rel,
311 std::memory_order_relaxed)) {
317 expected =
hazard()(ptr, expect);
337 T* expect = expected.get();
338 if (ptr.compare_exchange_strong(
341 std::memory_order_acq_rel,
342 std::memory_order_relaxed)) {
348 auto actual = hz(ptr, expect);
349 if (expected != actual) {
350 expected = std::move(actual);
361 alignas(
sizeof(ptr_set))
static inline ptr_set ptr_set_impl_;
364 static auto ptr_set_()
367 return ptr_set_impl_;
371 static auto allocate_()
374 static std::atomic<unsigned int> seq_{ 0u };
376 ptr_set& ps = ptr_set_();
377 return ps[seq_.fetch_add(1u, std::memory_order_relaxed) % ps.size()];
381 static auto acquire_(T* ptr)
385 if (ptr !=
nullptr) intrusive_ptr_add_ref(ptr);
390 static auto release_(T* ptr)
394 if (ptr !=
nullptr) intrusive_ptr_release(ptr);
426 #if __cplusplus >= 201703 427 static constexpr
bool is_always_lock_free = std::atomic<T*>::is_always_lock_free;
435 return ptr_.is_lock_free();
442 return ptr_.is_lock_free();
455 : ptr_(p.ptr_.exchange(
nullptr, std::memory_order_acq_rel))
494 return *
this = p.get();
509 return *
this = p.exchange(
nullptr);
547 auto operator=([[maybe_unused]]
const std::nullptr_t nil)
570 auto reset([[maybe_unused]] std::nullptr_t nil)
706 return x.ptr_.load(std::memory_order_acquire) == y;
713 return x.ptr_.load(std::memory_order_acquire) == y;
720 -> std::enable_if_t<std::is_convertible_v<U*, T*>,
bool> {
742 -> std::enable_if_t<std::is_convertible_v<U*, T*>,
bool> {
764 -> std::enable_if_t<std::is_convertible_v<U*, T*>,
bool> {
786 -> std::enable_if_t<std::is_convertible_v<U*, T*>,
bool> {
792 std::atomic<T*> ptr_ =
nullptr;
~hazard_ptr() noexcept
Destructor.
Definition: hazard.h:481
auto operator=(hazard_ptr &&p) noexcept -> pointer
Move assignment.
Definition: hazard.h:506
Definition: generation.h:19
auto load() const noexcept -> pointer
Read the value of this.
Definition: hazard.h:626
hazard_ptr() noexcept=default
Default constructor initializes to nullptr.
auto compare_exchange_strong(pointer &expected, pointer desired) noexcept -> bool
Strong compare-exchange operation.
Definition: hazard.h:696
static auto exchange(std::atomic< T * > &ptr, const pointer &new_value) noexcept -> pointer
Exchange the pointer.
Definition: hazard.h:285
friend auto operator!=(std::nullptr_t x, const hazard_ptr &y) noexcept -> bool
Inequality comparison.
Definition: hazard.h:769
friend auto operator==(const hazard_ptr &x, const intrusive_ptr< U > &y) noexcept -> std::enable_if_t< std::is_convertible_v< U *, T * >, bool >
Equality comparison.
Definition: hazard.h:718
auto operator()(const std::atomic< T * > &ptr) noexcept -> pointer
Load value in ptr.
Definition: hazard.h:83
friend auto operator!=(const hazard_ptr &x, const intrusive_ptr< U > &y) noexcept -> std::enable_if_t< std::is_convertible_v< U *, T * >, bool >
Inequality comparison.
Definition: hazard.h:762
friend auto operator==(std::nullptr_t x, const hazard_ptr &y) noexcept -> bool
Equality comparison.
Definition: hazard.h:725
Intrusive pointer.
Definition: intrusive_ptr.h:21
auto is_lock_free() const volatile noexcept -> bool
Test if this instance is lock free.
Definition: hazard.h:439
friend auto operator==(std::add_const_t< T > *x, const hazard_ptr &y) noexcept -> bool
Equality comparison.
Definition: hazard.h:732
friend auto operator==(const intrusive_ptr< U > &x, const hazard_ptr &y) noexcept -> std::enable_if_t< std::is_convertible_v< U *, T * >, bool >
Equality comparison.
Definition: hazard.h:740
friend auto operator==(const hazard_ptr &x, std::add_const_t< T > *y) noexcept -> bool
Equality comparison.
Definition: hazard.h:710
friend auto operator==(const hazard_ptr &x, std::nullptr_t y) noexcept -> bool
Equality comparison.
Definition: hazard.h:703
static auto reset(std::atomic< T * > &ptr) noexcept -> void
Reset the pointer.
Definition: hazard.h:213
Hazard pointer.
Definition: hazard.h:413
friend auto operator!=(const hazard_ptr &x, std::nullptr_t y) noexcept -> bool
Inequality comparison.
Definition: hazard.h:747
friend auto operator!=(const intrusive_ptr< U > &x, const hazard_ptr &y) noexcept -> std::enable_if_t< std::is_convertible_v< U *, T * >, bool >
Inequality comparison.
Definition: hazard.h:784
hazard_ptr(pointer &&p) noexcept
Initializing constructor.
Definition: hazard.h:466
static auto compare_exchange_strong(std::atomic< T * > &ptr, pointer &expected, pointer desired) noexcept -> bool
Compare-exchange operation.
Definition: hazard.h:331
auto get() const noexcept -> pointer
Read the value of this.
Definition: hazard.h:613
auto store(const pointer &p) noexcept -> void
Assignment.
Definition: hazard.h:651
auto operator=(pointer &&p) noexcept -> pointer
Move assignment.
Definition: hazard.h:534
static auto exchange(std::atomic< T * > &ptr, pointer &&new_value) noexcept -> pointer
Exchange the pointer.
Definition: hazard.h:272
auto is_lock_free() const noexcept -> bool
Test if this instance is lock free.
Definition: hazard.h:432
auto operator=(const pointer &p) noexcept -> pointer
Copy assignment.
Definition: hazard.h:518
auto reset() noexcept -> void
Reset this.
Definition: hazard.h:559
auto reset([[maybe_unused]] std::nullptr_t nil) noexcept -> void
Reset this.
Definition: hazard.h:570
auto reset(pointer &&p) noexcept -> void
Assignment.
Definition: hazard.h:584
auto store(pointer &&p) noexcept -> void
Assignment.
Definition: hazard.h:640
auto operator=([[maybe_unused]] const std::nullptr_t nil) noexcept -> pointer
nullptr assignment.
Definition: hazard.h:547
static auto reset(std::atomic< T * > &ptr, const pointer &new_value) noexcept -> void
Reset the pointer to the given new value.
Definition: hazard.h:248
auto reset(const pointer &p) noexcept -> void
Assignment.
Definition: hazard.h:595
friend auto operator!=(std::add_const_t< T > *x, const hazard_ptr &y) noexcept -> bool
Inequality comparison.
Definition: hazard.h:776
auto compare_exchange_weak(pointer &expected, pointer desired) noexcept -> bool
Weak compare-exchange operation.
Definition: hazard.h:689
typename hazard_t::pointer pointer
Smart pointer equivalent.
Definition: hazard.h:422
hazard_ptr(const pointer &p) noexcept
Initializing constructor.
Definition: hazard.h:475
static auto exchange(std::atomic< T * > &ptr, std::nullptr_t new_value) noexcept -> pointer
Exchange the pointer.
Definition: hazard.h:259
auto exchange(const pointer &p) noexcept -> pointer
Exchange operation.
Definition: hazard.h:682
Hazard pointer algorithm.
Definition: hazard.h:28
static auto release(T *&&ptr) noexcept -> void
Release pointer, granting it to a hazard operation, if possible.
Definition: hazard.h:179
static auto compare_exchange_weak(std::atomic< T * > &ptr, pointer &expected, pointer desired) noexcept -> bool
Compare-exchange operation.
Definition: hazard.h:303
hazard() noexcept
Create hazard context.
Definition: hazard.h:76
auto exchange(pointer &&p) noexcept -> pointer
Exchange operation.
Definition: hazard.h:668
friend auto operator!=(const hazard_ptr &x, std::add_const_t< T > *y) noexcept -> bool
Inequality comparison.
Definition: hazard.h:754
intrusive_ptr< T > pointer
Pointer used by this algorithm.
Definition: hazard.h:70
hazard_ptr(hazard_ptr &&p) noexcept
Move construction.
Definition: hazard.h:454
pointer value_type
Type held in this atomic.
Definition: hazard.h:424
static auto reset(std::atomic< T * > &ptr, pointer &&new_value) noexcept -> void
Reset the pointer to the given new value.
Definition: hazard.h:231
auto operator=(const hazard_ptr &p) noexcept -> pointer
Copy assignment.
Definition: hazard.h:491