liminal_server/server/connection/
supervisor_lifecycle.rs1use std::sync::{Arc, Mutex};
2use std::thread::JoinHandle;
3
4use super::super::reclaim_placement::ReclaimRelease;
5use super::{ConnectionIncarnationAuthority, ConnectionSupervisor, SupervisorInner};
6use crate::ServerError;
7
8#[derive(Debug, Default)]
9pub(super) struct BackgroundStart {
10 started: bool,
11 stopped: bool,
12 worker: Option<JoinHandle<()>>,
13 incarnation: Option<JoinHandle<()>>,
15 failure: Option<String>,
16 reclaim_releases: Vec<ReclaimRelease>,
17}
18
19impl ConnectionSupervisor {
20 pub(crate) fn retain_reclaim_release(
21 &self,
22 release: ReclaimRelease,
23 ) -> Result<(), ServerError> {
24 let mut start = self
25 .inner
26 .background
27 .lock()
28 .map_err(|_| refusal("connection background start lock poisoned"))?;
29 if start.stopped {
30 return Err(refusal("spine release cannot be registered after shutdown"));
31 }
32 start.reclaim_releases.push(release);
33 drop(start);
34 Ok(())
35 }
36
37 pub(crate) fn listening(&self) -> Result<(), ServerError> {
38 let mut start = self
39 .inner
40 .background
41 .lock()
42 .map_err(|_| refusal("connection background start lock poisoned"))?;
43 if start.stopped {
44 return Err(refusal(
45 "connection background work cannot start after shutdown",
46 ));
47 }
48 if !start.started {
49 start.worker = self.inner.runtime.services_arc().listening();
50 start.incarnation = self.start_incarnation()?;
51 start.started = true;
52 }
53 drop(start);
54 Ok(())
55 }
56
57 fn start_incarnation(&self) -> Result<Option<JoinHandle<()>>, ServerError> {
67 let Some(authority) = self.inner.incarnations.clone() else {
68 return Ok(None);
69 };
70 let fatal = self.inner.runtime.fatal_shutdown.clone();
71 let held = Arc::clone(&self.inner.held);
72 let supervisor = Arc::downgrade(&self.inner);
73 held.starting();
74 std::thread::Builder::new()
75 .name("liminal-incarnation-start".to_owned())
76 .spawn(move || {
77 let started = authority.ensure_started();
78 let admissions = held.started();
79 if !admissions.is_empty()
80 && let Some(inner) = supervisor.upgrade()
81 {
82 let supervisor = Self { inner };
83 for admit in admissions {
84 admit(&supervisor);
85 }
86 }
87 let settled = started.and_then(|()| authority.settle_anchor());
91 if let Err(error) = settled
92 && !ConnectionIncarnationAuthority::is_abandoned(&error)
93 {
94 tracing::error!(%error, "the incarnation start or its anchor swap failed behind the bound listener; the server stops");
95 if let Some(fatal) = fatal {
96 fatal.initiate();
97 }
98 }
99 })
100 .map(Some)
101 .map_err(|error| {
102 self.inner.held.stop();
103 refusal(format!("the incarnation start could not begin: {error}"))
104 })
105 }
106
107 pub(in crate::server) fn admit_when_started(&self, admit: impl FnOnce(&Self) + Send + 'static) {
112 if let Some(admit) = self.inner.held.hold(Box::new(admit)) {
113 admit(self);
114 }
115 }
116
117 #[must_use]
120 pub fn incarnation_start_failure(&self) -> Option<ServerError> {
121 self.inner
122 .incarnations
123 .as_ref()
124 .and_then(|authority| authority.start_failure())
125 }
126
127 pub(in crate::server) fn finish_store_work(&self) -> Result<(), ServerError> {
128 self.inner.finish_store_work()
129 }
130
131 pub(in crate::server) fn release_store_holders(&self) -> Result<(), ServerError> {
132 self.inner.runtime.services().release_store_holders()
133 }
134}
135
136impl SupervisorInner {
137 fn finish_store_work(&self) -> Result<(), ServerError> {
138 #[cfg(test)]
139 let observer = self
140 .reclaim_stop_observer
141 .lock()
142 .map_err(|_| refusal("reclamation stop observer lock poisoned"))?
143 .take();
144 #[cfg(test)]
145 if let Some(observer) = observer {
146 observer
147 .send(true)
148 .map_err(|error| refusal(error.to_string()))?;
149 }
150 self.held.stop();
151 if let Some(authority) = &self.incarnations {
152 authority.abandon();
153 }
154 first_failure([
155 self.reclaim.finish(&self.scheduler),
156 join_background(&self.background),
157 self.runtime.services().finish_background_store_work(),
158 ])
159 }
160}
161
162impl Drop for SupervisorInner {
163 fn drop(&mut self) {
164 crate::server::boot_steps::stop_timed("connection_drop_scheduler", || {
167 self.scheduler.shutdown();
168 });
169 let finished =
170 crate::server::boot_steps::stop_timed("connection_drop_store_finish", || {
171 self.finish_store_work()
172 });
173 if let Err(error) = finished {
174 tracing::error!(%error, "connection supervisor store teardown failed");
175 }
176 }
177}
178
179fn join_background(background: &Mutex<BackgroundStart>) -> Result<(), ServerError> {
180 let mut start = background
181 .lock()
182 .map_err(|_| refusal("connection background join lock poisoned"))?;
183 start.stopped = true;
184 if let Some(failure) = &start.failure {
185 return Err(refusal(failure.clone()));
186 }
187 let joined = start.worker.take().map_or(Ok(()), |worker| {
188 worker
189 .join()
190 .map_err(|_| refusal("connection background store work panicked during shutdown"))
191 });
192 let started = start.incarnation.take().map_or(Ok(()), |worker| {
193 if worker.thread().id() == std::thread::current().id() {
197 return Ok(());
198 }
199 crate::server::boot_steps::stop_timed("incarnation_join", || worker.join())
200 .map_err(|_| refusal("the incarnation start panicked during shutdown"))
201 });
202 let mut results = vec![joined, started];
203 for release in &mut start.reclaim_releases {
204 results.push(release.finish());
205 }
206 start.reclaim_releases.clear();
207 let mut first = None;
208 for result in results {
209 if let Err(error) = result {
210 tracing::error!(%error, "background store teardown failed");
211 if first.is_none() {
212 first = Some(error);
213 }
214 }
215 }
216 if let Some(error) = first {
217 start.failure = Some(error.to_string());
218 return Err(error);
219 }
220 drop(start);
221 Ok(())
222}
223
224fn first_failure<const N: usize>(results: [Result<(), ServerError>; N]) -> Result<(), ServerError> {
225 let mut first = None;
226 for result in results {
227 if let Err(error) = result {
228 tracing::error!(%error, "connection store work teardown failed");
229 if first.is_none() {
230 first = Some(error);
231 }
232 }
233 }
234 first.map_or(Ok(()), Err)
235}
236
237fn refusal(message: impl Into<String>) -> ServerError {
238 ServerError::ListenerAccept {
239 message: message.into(),
240 }
241}