25class shared_storage_control_block {
27 explicit shared_storage_control_block(shared_storage_control_block* lifetime_owner =
nullptr) noexcept
28 : lifetime_owner_(lifetime_owner)
30 if (lifetime_owner_) lifetime_owner_->add_ref();
33 shared_storage_control_block(shared_storage_control_block
const&) =
delete;
34 shared_storage_control_block& operator=(shared_storage_control_block
const&) =
delete;
36 virtual ~shared_storage_control_block()
38 if (lifetime_owner_) lifetime_owner_->release_ref();
41 void add_ref() noexcept { strong_count_.fetch_add(1, std::memory_order_relaxed); }
43 void release_ref() noexcept
45 if (strong_count_.fetch_sub(1, std::memory_order_acq_rel) == 1)
delete this;
48 [[nodiscard]] std::size_t use_count() const noexcept {
return strong_count_.load(std::memory_order_relaxed); }
50 [[nodiscard]]
bool constructed() const noexcept
52 return this->local_value_constructed() && (!lifetime_owner_ || lifetime_owner_->constructed());
55 [[nodiscard]]
bool has_lifetime_owner() const noexcept {
return lifetime_owner_ !=
nullptr; }
58 [[nodiscard]]
virtual bool local_value_constructed() const noexcept = 0;
60 std::atomic<
std::
size_t> strong_count_{1};
61 shared_storage_control_block* lifetime_owner_ =
nullptr;
99 struct control_block final : detail::shared_storage_control_block
101 explicit control_block(detail::shared_storage_control_block* lifetime_owner =
nullptr) noexcept
102 : detail::shared_storage_control_block(lifetime_owner)
105 alignas(T)
unsigned char storage[
sizeof(T)];
106 std::atomic<bool> constructed_{
false};
110 T* storage_address() noexcept {
return reinterpret_cast<T*
>(storage); }
114 T* ptr() noexcept {
return std::launder(this->storage_address()); }
119 template <
typename... Args>
void construct(Args&&... args)
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);
127 void destroy_object() noexcept
129 if (constructed_.exchange(
false, std::memory_order_acq_rel)) ptr()->~T();
132 [[nodiscard]]
bool value_constructed_locally() const noexcept
134 return constructed_.load(std::memory_order_acquire);
137 ~control_block()
override { this->destroy_object(); }
140 [[nodiscard]]
bool local_value_constructed() const noexcept
override
142 return this->value_constructed_locally();
146 control_block* ctrl_ =
nullptr;
148 template <
typename U,
typename... Args>
friend shared_storage<U>
make_shared_storage(Args&&... args);
150 template <
typename U,
typename Owner,
typename... Args>
160 [[nodiscard]]
static bool diagnostic_constructed_from_control(
void const* control)
noexcept
162 auto const* block =
static_cast<control_block const*
>(control);
163 return block && block->constructed();
169 [[nodiscard]]
static void const* diagnostic_value_address_from_control(
void const* control)
noexcept
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;
185 if (ctrl_) ctrl_->add_ref();
201 if (ctrl_) ctrl_->add_ref();
214 ctrl_ = std::exchange(other.ctrl_,
nullptr);
227 ctrl_->release_ref();
234 [[nodiscard]]
bool constructed() const noexcept {
return ctrl_ && ctrl_->constructed(); }
238 [[nodiscard]]
bool valid() const noexcept {
return ctrl_ !=
nullptr; }
242 [[nodiscard]]
void const*
control_address() const noexcept {
return static_cast<void const*
>(ctrl_); }
246 [[nodiscard]]
size_t use_count() const noexcept {
return ctrl_ ? ctrl_->use_count() : 0; }
250 [[nodiscard]]
bool has_lifetime_owner() const noexcept {
return ctrl_ && ctrl_->has_lifetime_owner(); }
258 [[nodiscard]] T*
storage_address() noexcept {
return ctrl_ ? ctrl_->storage_address() :
nullptr; }
262 [[nodiscard]] T
const*
storage_address() const noexcept {
return ctrl_ ? ctrl_->storage_address() :
nullptr; }
268 template <
typename... Args>
269 requires std::constructible_from<T, Args...>
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();
283 explicit operator bool() const noexcept {
return bool(ctrl_); };
288 if (ctrl_) ctrl_->destroy_object();
293 [[nodiscard]] T*
get() noexcept {
return constructed() ? ctrl_->ptr() :
nullptr; }
296 [[nodiscard]]
const T*
get() const noexcept {
return this->
constructed() ? ctrl_->ptr() :
nullptr; }
330 T
take() noexcept(
std::is_nothrow_move_constructible_v<T>)
332 T x = std::move(**
this);
343 auto*
c =
new control_block{};
346 c->construct(std::forward<Args>(args)...);
357 template <
typename... Args>
360 auto*
c =
new control_block{lifetime_owner};
363 c->construct(std::forward<Args>(args)...);
407template <
typename T,
typename Owner,
typename... Args>
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
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
#define CHECK(cond,...)
Definition trace.hpp:299
#define DEBUG_CHECK(cond,...)
Definition trace.hpp:608