Skip to main content

liminal_server/server/connection/
supervisor_lifecycle.rs

1use 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    /// The incarnation start behind the bind (SRV-041 R2).
14    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    /// Begins the incarnation start on its own thread. A start that fails
58    /// stops the server through the fatal shutdown; its typed error is
59    /// answered by [`Self::incarnation_start_failure`].
60    ///
61    /// SRV-042 R1: from here until the start publishes, an accepted
62    /// connection is held (see [`Self::admit_when_started`]); the start thread
63    /// runs what was held once it has, before a failed start stops the server,
64    /// so a held client reads the start's refusal. The thread holds the
65    /// supervisor weakly: it never keeps a stopped server alive.
66    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                // SRV-048 R2: the anchor swap is settled here, after the
88                // held connections are admitted; a refused swap stops the
89                // server as a failed start does.
90                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    /// Runs `admit` for a connection just accepted: at once, or, while the
108    /// incarnation start runs, on the start thread once it has published, so
109    /// the accept thread returns to accepting and a stop never waits on the
110    /// start (SRV-042 R1). After shutdown it is dropped, closing its socket.
111    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    /// The incarnation start's typed error once it has failed, which a server
118    /// stopped by that failure returns; nothing while it has not.
119    #[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        // A door that drops the supervisor reads these two lines for where
165        // its stop went (Waffles, 25 Oct: 578 ms with no line).
166        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        // The start thread can hold the last owner while it admits what it
194        // held; dropping it there ends here, on the thread itself, which is
195        // finishing: it is not joined from itself.
196        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}