task_supervisor/
handle.rs1use std::{collections::HashMap, sync::Arc};
2
3use tokio::{
4 sync::{mpsc, oneshot, watch},
5 task::JoinHandle,
6};
7
8use crate::{
9 supervisor::{Outcome, SupervisorError},
10 task::{panic_message, DynTask, Reply, SupervisedTask, TaskStatus},
11};
12
13#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
15pub enum SupervisorHandleError {
16 #[error("supervisor is no longer running")]
18 Closed,
19 #[error("task '{0}' already exists")]
21 TaskAlreadyExists(String),
22 #[error("task '{0}' not found")]
24 TaskNotFound(String),
25}
26
27pub(crate) enum Message {
28 AddTask {
29 name: Arc<str>,
30 task: Box<dyn DynTask>,
31 reply: Reply<()>,
32 },
33 RestartTask {
34 name: String,
35 reply: Reply<()>,
36 },
37 KillTask {
38 name: String,
39 reply: Reply<()>,
40 },
41 TaskStatus {
42 name: String,
43 reply: Reply<TaskStatus>,
44 },
45 TaskStatuses {
46 reply: oneshot::Sender<HashMap<Arc<str>, TaskStatus>>,
47 },
48 Shutdown,
49}
50
51#[derive(Clone)]
55pub struct SupervisorHandle {
56 tx: mpsc::UnboundedSender<Message>,
57 outcome: watch::Receiver<Option<Outcome>>,
58}
59
60impl SupervisorHandle {
61 pub(crate) fn new(join: JoinHandle<Outcome>, tx: mpsc::UnboundedSender<Message>) -> Self {
62 let (outcome_tx, outcome) = watch::channel(None);
63 tokio::spawn(async move {
64 let outcome = match join.await {
65 Ok(outcome) => outcome,
66 Err(err) if err.is_panic() => {
67 Err(SupervisorError::Panicked(panic_message(err.into_panic())))
68 }
69 Err(_) => Err(SupervisorError::Aborted),
70 };
71 let _ = outcome_tx.send(Some(outcome));
72 });
73 Self { tx, outcome }
74 }
75
76 pub async fn add_task(
79 &self,
80 name: &str,
81 task: impl SupervisedTask,
82 ) -> Result<(), SupervisorHandleError> {
83 self.request(|reply| Message::AddTask {
84 name: Arc::from(name),
85 task: Box::new(task),
86 reply,
87 })
88 .await?
89 }
90
91 pub async fn restart_task(&self, name: &str) -> Result<(), SupervisorHandleError> {
96 self.request(|reply| Message::RestartTask {
97 name: name.to_owned(),
98 reply,
99 })
100 .await?
101 }
102
103 pub async fn kill_task(&self, name: &str) -> Result<(), SupervisorHandleError> {
107 self.request(|reply| Message::KillTask {
108 name: name.to_owned(),
109 reply,
110 })
111 .await?
112 }
113
114 pub async fn task_status(&self, name: &str) -> Result<TaskStatus, SupervisorHandleError> {
116 self.request(|reply| Message::TaskStatus {
117 name: name.to_owned(),
118 reply,
119 })
120 .await?
121 }
122
123 pub async fn task_statuses(
125 &self,
126 ) -> Result<HashMap<Arc<str>, TaskStatus>, SupervisorHandleError> {
127 self.request(|reply| Message::TaskStatuses { reply }).await
128 }
129
130 pub async fn shutdown(&self) -> Outcome {
134 let _ = self.tx.send(Message::Shutdown);
135 self.wait().await
136 }
137
138 pub async fn wait(&self) -> Outcome {
141 let mut outcome = self.outcome.clone();
142 loop {
143 if let Some(outcome) = outcome.borrow_and_update().clone() {
144 return outcome;
145 }
146 if outcome.changed().await.is_err() {
147 return Err(SupervisorError::Aborted);
148 }
149 }
150 }
151
152 async fn request<T>(
153 &self,
154 message: impl FnOnce(oneshot::Sender<T>) -> Message,
155 ) -> Result<T, SupervisorHandleError> {
156 let (reply, response) = oneshot::channel();
157 self.tx
158 .send(message(reply))
159 .map_err(|_| SupervisorHandleError::Closed)?;
160 response.await.map_err(|_| SupervisorHandleError::Closed)
161 }
162}
163
164impl std::fmt::Debug for SupervisorHandle {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("SupervisorHandle")
167 .field("running", &!self.tx.is_closed())
168 .finish()
169 }
170}