Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
storage_buffer.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <atomic>
9#include <cstddef>
10#include <memory>
11#include <mutex>
12#include <new>
13#include <type_traits>
14#include <utility>
15
16namespace uni20::async::detail
17{
18
21template <typename T> class StorageBuffer {
22 public:
24 StorageBuffer() = default;
25
29 template <typename... Args> explicit StorageBuffer(Args&&... args) { this->construct(std::forward<Args>(args)...); }
30
31 StorageBuffer(StorageBuffer const&) = delete;
32 StorageBuffer& operator=(StorageBuffer const&) = delete;
33
34 StorageBuffer(StorageBuffer&&) = delete;
35 StorageBuffer& operator=(StorageBuffer&&) = delete;
36
38 ~StorageBuffer() { this->destroy_if_owned(); }
39
42 T* get() noexcept { return value_ptr_cache_.load(std::memory_order_acquire); }
45 T const* get() const noexcept { return value_ptr_cache_.load(std::memory_order_acquire); }
46
49 bool constructed() const noexcept { return constructed_.load(std::memory_order_acquire); }
50
55 template <typename... Args> T* construct(Args&&... args)
56 {
57 this->destroy_if_owned();
58 external_owner_.reset();
59 auto* ptr = this->storage_ptr();
60 std::construct_at(ptr, std::forward<Args>(args)...);
61 owns_storage_ = true;
62 constructed_.store(true, std::memory_order_release);
63 value_ptr_cache_.store(ptr, std::memory_order_release);
64 return ptr;
65 }
66
69 T* ensure_default()
70 {
71 if (auto* cached = this->get()) return cached;
72
73 if constexpr (!std::is_default_constructible_v<T>)
74 {
75 return nullptr;
76 }
77 else
78 {
79 std::call_once(default_once_, [this]() { this->construct(); });
80 return this->get();
81 }
82 }
83
88 T* reset_external_pointer(T* ptr, std::shared_ptr<void> owner = {}) noexcept
89 {
90 this->destroy_if_owned();
91 external_owner_ = std::move(owner);
92 owns_storage_ = false;
93 constructed_.store(true, std::memory_order_release);
94 value_ptr_cache_.store(ptr, std::memory_order_release);
95 return ptr;
96 }
97
100 void set_external_owner(std::shared_ptr<void> owner) noexcept { external_owner_ = std::move(owner); }
101
104 std::shared_ptr<void> const& external_owner() const noexcept { return external_owner_; }
105
106 private:
109 T* storage_ptr() noexcept { return std::launder(reinterpret_cast<T*>(storage_)); }
112 T const* storage_ptr() const noexcept { return std::launder(reinterpret_cast<T const*>(storage_)); }
113
115 void destroy_if_owned() noexcept
116 {
117 if (constructed_.load(std::memory_order_acquire) && owns_storage_)
118 {
119 std::destroy_at(this->storage_ptr());
120 }
121 constructed_.store(false, std::memory_order_release);
122 value_ptr_cache_.store(nullptr, std::memory_order_release);
123 }
124
125 alignas(T) std::byte storage_[sizeof(T)]{};
126 std::atomic<T*> value_ptr_cache_{nullptr};
127 std::atomic<bool> constructed_{false};
128 bool owns_storage_{false};
129 std::once_flag default_once_{};
130 std::shared_ptr<void> external_owner_{};
131};
132
133} // namespace uni20::async::detail