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 if let Err(error) = started
88 && !ConnectionIncarnationAuthority::is_abandoned(&error)
89 {
90 tracing::error!(%error, "the incarnation start failed behind the bound listener; the server stops");
91 if let Some(fatal) = fatal {
92 fatal.initiate();
93 }
94 }
95 })
96 .map(Some)
97 .map_err(|error| {
98 self.inner.held.stop();
99 refusal(format!("the incarnation start could not begin: {error}"))
100 })
101 }
102
103 pub(in crate::server) fn admit_when_started(&self, admit: impl FnOnce(&Self) + Send + 'static) {
108 if let Some(admit) = self.inner.held.hold(Box::new(admit)) {
109 admit(self);
110 }
111 }
112
113 #[must_use]
116 pub fn incarnation_start_failure(&self) -> Option<ServerError> {
117 self.inner
118 .incarnations
119 .as_ref()
120 .and_then(|authority| authority.start_failure())
121 }
122
123 pub(in crate::server) fn finish_store_work(&self) -> Result<(), ServerError> {
124 self.inner.finish_store_work()
125 }
126
127 pub(in crate::server) fn release_store_holders(&self) -> Result<(), ServerError> {
128 self.inner.runtime.services().release_store_holders()
129 }
130}
131
132impl SupervisorInner {
133 fn finish_store_work(&self) -> Result<(), ServerError> {
134 #[cfg(test)]
135 let observer = self
136 .reclaim_stop_observer
137 .lock()
138 .map_err(|_| refusal("reclamation stop observer lock poisoned"))?
139 .take();
140 #[cfg(test)]
141 if let Some(observer) = observer {
142 observer
143 .send(true)
144 .map_err(|error| refusal(error.to_string()))?;
145 }
146 self.held.stop();
147 if let Some(authority) = &self.incarnations {
148 authority.abandon();
149 }
150 first_failure([
151 self.reclaim.finish(&self.scheduler),
152 join_background(&self.background),
153 self.runtime.services().finish_background_store_work(),
154 ])
155 }
156}
157
158impl Drop for SupervisorInner {
159 fn drop(&mut self) {
160 self.scheduler.shutdown();
161 if let Err(error) = self.finish_store_work() {
162 tracing::error!(%error, "connection supervisor store teardown failed");
163 }
164 }
165}
166
167fn join_background(background: &Mutex<BackgroundStart>) -> Result<(), ServerError> {
168 let mut start = background
169 .lock()
170 .map_err(|_| refusal("connection background join lock poisoned"))?;
171 start.stopped = true;
172 if let Some(failure) = &start.failure {
173 return Err(refusal(failure.clone()));
174 }
175 let joined = start.worker.take().map_or(Ok(()), |worker| {
176 worker
177 .join()
178 .map_err(|_| refusal("connection background store work panicked during shutdown"))
179 });
180 let started = start.incarnation.take().map_or(Ok(()), |worker| {
181 if worker.thread().id() == std::thread::current().id() {
185 return Ok(());
186 }
187 crate::server::boot_steps::stop_timed("incarnation_join", || worker.join())
188 .map_err(|_| refusal("the incarnation start panicked during shutdown"))
189 });
190 let mut results = vec![joined, started];
191 for release in &mut start.reclaim_releases {
192 results.push(release.finish());
193 }
194 start.reclaim_releases.clear();
195 let mut first = None;
196 for result in results {
197 if let Err(error) = result {
198 tracing::error!(%error, "background store teardown failed");
199 if first.is_none() {
200 first = Some(error);
201 }
202 }
203 }
204 if let Some(error) = first {
205 start.failure = Some(error.to_string());
206 return Err(error);
207 }
208 drop(start);
209 Ok(())
210}
211
212fn first_failure<const N: usize>(results: [Result<(), ServerError>; N]) -> Result<(), ServerError> {
213 let mut first = None;
214 for result in results {
215 if let Err(error) = result {
216 tracing::error!(%error, "connection store work teardown failed");
217 if first.is_none() {
218 first = Some(error);
219 }
220 }
221 }
222 first.map_or(Ok(()), Err)
223}
224
225fn refusal(message: impl Into<String>) -> ServerError {
226 ServerError::ListenerAccept {
227 message: message.into(),
228 }
229}