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, strong only inside one admission, which a stop
66    /// waits for: it never keeps a stopped server alive.
67    fn start_incarnation(&self) -> Result<Option<JoinHandle<()>>, ServerError> {
68        let Some(authority) = self.inner.incarnations.clone() else {
69            return Ok(None);
70        };
71        let fatal = self.inner.runtime.fatal_shutdown.clone();
72        let held = Arc::clone(&self.inner.held);
73        let supervisor = Arc::downgrade(&self.inner);
74        held.starting();
75        std::thread::Builder::new()
76            .name("liminal-incarnation-start".to_owned())
77            .spawn(move || {
78                let started = authority.ensure_started();
79                if held.admitting() {
80                    // One admission at a time, each under a strong supervisor
81                    // that is answered back and dropped after the admission's
82                    // lock: a stop waits for the running one and runs none
83                    // after it, so its connection snapshot sees every
84                    // connection spawned here (Flapjack's post 108).
85                    held.admit_held(|admit| {
86                        let supervisor = supervisor.upgrade().map(|inner| Self { inner });
87                        if let Some(supervisor) = &supervisor {
88                            admit(supervisor);
89                        }
90                        supervisor
91                    });
92                }
93                // SRV-048 R2: the anchor swap is settled here, after the
94                // held connections are admitted; a refused swap stops the
95                // server as a failed start does.
96                let settled = started.and_then(|()| authority.settle_anchor());
97                if let Err(error) = settled
98                    && !ConnectionIncarnationAuthority::is_abandoned(&error)
99                {
100                    tracing::error!(%error, "the incarnation start or its anchor swap failed behind the bound listener; the server stops");
101                    if let Some(fatal) = fatal {
102                        fatal.initiate();
103                    }
104                }
105            })
106            .map(Some)
107            .map_err(|error| {
108                self.inner.held.stop();
109                refusal(format!("the incarnation start could not begin: {error}"))
110            })
111    }
112
113    /// Runs `admit` for a connection just accepted: at once, or, while the
114    /// incarnation start runs, on the start thread once it has published, so
115    /// the accept thread returns to accepting and a stop never waits on the
116    /// start (SRV-042 R1). After shutdown it is dropped, closing its socket.
117    pub(in crate::server) fn admit_when_started(&self, admit: impl FnOnce(&Self) + Send + 'static) {
118        if let Some(admit) = self.inner.held.hold(Box::new(admit)) {
119            admit(self);
120        }
121    }
122
123    /// The incarnation start's typed error once it has failed, which a server
124    /// stopped by that failure returns; nothing while it has not.
125    #[must_use]
126    pub fn incarnation_start_failure(&self) -> Option<ServerError> {
127        self.inner
128            .incarnations
129            .as_ref()
130            .and_then(|authority| authority.start_failure())
131    }
132
133    pub(in crate::server) fn finish_store_work(&self) -> Result<(), ServerError> {
134        self.inner.finish_store_work()
135    }
136
137    pub(in crate::server) fn release_store_holders(&self) -> Result<(), ServerError> {
138        self.inner.runtime.services().release_store_holders()
139    }
140}
141
142impl SupervisorInner {
143    fn finish_store_work(&self) -> Result<(), ServerError> {
144        #[cfg(test)]
145        let observer = self
146            .reclaim_stop_observer
147            .lock()
148            .map_err(|_| refusal("reclamation stop observer lock poisoned"))?
149            .take();
150        #[cfg(test)]
151        if let Some(observer) = observer {
152            observer
153                .send(true)
154                .map_err(|error| refusal(error.to_string()))?;
155        }
156        self.held.stop();
157        if let Some(authority) = &self.incarnations {
158            authority.abandon();
159        }
160        first_failure([
161            self.reclaim.finish(&self.scheduler),
162            join_background(&self.background),
163            self.runtime.services().finish_background_store_work(),
164        ])
165    }
166}
167
168impl Drop for SupervisorInner {
169    fn drop(&mut self) {
170        // A door that drops the supervisor reads these two lines for where
171        // its stop went (Waffles, 25 Oct: 578 ms with no line).
172        crate::server::boot_steps::stop_timed("connection_drop_scheduler", || {
173            self.scheduler.shutdown();
174        });
175        let finished =
176            crate::server::boot_steps::stop_timed("connection_drop_store_finish", || {
177                self.finish_store_work()
178            });
179        if let Err(error) = finished {
180            tracing::error!(%error, "connection supervisor store teardown failed");
181        }
182    }
183}
184
185fn join_background(background: &Mutex<BackgroundStart>) -> Result<(), ServerError> {
186    let mut start = background
187        .lock()
188        .map_err(|_| refusal("connection background join lock poisoned"))?;
189    start.stopped = true;
190    if let Some(failure) = &start.failure {
191        return Err(refusal(failure.clone()));
192    }
193    let joined = start.worker.take().map_or(Ok(()), |worker| {
194        worker
195            .join()
196            .map_err(|_| refusal("connection background store work panicked during shutdown"))
197    });
198    let started = start.incarnation.take().map_or(Ok(()), |worker| {
199        // The start thread can hold the last owner while it admits what it
200        // held; dropping it there ends here, on the thread itself, which is
201        // finishing: it is not joined from itself.
202        if worker.thread().id() == std::thread::current().id() {
203            return Ok(());
204        }
205        crate::server::boot_steps::stop_timed("incarnation_join", || worker.join())
206            .map_err(|_| refusal("the incarnation start panicked during shutdown"))
207    });
208    let mut results = vec![joined, started];
209    for release in &mut start.reclaim_releases {
210        results.push(release.finish());
211    }
212    start.reclaim_releases.clear();
213    let mut first = None;
214    for result in results {
215        if let Err(error) = result {
216            tracing::error!(%error, "background store teardown failed");
217            if first.is_none() {
218                first = Some(error);
219            }
220        }
221    }
222    if let Some(error) = first {
223        start.failure = Some(error.to_string());
224        return Err(error);
225    }
226    drop(start);
227    Ok(())
228}
229
230fn first_failure<const N: usize>(results: [Result<(), ServerError>; N]) -> Result<(), ServerError> {
231    let mut first = None;
232    for result in results {
233        if let Err(error) = result {
234            tracing::error!(%error, "connection store work teardown failed");
235            if first.is_none() {
236                first = Some(error);
237            }
238        }
239    }
240    first.map_or(Ok(()), Err)
241}
242
243fn refusal(message: impl Into<String>) -> ServerError {
244    ServerError::ListenerAccept {
245        message: message.into(),
246    }
247}