90 lines
3.1 KiB
Rust
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;
|
|
}
|
|
}
|