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 pub(crate) 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 unless_draining<T>(
113 &self,
114 operation: impl std::future::Future<Output = T>,
115 ) -> Option<T> {
116 tokio::select! {
117 biased;
118 () = self.draining() => None,
119 value = operation => Some(value),
120 }
121 }
122
123 pub async fn drain(&self) {
125 self.start_drain();
126 let mut changes = self.changes.subscribe();
127 loop {
128 changes.borrow_and_update();
129 let active = self.state.lock().unwrap_or_else(|e| e.into_inner()).active;
130 if active == 0 {
131 return;
132 }
133 changes.changed().await.expect("self keeps sender alive");
134 }
135 }
136
137 pub async fn drain_until(&self, deadline: Instant) -> DrainOutcome {
142 if tokio::time::timeout_at(deadline, self.drain())
143 .await
144 .is_ok()
145 {
146 return DrainOutcome::Complete;
147 }
148 let active = self.state.lock().unwrap_or_else(|e| e.into_inner()).active;
149 if active == 0 {
150 DrainOutcome::Complete
151 } else {
152 self.record(crate::observation::Event::DrainTimedOut);
153 DrainOutcome::TimedOut { active }
154 }
155 }
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq)]
160#[non_exhaustive]
161pub struct LifecycleSnapshot {
162 pub active: usize,
164 pub draining: bool,
166 pub draining_for: Option<std::time::Duration>,
168}
169
170#[derive(Debug, PartialEq, Eq)]
172#[non_exhaustive]
173pub enum DrainOutcome {
174 Complete,
176 TimedOut {
178 active: usize,
180 },
181}
182
183#[must_use = "keep the guard alive until the operation and its reply finish"]
185pub struct RequestGuard(Arc<Lifecycle>);
186
187impl Drop for RequestGuard {
188 fn drop(&mut self) {
189 let mut state = self.0.state.lock().unwrap_or_else(|e| e.into_inner());
190 state.active -= 1;
191 if state.draining && state.active == 0 {
192 self.0.record(crate::observation::Event::DrainCompleted);
193 self.0.changes.send_replace(());
194 }
195 }
196}