use std::{collections::VecDeque, future::Future, pin::Pin, sync::Arc};
use tokio::sync::oneshot;
use tokio_util::sync::CancellationToken;
use crate::handle::SupervisorHandleError;
pub type TaskError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub type TaskResult = Result<(), TaskError>;
pub trait SupervisedTask: Clone + Send + 'static {
fn run(self, cancel: CancellationToken) -> impl Future<Output = TaskResult> + Send;
}
pub(crate) trait DynTask: Send + 'static {
fn run(
self: Box<Self>,
cancel: CancellationToken,
) -> Pin<Box<dyn Future<Output = TaskResult> + Send>>;
fn clone_box(&self) -> Box<dyn DynTask>;
}
impl<T: SupervisedTask> DynTask for T {
fn run(
self: Box<Self>,
cancel: CancellationToken,
) -> Pin<Box<dyn Future<Output = TaskResult> + Send>> {
Box::pin(SupervisedTask::run(*self, cancel))
}
fn clone_box(&self) -> Box<dyn DynTask> {
Box::new(self.clone())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TaskStatus {
Running,
Restarting,
Stopping,
Completed,
Dead,
}
impl std::fmt::Display for TaskStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Running => "running",
Self::Restarting => "restarting",
Self::Stopping => "stopping",
Self::Completed => "completed",
Self::Dead => "dead",
})
}
}
pub(crate) type Reply<T> = oneshot::Sender<Result<T, SupervisorHandleError>>;
pub(crate) struct Slot {
pub(crate) name: Arc<str>,
pub(crate) status: TaskStatus,
pub(crate) task: Box<dyn DynTask>,
pub(crate) cancel: Option<CancellationToken>,
pub(crate) generation: u64,
pub(crate) restarts: VecDeque<tokio::time::Instant>,
pub(crate) restart_after_stop: bool,
pub(crate) stop_waiters: Vec<Reply<()>>,
}
impl Slot {
pub(crate) fn new(name: Arc<str>, task: Box<dyn DynTask>) -> Self {
Self {
name,
status: TaskStatus::Dead,
task,
cancel: None,
generation: 0,
restarts: VecDeque::new(),
restart_after_stop: false,
stop_waiters: Vec::new(),
}
}
pub(crate) fn request_stop(&mut self, restart_after: bool) {
if let Some(cancel) = &self.cancel {
cancel.cancel();
}
self.status = TaskStatus::Stopping;
self.restart_after_stop = restart_after;
}
}
pub(crate) fn panic_message(payload: Box<dyn std::any::Any + Send>) -> String {
payload
.downcast_ref::<&str>()
.map(|s| (*s).to_owned())
.or_else(|| payload.downcast_ref::<String>().cloned())
.unwrap_or_else(|| "non-string panic payload".to_owned())
}