kunobi_daemon/
lifecycle.rs1use std::sync::{Arc, Mutex};
2use tokio::sync::watch;
3use tokio::time::Instant;
4
5#[derive(Clone, Copy, Default)]
6struct State {
7 draining: bool,
8 active: usize,
9 drain_started: Option<Instant>,
10}
11
12pub struct Lifecycle {
18 state: Mutex<State>,
19 changes: watch::Sender<()>,
20 observations: Option<Arc<crate::observation::Observations>>,
21}
22
23impl Default for Lifecycle {
24 fn default() -> Self {
25 Self {
26 state: Mutex::new(State::default()),
27 changes: watch::channel(()).0,
28 observations: None,
29 }
30 }
31}
32
33impl Lifecycle {
34 pub fn observed(observations: Arc<crate::observation::Observations>) -> Self {
36 Self {
37 observations: Some(observations),
38 ..Self::default()
39 }
40 }
41
42 pub fn snapshot(&self) -> LifecycleSnapshot {
44 let state = self.state.lock().unwrap_or_else(|e| e.into_inner());
45 LifecycleSnapshot {
46 active: state.active,
47 draining: state.draining,
48 draining_for: state.drain_started.map(|started| started.elapsed()),
49 }
50 }
51
52 fn record(&self, event: crate::observation::Event) {
53 if let Some(observations) = &self.observations {
54 observations.record(event);
55 }
56 }
57 pub fn begin(self: &Arc<Self>) -> Option<RequestGuard> {
59 let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
60 if state.draining {
61 return None;
62 }
63 state.active += 1;
64 Some(RequestGuard(Arc::clone(self)))
65 }
66
67 pub fn start_drain(&self) -> bool {
69 let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
70 if state.draining {
71 return false;
72 }
73 state.draining = true;
74 state.drain_started = Some(Instant::now());
75 self.record(crate::observation::Event::DrainStarted);
76 if state.active == 0 {
77 self.record(crate::observation::Event::DrainCompleted);
78 }
79 self.changes.send_replace(());
80 true
81 }
82
83 pub fn accepting_calls(&self) -> bool {
85 !self
86 .state
87 .lock()
88 .unwrap_or_else(|e| e.into_inner())
89 .draining
90 }
91
92 pub async fn draining(&self) {
94 let mut changes = self.changes.subscribe();
95 loop {
96 changes.borrow_and_update();
97 if self
98 .state
99 .lock()
100 .unwrap_or_else(|e| e.into_inner())
101 .draining
102 {
103 return;
104 }
105 changes.changed().await.expect("self keeps sender alive");
106 }
107 }
108
109 pub async fn drain(&self) {
111 self.start_drain();
112 let mut changes = self.changes.subscribe();
113 loop {
114 changes.borrow_and_update();
115 let active = self.state.lock().unwrap_or_else(|e| e.into_inner()).active;
116 if active == 0 {
117 return;
118 }
119 changes.changed().await.expect("self keeps sender alive");
120 }
121 }
122
123 pub async fn drain_until(&self, deadline: Instant) -> DrainOutcome {
128 if tokio::time::timeout_at(deadline, self.drain())
129 .await
130 .is_ok()
131 {
132 return DrainOutcome::Complete;
133 }
134 let active = self.state.lock().unwrap_or_else(|e| e.into_inner()).active;
135 if active == 0 {
136 DrainOutcome::Complete
137 } else {
138 self.record(crate::observation::Event::DrainTimedOut);
139 DrainOutcome::TimedOut { active }
140 }
141 }
142}
143
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct LifecycleSnapshot {
147 pub active: usize,
149 pub draining: bool,
151 pub draining_for: Option<std::time::Duration>,
153}
154
155#[derive(Debug, PartialEq, Eq)]
157pub enum DrainOutcome {
158 Complete,
160 TimedOut {
162 active: usize,
164 },
165}
166
167#[must_use = "keep the guard alive until the operation and its reply finish"]
169pub struct RequestGuard(Arc<Lifecycle>);
170
171impl Drop for RequestGuard {
172 fn drop(&mut self) {
173 let mut state = self.0.state.lock().unwrap_or_else(|e| e.into_inner());
174 state.active -= 1;
175 if state.draining && state.active == 0 {
176 self.0.record(crate::observation::Event::DrainCompleted);
177 self.0.changes.send_replace(());
178 }
179 }
180}