tinyflow_framework/runtime/
task_lifecycle.rs1use std::future::Future;
4
5use tinyflow_api::error::{StreamError, StreamResult, task_failed};
6
7pub async fn report_task_failure(
9 error_tx: &tokio::sync::mpsc::Sender<StreamError>,
10 task_id: u32,
11 task_name: &str,
12 err: StreamError,
13) {
14 log::error!("[{task_name}] task failed: {err}");
15 if error_tx.send(err).await.is_err() {
16 log::error!(
17 "[{task_name}] could not report failure to job (task_id={task_id}, error_tx closed)"
18 );
19 }
20}
21
22pub fn spawn_monitored_task<F, Fut>(
24 task_id: u32,
25 task_name: impl Into<String>,
26 error_tx: tokio::sync::mpsc::Sender<StreamError>,
27 run: F,
28) -> tokio::task::JoinHandle<()>
29where
30 F: FnOnce() -> Fut + Send + 'static,
31 Fut: Future<Output = StreamResult<()>> + Send + 'static,
32{
33 let task_name = task_name.into();
34 tokio::spawn(async move {
35 let inner = tokio::spawn(run());
36 match inner.await {
37 Ok(Ok(())) => {}
38 Ok(Err(e)) => report_task_failure(&error_tx, task_id, &task_name, e).await,
39 Err(join_err) => {
40 report_task_failure(
41 &error_tx,
42 task_id,
43 &task_name,
44 task_failed(task_id, format!("task panicked or cancelled: {join_err}")),
45 )
46 .await;
47 }
48 }
49 })
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use tinyflow_api::error::StreamError;
56
57 #[tokio::test]
58 async fn report_task_failure_logs_when_channel_closed() {
59 let (tx, rx) = tokio::sync::mpsc::channel(1);
60 drop(rx);
61 report_task_failure(
62 &tx,
63 1,
64 "test-task",
65 StreamError::TaskFailed {
66 task_id: 1,
67 reason: "boom".into(),
68 },
69 )
70 .await;
71 }
72}