feat(supervisor): ChildHandle trait + MockChild
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,5 @@
|
|||||||
//! Process-supervision primitives for the xy daemon.
|
//! Process-supervision primitives for the xy daemon.
|
||||||
|
|
||||||
|
pub mod child;
|
||||||
|
|
||||||
|
pub use child::{ChildHandle, MockChild, MockChildController};
|
||||||
|
|||||||
Reference in New Issue
Block a user