//! Handles spawning and restarting service task(s) use super::*; use tokio::sync::RwLock; use std::mem::MaybeUninit; use std::ops; use futures::future::Future; const SUPERVISOR_BACKLOG: usize = 32; mod dispatch; pub use dispatch::*; /// Signal the shutdown method to the supervisor. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] pub enum SupervisorControl { /// Normal working Initialise, /// Signal the subtask(s) to shut down, then wait for them and exit. Signal, /// Drop all handles and pipes to subtask(s) then immediately exit. Drop, /// Restart any and all subtask(s) Restart, /// Set the max task limit. Default is 0. TaskLimit(usize), } impl Default for SupervisorControl { #[inline] fn default() -> Self { Self::Initialise } } /// Supervisor responsible for spawning the state handler service. #[derive(Debug)] pub(super) struct Supervisor { /// Handle for the supervisor task itself handle: JoinHandle, /// Watch sender for signalling shutdowns the supervisor task itself shutdown: watch::Sender, /// The pipe to send requests to the supervisor's subtasks pipe: mpsc::Sender, /// The initial receiver created from `broadcast_root`. broadcast_receiver: broadcast::Receiver, /// Data shared between the supervisor's task and its controller instance here. shared: Arc, } /// Object shared btweeen the Supervisor control instance and its supervisor task. #[derive(Debug)] struct SupervisorShared { //// scratch that, idk what this was supposed to be for. I'm sure i'll remember if it's important. //sub: RwLock>, broadcast_root: broadcast::Sender, state: state::State, } /// The state held by the running superviser service #[derive(Debug)] struct SupervisorTaskState { shutdown: watch::Receiver, recv: mpsc::Receiver, shared: Arc, } impl Supervisor { /// Attempt to send a control signal to the supervisor itself pub fn signal_control(&self, sig: SupervisorControl) -> Result<(), watch::error::SendError> { self.shutdown.broadcast(sig)?; Ok(()) } /// Drop all communications with background worker and wait for it to complete pub async fn join_now(self) -> eyre::Result<()> { let handle = { let Self { handle, ..} = self; // drop everything else handle }; handle.await?; Ok(()) } /// Check if the background worker has not been dropped. /// /// If this returns false it usually indicates a fatal error. pub fn is_alive(&self) -> bool { Arc::strong_count(&self.shared) > 1 } /// Create a new supervisor for this state. pub fn new(state: state::State) -> Self { let shutdown = watch::channel(Default::default()); let pipe = mpsc::channel(SUPERVISOR_BACKLOG); let (broadcast_root, broadcast_receiver) = broadcast::channel(SUPERVISOR_BACKLOG); let shared = Arc::new(SupervisorShared{ broadcast_root, state, }); let (shutdown_0, shutdown_1) = shutdown; let (pipe_0, pipe_1) = pipe; Self { shutdown: shutdown_0, pipe: pipe_0, broadcast_receiver, shared: Arc::clone(&shared), handle: tokio::spawn(async move { let shared = shared; ExitStatus::from(service_fn(SupervisorTaskState { shared, recv: pipe_1, shutdown: shutdown_1, }).await) }), } } } async fn service_fn(mut state: SupervisorTaskState) -> eyre::Result<()> { while let Some(req) = state.recv.recv().await { } Ok(()) }