Files
sausage/host/src/process/services.rs
T

90 lines
3.1 KiB
Rust

//! Starts world init, reaps its command tree, and applies host restart and shutdown deadlines.
use super::{GroupGuard, Request, Runtime, Stdio};
use std::{sync::Arc, time::Duration};
pub struct Supervisor {
shutdown: tokio::sync::oneshot::Sender<()>,
task: tokio::task::JoinHandle<()>,
}
impl Supervisor {
pub async fn shutdown(self) {
let _ = self.shutdown.send(());
let _ = self.task.await;
}
}
/// The host knows only init. Unit policy belongs to the WASI program.
pub fn supervise(runtime: Arc<Runtime>) -> Supervisor {
let (shutdown, mut stop) = tokio::sync::oneshot::channel();
let task = tokio::spawn(async move {
let mut backoff = Duration::from_secs(1);
loop {
let start = runtime.start(
Request {
program: "/bin/init.wasm".into(),
argv: vec!["/bin/init.wasm".into()],
cwd: "/".into(),
env: Vec::new(),
group: None,
stdin: Stdio::Pipe,
stdout: Stdio::Null,
stderr: Stdio::Null,
},
0,
0,
0,
Vec::new(),
None,
None,
);
let child = tokio::select! { _ = &mut stop => return, child = start => child };
if let Ok(mut child) = child {
let guard = GroupGuard {
runtime: runtime.clone(),
group: child.pid,
};
let exiting = async {
while child.code.borrow_and_update().is_none() {
if child.code.changed().await.is_err() {
break;
}
}
};
let shutdown = tokio::select! { _ = &mut stop => true, _ = exiting => false };
if shutdown {
drop(child.input.take());
let _ = tokio::time::timeout(Duration::from_secs(15), async {
while child.code.borrow_and_update().is_none() {
if child.code.changed().await.is_err() {
break;
}
}
})
.await;
drop(guard);
reaped(&runtime, child.pid).await;
return;
}
// This guard kills all descendants, including separate process groups.
drop(guard);
reaped(&runtime, child.pid).await;
}
tokio::select! { _ = &mut stop => return, _ = tokio::time::sleep(backoff) => () }
backoff = (backoff * 2).min(Duration::from_secs(30));
}
});
Supervisor { shutdown, task }
}
async fn reaped(runtime: &Runtime, command: u32) {
loop {
if !runtime
.entries
.lock()
.unwrap()
.values()
.any(|entry| entry.command == command)
{
return;
}
tokio::task::yield_now().await;
}
}