Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
shared_storage.hpp
Go to the documentation of this file.
1#pragma once
4#include <atomic>
5#include <concepts>
6#include <cstddef>
7#include <memory>
8#include <new>
9#include <type_traits>
11#include <utility>
12
13namespace uni20::async
14{
15
16class NodeInfo;
17
18namespace detail
19{
20
25class shared_storage_control_block {
26 public:
27 explicit shared_storage_control_block(shared_storage_control_block* lifetime_owner = nullptr) noexcept
28 : lifetime_owner_(lifetime_owner)
29 {
30 if (lifetime_owner_) lifetime_owner_->add_ref();
31 }
32
33 shared_storage_control_block(shared_storage_control_block const&) = delete;
34 shared_storage_control_block& operator=(shared_storage_control_block const&) = delete;
35
36 virtual ~shared_storage_control_block()
37 {
38 if (lifetime_owner_) lifetime_owner_->release_ref();
39 }
40
41 void add_ref() noexcept { strong_count_.fetch_add(1, std::memory_order_relaxed); }
42
43 void release_ref() noexcept
44 {
45 if (strong_count_.fetch_sub(1, std::memory_order_acq_rel) == 1) delete this;
46 }
47
48 [[nodiscard]] std::size_t use_count() const noexcept { return strong_count_.load(std::memory_order_relaxed); }
49
50 [[nodiscard]] bool constructed() const noexcept
51 {
52 return this->local_value_constructed() && (!lifetime_owner_ || lifetime_owner_->constructed());
53 }
54
55 [[nodiscard]] bool has_lifetime_owner() const noexcept { return lifetime_owner_ != nullptr; }
56
57 private:
58 [[nodiscard]] virtual bool local_value_constructed() const noexcept = 0;
59
60 std::atomic<std::size_t> strong_count_{1};
61 shared_storage_control_block* lifetime_owner_ = nullptr;
62};
63
64} // namespace detail
65
94template <typename T> class shared_storage {
95 private:
96 friend class NodeInfo;
97
99 struct control_block final : detail::shared_storage_control_block
100 {
101 explicit control_block(detail::shared_storage_control_block* lifetime_owner = nullptr) noexcept
102 : detail::shared_storage_control_block(lifetime_owner)
103 {}
104
105 alignas(T) unsigned char storage[sizeof(T)];
106 std::atomic<bool> constructed_{false};
107
110 T* storage_address() noexcept { return reinterpret_cast<T*>(storage); }
111
114 T* ptr() noexcept { return std::launder(this->storage_address()); }
115
119 template <typename... Args> void construct(Args&&... args)
120 {
121 DEBUG_CHECK(!constructed_.load(std::memory_order_relaxed));
122 ::new (storage) T(std::forward<Args>(args)...);
123 constructed_.store(true, std::memory_order_release);
124 }
125
127 void destroy_object() noexcept
128 {
129 if (constructed_.exchange(false, std::memory_order_acq_rel)) ptr()->~T();
130 }
131
132 [[nodiscard]] bool value_constructed_locally() const noexcept
133 {
134 return constructed_.load(std::memory_order_acquire);
135 }
136
137 ~control_block() override { this->destroy_object(); }
138
139 private:
140 [[nodiscard]] bool local_value_constructed() const noexcept override
141 {
142 return this->value_constructed_locally();
143 }
144 };
145
146 control_block* ctrl_ = nullptr;
147
148 template <typename U, typename... Args> friend shared_storage<U> make_shared_storage(Args&&... args);
149 template <typename U> friend shared_storage<U> make_unconstructed_shared_storage();
150 template <typename U, typename Owner, typename... Args>
151 friend shared_storage<U> make_shared_storage_alias(shared_storage<Owner> const& owner, Args&&... args);
152
155 explicit shared_storage(control_block* c) noexcept : ctrl_(c) {}
156
160 [[nodiscard]] static bool diagnostic_constructed_from_control(void const* control) noexcept
161 {
162 auto const* block = static_cast<control_block const*>(control);
163 return block && block->constructed();
164 }
165
169 [[nodiscard]] static void const* diagnostic_value_address_from_control(void const* control) noexcept
170 {
171 auto* block = const_cast<control_block*>(static_cast<control_block const*>(control));
172 return block && block->constructed() ? static_cast<void const*>(block->ptr()) : nullptr;
173 }
174
175 public:
176 using element_type = T;
177
179 shared_storage() noexcept = default;
180
183 shared_storage(const shared_storage& other) noexcept : ctrl_(other.ctrl_)
184 {
185 if (ctrl_) ctrl_->add_ref();
186 }
187
190 shared_storage(shared_storage&& other) noexcept : ctrl_(std::exchange(other.ctrl_, nullptr)) {}
191
196 {
197 if (this != &other)
198 {
199 this->reset();
200 ctrl_ = other.ctrl_;
201 if (ctrl_) ctrl_->add_ref();
202 }
203 return *this;
204 }
205
210 {
211 if (this != &other)
212 {
213 this->reset();
214 ctrl_ = std::exchange(other.ctrl_, nullptr);
215 }
216 return *this;
217 }
218
221
223 void reset() noexcept
224 {
225 if (ctrl_)
226 {
227 ctrl_->release_ref();
228 ctrl_ = nullptr;
229 }
230 }
231
234 [[nodiscard]] bool constructed() const noexcept { return ctrl_ && ctrl_->constructed(); }
235
238 [[nodiscard]] bool valid() const noexcept { return ctrl_ != nullptr; }
239
242 [[nodiscard]] void const* control_address() const noexcept { return static_cast<void const*>(ctrl_); }
243
246 [[nodiscard]] size_t use_count() const noexcept { return ctrl_ ? ctrl_->use_count() : 0; }
247
250 [[nodiscard]] bool has_lifetime_owner() const noexcept { return ctrl_ && ctrl_->has_lifetime_owner(); }
251
258 [[nodiscard]] T* storage_address() noexcept { return ctrl_ ? ctrl_->storage_address() : nullptr; }
259
262 [[nodiscard]] T const* storage_address() const noexcept { return ctrl_ ? ctrl_->storage_address() : nullptr; }
263
268 template <typename... Args>
269 requires std::constructible_from<T, Args...>
270 T& emplace(Args&&... args)
271 {
272 DEBUG_CHECK(ctrl_, "shared_storage must be initialized with make_shared_storage()");
273 if (ctrl_->value_constructed_locally()) ctrl_->destroy_object();
274 ctrl_->construct(std::forward<Args>(args)...);
275 return *ctrl_->ptr();
276 }
277
280 bool operator!() const noexcept { return !ctrl_; }; // no control block
283 explicit operator bool() const noexcept { return bool(ctrl_); }; // has control block
284
286 void destroy() noexcept
287 {
288 if (ctrl_) ctrl_->destroy_object();
289 }
290
293 [[nodiscard]] T* get() noexcept { return constructed() ? ctrl_->ptr() : nullptr; }
296 [[nodiscard]] const T* get() const noexcept { return this->constructed() ? ctrl_->ptr() : nullptr; }
297
300 T& operator*() noexcept
301 {
303 return *this->get();
304 }
307 const T& operator*() const noexcept
308 {
310 return *this->get();
311 }
312
315 T* operator->() noexcept
316 {
318 return this->get();
319 }
322 const T* operator->() const noexcept
323 {
325 return get();
326 }
327
330 T take() noexcept(std::is_nothrow_move_constructible_v<T>)
331 {
332 T x = std::move(**this);
333 this->destroy();
334 return x;
335 }
336
341 template <typename... Args> static shared_storage make_constructed(Args&&... args)
342 {
343 auto* c = new control_block{};
344 try
345 {
346 c->construct(std::forward<Args>(args)...);
347 }
348 catch (...)
349 {
350 delete c;
351 throw;
352 }
353 return shared_storage(c);
354 }
355
357 template <typename... Args>
358 static shared_storage make_constructed_alias(detail::shared_storage_control_block* lifetime_owner, Args&&... args)
359 {
360 auto* c = new control_block{lifetime_owner};
361 try
362 {
363 c->construct(std::forward<Args>(args)...);
364 }
365 catch (...)
366 {
367 delete c;
368 throw;
369 }
370 return shared_storage(c);
371 }
372
377 friend bool operator==(const shared_storage& a, const shared_storage& b) noexcept { return a.ctrl_ == b.ctrl_; }
378};
379
381template <typename T> [[nodiscard]] inline shared_storage<T> make_unconstructed_shared_storage()
382{
383 using ctrl_t = typename shared_storage<T>::control_block;
384 return shared_storage<T>(new ctrl_t{});
385}
386
392template <typename T, typename... Args> [[nodiscard]] inline shared_storage<T> make_shared_storage(Args&&... args)
393{
394 return shared_storage<T>::make_constructed(std::forward<Args>(args)...);
395}
396
407template <typename T, typename Owner, typename... Args>
408[[nodiscard]] inline shared_storage<T> make_shared_storage_alias(shared_storage<Owner> const& owner, Args&&... args)
409{
410 CHECK(owner.valid());
411 return shared_storage<T>::make_constructed_alias(owner.ctrl_, std::forward<Args>(args)...);
412}
413
414} // namespace uni20::async
Records node information for DAG/debugging visualization.
Definition async_node.hpp:171
A lightweight, thread-safe, reference-counted storage for a single object.
Definition shared_storage.hpp:94
T take() noexcept(std::is_nothrow_move_constructible_v< T >)
Move the contained value out and destroy the in-place object.
Definition shared_storage.hpp:330
shared_storage & operator=(const shared_storage &other) noexcept
Copy assignment shares the control block.
Definition shared_storage.hpp:195
bool valid() const noexcept
Reports whether a control block is present.
Definition shared_storage.hpp:238
const T * get() const noexcept
Returns a const pointer to the constructed value.
Definition shared_storage.hpp:296
void const * control_address() const noexcept
Returns the storage control-block address for diagnostics.
Definition shared_storage.hpp:242
friend shared_storage< U > make_shared_storage(Args &&... args)
Create a new shared_storage<T> with T(args...) in-place.
Definition shared_storage.hpp:392
shared_storage & operator=(shared_storage &&other) noexcept
Move assignment transfers the control block pointer.
Definition shared_storage.hpp:209
void destroy() noexcept
Destroy the contained object while keeping the control block alive.
Definition shared_storage.hpp:286
T * operator->() noexcept
Pointer-style mutable access to the constructed value.
Definition shared_storage.hpp:315
shared_storage() noexcept=default
Construct an empty handle with no control block.
T * storage_address() noexcept
Return the address reserved for the contained value.
Definition shared_storage.hpp:258
size_t use_count() const noexcept
Returns the current strong reference count.
Definition shared_storage.hpp:246
friend shared_storage< U > make_unconstructed_shared_storage()
Create a new shared_storage<T> with an unconstructed T.
Definition shared_storage.hpp:381
T element_type
Definition shared_storage.hpp:176
bool has_lifetime_owner() const noexcept
Reports whether this value retains another storage control block.
Definition shared_storage.hpp:250
const T * operator->() const noexcept
Pointer-style const access to the constructed value.
Definition shared_storage.hpp:322
friend bool operator==(const shared_storage &a, const shared_storage &b) noexcept
Compare whether two handles reference the same control block.
Definition shared_storage.hpp:377
void reset() noexcept
Release this handle's reference to the control block.
Definition shared_storage.hpp:223
bool operator!() const noexcept
Returns true when no control block is present.
Definition shared_storage.hpp:280
T & operator*() noexcept
Dereference access to the constructed value.
Definition shared_storage.hpp:300
static shared_storage make_constructed_alias(detail::shared_storage_control_block *lifetime_owner, Args &&... args)
Construct a value whose control block retains a separate lifetime owner.
Definition shared_storage.hpp:358
const T & operator*() const noexcept
Dereference access to the constructed value.
Definition shared_storage.hpp:307
T const * storage_address() const noexcept
Return the const address reserved for the contained value.
Definition shared_storage.hpp:262
friend shared_storage< U > make_shared_storage_alias(shared_storage< Owner > const &owner, Args &&... args)
Create storage for a value that keeps another storage value alive.
Definition shared_storage.hpp:408
~shared_storage()
Destructor releases one reference to the control block.
Definition shared_storage.hpp:220
bool constructed() const noexcept
Reports whether a value is currently constructed.
Definition shared_storage.hpp:234
static shared_storage make_constructed(Args &&... args)
Construct storage with an immediately-constructed value.
Definition shared_storage.hpp:341
shared_storage(shared_storage &&other) noexcept
Move constructor transfers ownership of the control block pointer.
Definition shared_storage.hpp:190
T & emplace(Args &&... args)
Destroy any existing value and construct a new one in place.
Definition shared_storage.hpp:270
T * get() noexcept
Returns a mutable pointer to the constructed value.
Definition shared_storage.hpp:293
Definition qnum.hpp:403
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
shared_storage< T > make_shared_storage(Args &&... args)
Create a new shared_storage<T> with T(args...) in-place.
Definition shared_storage.hpp:392
shared_storage< T > make_unconstructed_shared_storage()
Create a new shared_storage<T> with an unconstructed T.
Definition shared_storage.hpp:381
shared_storage< T > make_shared_storage_alias(shared_storage< Owner > const &owner, Args &&... args)
Create storage for a value that keeps another storage value alive.
Definition shared_storage.hpp:408
Definition cli.cpp:6
#define CHECK(cond,...)
Definition trace.hpp:299
#define DEBUG_CHECK(cond,...)
Definition trace.hpp:608