5#include <core/async/StopToken.hpp>
12namespace morph::async {
20struct CallbackScopeState {
21 std::atomic<bool> stopped{
false};
22 ::core::async::StopSource stop;
33enum class CallbackStatus : std::uint8_t {
69 [[nodiscard]] CallbackStatus
status() const noexcept {
70 auto const state = _state.lock();
71 if (state ==
nullptr) {
72 return CallbackStatus::Expired;
74 return state->stopped.load(std::memory_order_acquire) ? CallbackStatus::Stopped : CallbackStatus::Active;
82 [[nodiscard]]
bool active() const noexcept {
return status() == CallbackStatus::Active; }
89 [[nodiscard]]
bool expired() const noexcept {
return _state.expired(); }
100 [[nodiscard]] ::core::async::StopToken
stopToken() const noexcept {
101 auto const state = _state.lock();
102 return state ==
nullptr ? ::core::async::StopToken{} : state->stop.get_token();
125 template <
typename F>
126 [[nodiscard]]
auto guard(F&& fn)
const {
127 return [token = *
this, fn = std::forward<F>(fn)](
auto&&... args)
mutable ->
void {
128 static_assert(std::is_void_v<std::invoke_result_t<F&,
decltype(args)...>>,
129 "CallbackToken::guard() only wraps void-returning callables: there is no correct value to "
130 "return when delivery is suppressed.");
131 if (token.status() != CallbackStatus::Active) {
134 std::invoke(fn, std::forward<
decltype(args)>(args)...);
141 explicit CallbackToken(std::weak_ptr<detail::CallbackScopeState> state) noexcept : _state{std::move(state)} {}
143 std::weak_ptr<detail::CallbackScopeState> _state;
264 _state->stopped.store(
true, std::memory_order_release);
265 static_cast<void>(_state->stop.request_stop());
276 auto fresh = std::make_shared<detail::CallbackScopeState>();
278 _state = std::move(fresh);
283 [[nodiscard]]
bool stopRequested() const noexcept {
return _state->stopped.load(std::memory_order_acquire); }
300 template <
typename F>
301 [[nodiscard]]
auto guard(F&& fn)
const {
314 std::shared_ptr<detail::CallbackScopeState> _state;
Owned lifetime-and-stop gate for asynchronous callbacks — a data member, not a base class.
auto guard(F &&fn) const
Wraps fn so it runs only while this scope is alive and un-stopped.
~CallbackScope()
Refuses every outstanding token, then releases the state.
CallbackToken token() const noexcept
Issues a weak token for the current generation.
CallbackScope()
Constructs a live, un-stopped scope.
bool stopRequested() const noexcept
Whether this generation has been stopped.
void requestStop() const noexcept
Marks this generation stopped: gated callbacks stop being delivered, and every call the scope owns is...
void reset()
Retires every token issued so far and starts a fresh, live generation.
Weak, copyable observer of a CallbackScope — the half a callback captures.
CallbackStatus status() const noexcept
Reports whether the issuing scope is alive, stopped, or gone.
bool expired() const noexcept
Whether the issuing scope generation is gone (destroyed or reset()).
auto guard(F &&fn) const
Wraps fn so it runs only while this token is Active.
::core::async::StopToken stopToken() const noexcept
The stop token of the issuing scope's generation: stop is requested on it when the scope is stopped,...
bool active() const noexcept
Whether a callback gated on this token may run right now.
CallbackToken()=default
Constructs an unbound token, permanently CallbackStatus::Expired.