feat(supervisor): ChildHandle trait + MockChild

This commit is contained in:
2026-05-25 11:33:23 +02:00
parent bd926061bf
commit 1d2848f03a
2 changed files with 96 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
use std::sync::Arc;
use tokio::sync::{Mutex, oneshot};
#[async_trait::async_trait]
pub trait ChildHandle: Send + 'static {
fn pid(&self) -> u32;
async fn wait(&mut self) -> std::io::Result<Option<i32>>;
fn terminate(&mut self) -> std::io::Result<()>;
fn kill(&mut self) -> std::io::Result<()>;
}
pub struct MockChild {
pid: u32,
exit_rx: Arc<Mutex<oneshot::Receiver<Option<i32>>>>,
terminate_tx: Option<oneshot::Sender<()>>,
kill_tx: Option<oneshot::Sender<()>>,
}
pub struct MockChildController {
pub exit_tx: Option<oneshot::Sender<Option<i32>>>,
pub terminate_rx: oneshot::Receiver<()>,
pub kill_rx: oneshot::Receiver<()>,
}
impl MockChild {
pub fn new(pid: u32) -> (Self, MockChildController) {
let (exit_tx, exit_rx) = oneshot::channel();
let (terminate_tx, terminate_rx) = oneshot::channel();
let (kill_tx, kill_rx) = oneshot::channel();
let child = Self {
pid,
exit_rx: Arc::new(Mutex::new(exit_rx)),
terminate_tx: Some(terminate_tx),
kill_tx: Some(kill_tx),
};
let ctl = MockChildController {
exit_tx: Some(exit_tx),
terminate_rx,
kill_rx,
};
(child, ctl)
}
}
#[async_trait::async_trait]
impl ChildHandle for MockChild {
fn pid(&self) -> u32 {
self.pid
}
async fn wait(&mut self) -> std::io::Result<Option<i32>> {
let mut rx = self.exit_rx.lock().await;
match (&mut *rx).await {
Ok(code) => Ok(code),
Err(_) => Err(std::io::Error::other("exit_tx dropped")),
}
}
fn terminate(&mut self) -> std::io::Result<()> {
if let Some(tx) = self.terminate_tx.take() {
let _ = tx.send(());
}
Ok(())
}
fn kill(&mut self) -> std::io::Result<()> {
if let Some(tx) = self.kill_tx.take() {
let _ = tx.send(());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn mock_child_exit() {
let (mut child, mut ctl) = MockChild::new(123);
assert_eq!(child.pid(), 123);
ctl.exit_tx.take().unwrap().send(Some(0)).unwrap();
assert_eq!(child.wait().await.unwrap(), Some(0));
}
#[tokio::test]
async fn mock_child_terminate() {
let (mut child, mut ctl) = MockChild::new(1);
child.terminate().unwrap();
ctl.terminate_rx.try_recv().unwrap();
}
}
+4
View File
@@ -1 +1,5 @@
//! Process-supervision primitives for the xy daemon.
pub mod child;
pub use child::{ChildHandle, MockChild, MockChildController};