MADNESS 0.10.1
atomic_shared_ptr.h
Go to the documentation of this file.
1/**
2 \file atomic_shared_ptr.h
3 \brief Defines \c atomic_shared_ptr — a std::shared_ptr-compatible
4 wrapper backed by atomic storage.
5 \ingroup world
6*/
7
8#ifndef MADNESS_WORLD_ATOMIC_SHARED_PTR_H__INCLUDED
9#define MADNESS_WORLD_ATOMIC_SHARED_PTR_H__INCLUDED
10
11#include <atomic>
12#include <memory>
14
15namespace madness {
16
17/// A \c std::shared_ptr-like handle backed by atomic storage.
18///
19/// Uses \c std::atomic<std::shared_ptr<T>> when available (C++20), otherwise
20/// falls back to the pre-C++20 atomic free functions on \c std::shared_ptr.
21/// The public API mirrors \c std::shared_ptr (copy construction/assignment,
22/// \c operator->, \c operator bool, implicit conversion, \c reset,
23/// \c use_count, equality comparison) plus a single extension, \c exchange(),
24/// needed for the move-optimisation in \c Future::get()&&.
25///
26/// \note Move operations are explicitly provided: they atomically take the
27/// value from the source (leaving it empty) via a relaxed-order \c exchange.
28/// This is necessary because (a) user-declared copy operations would otherwise
29/// suppress the implicit move operations, and (b) \c std::atomic is non-movable
30/// in the C++20 backend, so the implicit move would silently fall back to copy.
31///
32/// \note All loads/stores and move operations use \c memory_order_relaxed,
33/// relying on the caller to provide necessary ordering guarantees. The exception
34/// is \c exchange(), which defaults to \c memory_order_seq_cst to match the
35/// pre-C++20 \c std::atomic_exchange behaviour it replaces.
36template<typename T>
38#if defined(__cpp_lib_atomic_shared_ptr)
39 std::atomic<std::shared_ptr<T>> ptr_;
40
41 std::shared_ptr<T> do_load() const noexcept {
42 return ptr_.load(std::memory_order_relaxed);
43 }
44 void do_store(std::shared_ptr<T> p) noexcept {
45 ptr_.store(std::move(p), std::memory_order_relaxed);
46 }
47#else
48 std::shared_ptr<T> ptr_;
49
50 // The atomic_*_explicit free functions on std::shared_ptr are deprecated
51 // in C++20 (superseded by std::atomic<shared_ptr<T>>). We reach this
52 // branch only when the library lacks __cpp_lib_atomic_shared_ptr, which
53 // can happen even on a C++20 compiler (old stdlib), so suppress the
54 // warning specifically for C++20 builds where it would fire.
55#if __cplusplus >= 202002L
56 MADNESS_PRAGMA_CLANG(diagnostic push)
57 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated-declarations")
58 MADNESS_PRAGMA_GCC(diagnostic push)
59 MADNESS_PRAGMA_GCC(diagnostic ignored "-Wdeprecated-declarations")
60#endif
61 // Payload ordering supplied by FutureImpl's Spinlock; relaxed for identity.
62 std::shared_ptr<T> do_load() const noexcept {
63 return std::atomic_load_explicit(&ptr_, std::memory_order_relaxed);
64 }
65 void do_store(std::shared_ptr<T> p) noexcept {
66 std::atomic_store_explicit(&ptr_, std::move(p), std::memory_order_relaxed);
67 }
68#if __cplusplus >= 202002L
69 MADNESS_PRAGMA_CLANG(diagnostic pop)
70 MADNESS_PRAGMA_GCC(diagnostic pop)
71#endif
72#endif
73
74public:
75 constexpr atomic_shared_ptr() noexcept = default;
76
77 atomic_shared_ptr(std::shared_ptr<T> p) noexcept : ptr_(std::move(p)) {}
78
79 /// Copy-constructs by atomically loading the other pointer.
80 atomic_shared_ptr(const atomic_shared_ptr& other) noexcept : ptr_(other.do_load()) {}
81
82 /// Move-constructs by atomically taking the value from \p other, which
83 /// is left empty. Uses \c memory_order_relaxed (consistent with
84 /// \c do_load / \c do_store); caller is responsible for any synchronization.
86 : ptr_(other.exchange({}, std::memory_order_relaxed)) {}
87
88 /// Copy-assigns by atomically loading from \p other and storing.
90 if (this != &other) do_store(other.do_load());
91 return *this;
92 }
93
94 /// Move-assigns by atomically taking the value from \p other, which
95 /// is left empty. Self-assignment is a no-op.
97 if (this != &other) do_store(other.exchange({}, std::memory_order_relaxed));
98 return *this;
99 }
100
101 /// Assigns a new shared_ptr value atomically.
102 atomic_shared_ptr& operator=(std::shared_ptr<T> p) noexcept {
103 do_store(std::move(p));
104 return *this;
105 }
106
107 /// Replaces the stored pointer, adopting \p p (may be null).
108 void reset(T* p) { do_store(std::shared_ptr<T>(p)); }
109
110 /// Replaces the stored pointer with an empty shared pointer.
111 void reset() { do_store(std::shared_ptr<T>()); }
112
113 explicit operator bool() const noexcept { return bool(do_load()); }
114
115 /// Dereferences the stored pointer.
116 /// Safe because \c do_load() returns a \c shared_ptr temporary whose
117 /// lifetime extends to the end of the full expression containing the
118 /// \c -> call, keeping the referent alive across the dereference.
119 T* operator->() noexcept { return do_load().get(); }
120
121 /// Dereferences the stored pointer, const overload.
122 /// Safe because \c do_load() returns a \c shared_ptr temporary whose
123 /// lifetime extends to the end of the full expression containing the
124 /// \c -> call, keeping the referent alive across the dereference.
125 const T* operator->() const noexcept { return do_load().get(); }
126
127 /// Implicit conversion to \c shared_ptr, used for lifetime extension and
128 /// APIs that require a \c shared_ptr (e.g. \c RemoteReference).
129 operator std::shared_ptr<T>() const noexcept { return do_load(); }
130
131 long use_count() const noexcept { return do_load().use_count(); }
132
133 bool operator==(const atomic_shared_ptr& r) const noexcept { return do_load() == r.do_load(); }
134 bool operator!=(const atomic_shared_ptr& r) const noexcept { return do_load() != r.do_load(); }
135
136 /// Atomically replaces the stored pointer with \p p and returns the old
137 /// value. This is the only operation that differs from \c std::shared_ptr.
138 /// Defaults to \c memory_order_seq_cst to match the pre-C++20
139 /// \c std::atomic_exchange behaviour this replaces.
140 std::shared_ptr<T> exchange(std::shared_ptr<T> p = {},
141 std::memory_order order = std::memory_order_seq_cst) noexcept {
142#if defined(__cpp_lib_atomic_shared_ptr)
143 return ptr_.exchange(std::move(p), order);
144#else
145#if __cplusplus >= 202002L
146 MADNESS_PRAGMA_CLANG(diagnostic push)
147 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated-declarations")
148 MADNESS_PRAGMA_GCC(diagnostic push)
149 MADNESS_PRAGMA_GCC(diagnostic ignored "-Wdeprecated-declarations")
150#endif
151 return std::atomic_exchange_explicit(&ptr_, std::move(p), order);
152#if __cplusplus >= 202002L
153 MADNESS_PRAGMA_CLANG(diagnostic pop)
154 MADNESS_PRAGMA_GCC(diagnostic pop)
155#endif
156#endif
157 }
158};
159
160} // namespace madness
161
162#endif // MADNESS_WORLD_ATOMIC_SHARED_PTR_H__INCLUDED
Definition atomic_shared_ptr.h:37
atomic_shared_ptr(atomic_shared_ptr &&other) noexcept
Definition atomic_shared_ptr.h:85
void do_store(std::shared_ptr< T > p) noexcept
Definition atomic_shared_ptr.h:65
constexpr atomic_shared_ptr() noexcept=default
T * operator->() noexcept
Definition atomic_shared_ptr.h:119
std::shared_ptr< T > ptr_
Definition atomic_shared_ptr.h:48
void reset()
Replaces the stored pointer with an empty shared pointer.
Definition atomic_shared_ptr.h:111
void reset(T *p)
Replaces the stored pointer, adopting p (may be null).
Definition atomic_shared_ptr.h:108
long use_count() const noexcept
Definition atomic_shared_ptr.h:131
const T * operator->() const noexcept
Definition atomic_shared_ptr.h:125
bool operator==(const atomic_shared_ptr &r) const noexcept
Definition atomic_shared_ptr.h:133
atomic_shared_ptr & operator=(std::shared_ptr< T > p) noexcept
Assigns a new shared_ptr value atomically.
Definition atomic_shared_ptr.h:102
atomic_shared_ptr(const atomic_shared_ptr &other) noexcept
Copy-constructs by atomically loading the other pointer.
Definition atomic_shared_ptr.h:80
atomic_shared_ptr & operator=(const atomic_shared_ptr &other) noexcept
Copy-assigns by atomically loading from other and storing.
Definition atomic_shared_ptr.h:89
bool operator!=(const atomic_shared_ptr &r) const noexcept
Definition atomic_shared_ptr.h:134
std::shared_ptr< T > exchange(std::shared_ptr< T > p={}, std::memory_order order=std::memory_order_seq_cst) noexcept
Definition atomic_shared_ptr.h:140
std::shared_ptr< T > do_load() const noexcept
Definition atomic_shared_ptr.h:62
atomic_shared_ptr & operator=(atomic_shared_ptr &&other) noexcept
Definition atomic_shared_ptr.h:96
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
auto T(World &world, response_space &f) -> response_space
Definition global_functions.cc:28
Macros and tools pertaining to the configuration of MADNESS.
#define MADNESS_PRAGMA_CLANG(x)
Definition madness_config.h:200
#define MADNESS_PRAGMA_GCC(x)
Definition madness_config.h:205
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
static double pop(std::vector< double > &v)
Definition SCF.cc:115
Definition mraimpl.h:51