Skip to main content

guinea_core/scope/
lifetime.rs

1use std::rc::Rc;
2
3use tokio::task::JoinHandle;
4
5use crate::actor::Addr;
6use crate::actor::event_bus::subscribe::BusSubscription;
7
8use super::{Scope, ScopeData};
9
10/// Whether a scope is awake, for something it owns to ask on its own.
11///
12/// Held rather than the scope itself, so that a timer the scope owns can ask
13/// from off the scope's own code paths.
14#[derive(Clone)]
15pub struct Awake(Rc<std::cell::Cell<bool>>);
16
17impl Awake {
18    /// Whether the scope is awake now.
19    pub fn now(&self) -> bool {
20        !self.0.get()
21    }
22}
23
24impl Scope {
25    pub fn own_subscription(&self, subscription: BusSubscription) {
26        self.own(DropGuard(subscription));
27    }
28
29    /// Binds any [`Teardown`] resource to this scope's lifetime: it is torn
30    /// down when the scope is - at once, if the scope is already gone.
31    pub fn own<R: Teardown>(&self, resource: R) {
32        match self.data() {
33            Some(data) => data
34                .teardowns
35                .borrow_mut()
36                .push(Box::new(move || resource.teardown())),
37            None => resource.teardown(),
38        }
39    }
40
41    /// Asked before this scope is torn down by a navigation.
42    ///
43    /// Registered during `install`, which is the whole reason leaving and
44    /// entering are declared in different places: on the way out the scope
45    /// exists, so the guard can read its own state - which is what "unsaved
46    /// changes" is. On the way in there is nothing to read yet.
47    pub fn on_leave(&self, guard: impl Fn() -> crate::guard::Verdict + 'static) {
48        let data = self.installing("guarding a leave");
49        data.leave_guards.borrow_mut().push(Rc::new(guard));
50    }
51
52    /// The guards to ask, in the order they were registered.
53    ///
54    /// Cloned out rather than borrowed: a guard is free to touch this scope,
55    /// and the caller runs them while deciding.
56    pub fn leave_guards(&self) -> Vec<Rc<dyn Fn() -> crate::guard::Verdict>> {
57        self.data()
58            .map(|data| data.leave_guards.borrow().clone())
59            .unwrap_or_default()
60    }
61
62    /// Puts this scope to sleep: kept, with its state and actors, but deaf.
63    ///
64    /// What it owns stops hearing anything while it sleeps - its timers skip
65    /// their ticks, its actors and callbacks are not told what the buses
66    /// carry - and nothing is queued for later: what happened meanwhile is
67    /// missed. A router does this to a `keep` segment it leaves.
68    pub fn sleep(&self) {
69        if let Some(data) = self.data() {
70            data.asleep.set(true);
71        }
72    }
73
74    /// Wakes this scope, then runs what asked to hear of it, in the order it
75    /// asked.
76    pub fn wake(&self) {
77        let Some(data) = self.data() else { return };
78        data.asleep.set(false);
79
80        let hooks = data.wake_hooks.borrow().clone();
81        drop(data);
82        for hook in hooks {
83            hook();
84        }
85    }
86
87    /// Whether this scope is there and awake. A removed one is neither.
88    pub fn is_awake(&self) -> bool {
89        self.data().is_some_and(|data| !data.asleep.get())
90    }
91
92    /// Whether this scope is awake, for what it owns to ask later.
93    pub fn awake(&self) -> Awake {
94        match self.data() {
95            Some(data) => Awake(data.asleep.clone()),
96            None => Awake(Rc::new(std::cell::Cell::new(true))),
97        }
98    }
99
100    /// Runs `hook` every time this scope wakes: for a feature to catch up on
101    /// what it missed while it slept.
102    pub fn on_wake(&self, hook: impl Fn() + 'static) {
103        let data = self.installing("waiting for a wake");
104        data.wake_hooks.borrow_mut().push(Rc::new(hook));
105    }
106}
107
108impl ScopeData {
109    /// Lets go of what this scope took on, the last first: what came later
110    /// may rest on what came before it.
111    pub(super) fn tear_down(&self) {
112        let teardowns = std::mem::take(&mut *self.teardowns.borrow_mut());
113        for teardown in teardowns.into_iter().rev() {
114            teardown();
115        }
116    }
117}
118
119/// A resource whose lifetime can be bound to a [`Scope`] via [`Scope::own`].
120/// Implement per resource kind; the "blanket" over arbitrary `T` is the
121/// [`DropGuard`] newtype, so specialized teardowns never overlap.
122pub trait Teardown: 'static {
123    fn teardown(self);
124}
125
126impl Teardown for JoinHandle<()> {
127    fn teardown(self) {
128        self.abort();
129    }
130}
131
132impl<A: 'static> Teardown for Addr<A> {
133    fn teardown(self) {
134        self.dispose();
135        drop(self);
136    }
137}
138
139/// Blanket teardown for resources that just need dropping
140/// (`scope.own(DropGuard(resource))` when no specialized impl exists).
141pub struct DropGuard<T: 'static>(pub T);
142
143impl<T: 'static> Teardown for DropGuard<T> {
144    fn teardown(self) {
145        drop(self.0);
146    }
147}