Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
alive_token.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <atomic>
4#include <condition_variable>
5#include <cstddef>
6#include <memory>
7#include <mutex>
8#include <utility>
9
10namespace pulp::runtime {
11
20 struct State {
21 std::atomic<bool> alive{true};
22 std::atomic<bool> retired{false};
23 std::mutex mutex;
24 std::condition_variable quiesced;
25 std::size_t active_leases = 0;
26
27 // Keep Handle source compatibility for existing callback sites that
28 // only need an atomic liveness read or fire-and-forget invalidation.
29 // Owner teardown that must wait for in-flight users calls
30 // AliveToken::retire(), which also drains active leases.
31 bool load(std::memory_order order = std::memory_order_seq_cst) const noexcept {
32 return alive.load(order);
33 }
34
35 void store(bool value, std::memory_order order = std::memory_order_seq_cst) noexcept {
36 if (!value) {
37 std::lock_guard lock(mutex);
38 retired.store(true, std::memory_order_release);
39 alive.store(false, order);
40 return;
41 }
42 // A captured Handle can outlive its AliveToken. Never allow a
43 // stale callback to resurrect a retired owner; reset() publishes
44 // a fresh State when resurrection is actually intended.
45 std::lock_guard lock(mutex);
46 if (retired.load(std::memory_order_acquire))
47 return;
48 alive.store(true, order);
49 }
50 };
51
52 public:
53 using Handle = std::shared_ptr<State>;
54
59 class Lease {
60 public:
61 Lease() noexcept = default;
62 Lease(const Lease&) = delete;
63 Lease& operator=(const Lease&) = delete;
64
65 Lease(Lease&& other) noexcept : state_(std::move(other.state_)) {}
66
67 Lease& operator=(Lease&& other) noexcept {
68 if (this != &other) {
69 release();
70 state_ = std::move(other.state_);
71 }
72 return *this;
73 }
74
76 release();
77 }
78
79 explicit operator bool() const noexcept {
80 return static_cast<bool>(state_);
81 }
82
84 void reset() noexcept {
85 release();
86 }
87
88 private:
89 friend class AliveToken;
90
91 explicit Lease(Handle state) noexcept : state_(std::move(state)) {}
92
93 void release() noexcept {
94 if (!state_)
95 return;
96 auto state = std::move(state_);
97 {
98 std::lock_guard lock(state->mutex);
99 // A Lease can only be created by try_acquire(), which bumps
100 // this counter while holding the same mutex.
101 --state->active_leases;
102 }
103 state->quiesced.notify_all();
104 }
105
106 Handle state_;
107 };
108
109 AliveToken() : state_(std::make_shared<State>()) {}
111 retire();
112 }
113
114 AliveToken(const AliveToken&) = delete;
115 AliveToken& operator=(const AliveToken&) = delete;
118
119 Handle capture() const noexcept {
120 return state_;
121 }
122
127 void retire() noexcept {
128 const auto state = state_;
129 if (!state)
130 return;
131
132 std::unique_lock lock(state->mutex);
133 state->retired.store(true, std::memory_order_release);
134 state->alive.store(false, std::memory_order_release);
135 state->quiesced.wait(lock, [&state] { return state->active_leases == 0; });
136 }
137
138 void reset() {
139 retire();
140 state_ = std::make_shared<State>();
141 }
142
143 static bool is_alive(const Handle& handle) noexcept {
144 return handle && handle->alive.load(std::memory_order_acquire);
145 }
146
147 // Preserve the read-only compatibility surface for older callback owners
148 // that still use an independent shared atomic sentinel. These sentinels do
149 // not participate in AliveToken lease admission or retirement; callers
150 // that dereference owner-backed state must use Handle plus try_acquire().
151 static bool is_alive(const std::shared_ptr<std::atomic<bool>>& handle) noexcept {
152 return handle && handle->load(std::memory_order_acquire);
153 }
154
165 static Lease try_acquire(const Handle& handle) noexcept {
166 if (!handle)
167 return {};
168 std::lock_guard lock(handle->mutex);
169 if (handle->retired.load(std::memory_order_acquire) ||
170 !handle->alive.load(std::memory_order_acquire))
171 return {};
172 ++handle->active_leases;
173 return Lease(handle);
174 }
175
176 private:
177 Handle state_;
178};
179
180} // namespace pulp::runtime
A temporary lease proving that an owner-backed callback may safely use its referenced state.
Definition alive_token.hpp:59
Lease & operator=(Lease &&other) noexcept
Definition alive_token.hpp:67
friend class AliveToken
Definition alive_token.hpp:89
~Lease()
Definition alive_token.hpp:75
void reset() noexcept
Release this callback's quiescence lease early.
Definition alive_token.hpp:84
std::shared_ptr< State > Handle
Definition alive_token.hpp:53
AliveToken(const AliveToken &)=delete
static bool is_alive(const Handle &handle) noexcept
Definition alive_token.hpp:143
void reset()
Definition alive_token.hpp:138
void retire() noexcept
Retire from the owner teardown context, outside any callback that holds a Lease.
Definition alive_token.hpp:127
AliveToken & operator=(AliveToken &&)=delete
static Lease try_acquire(const Handle &handle) noexcept
Atomically admit one callback before teardown can retire the owner.
Definition alive_token.hpp:165
AliveToken & operator=(const AliveToken &)=delete
~AliveToken()
Definition alive_token.hpp:110
AliveToken(AliveToken &&)=delete
static bool is_alive(const std::shared_ptr< std::atomic< bool > > &handle) noexcept
Definition alive_token.hpp:151
AliveToken()
Definition alive_token.hpp:109
Handle capture() const noexcept
Definition alive_token.hpp:119
Definition generated_event_source.hpp:33
@ other
Definition udev_monitor.hpp:26
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
STL namespace.