mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat(tasks): pass downcasted error from panicked task (#3319)
This commit is contained in:
@ -17,6 +17,8 @@ use futures_util::{
|
|||||||
pin_mut, Future, FutureExt, TryFutureExt,
|
pin_mut, Future, FutureExt, TryFutureExt,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
fmt::{Display, Formatter},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{ready, Context, Poll},
|
task::{ready, Context, Poll},
|
||||||
};
|
};
|
||||||
@ -136,9 +138,9 @@ pub struct TaskManager {
|
|||||||
/// See [`Handle`] docs.
|
/// See [`Handle`] docs.
|
||||||
handle: Handle,
|
handle: Handle,
|
||||||
/// Sender half for sending panic signals to this type
|
/// Sender half for sending panic signals to this type
|
||||||
panicked_tasks_tx: UnboundedSender<&'static str>,
|
panicked_tasks_tx: UnboundedSender<PanickedTaskError>,
|
||||||
/// Listens for panicked tasks
|
/// Listens for panicked tasks
|
||||||
panicked_tasks_rx: UnboundedReceiver<&'static str>,
|
panicked_tasks_rx: UnboundedReceiver<PanickedTaskError>,
|
||||||
/// The [Signal] to fire when all tasks should be shutdown.
|
/// The [Signal] to fire when all tasks should be shutdown.
|
||||||
///
|
///
|
||||||
/// This is fired on drop.
|
/// This is fired on drop.
|
||||||
@ -177,14 +179,41 @@ impl Future for TaskManager {
|
|||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let err = ready!(self.get_mut().panicked_tasks_rx.poll_recv(cx));
|
let err = ready!(self.get_mut().panicked_tasks_rx.poll_recv(cx));
|
||||||
Poll::Ready(err.map(PanickedTaskError).expect("stream can not end"))
|
Poll::Ready(err.expect("stream can not end"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error with the name of the task that panicked.
|
/// Error with the name of the task that panicked and an error downcasted to string, if possible.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error("Critical task panicked: `{0}`")]
|
pub struct PanickedTaskError {
|
||||||
pub struct PanickedTaskError(&'static str);
|
task_name: &'static str,
|
||||||
|
error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PanickedTaskError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let task_name = self.task_name;
|
||||||
|
if let Some(error) = &self.error {
|
||||||
|
write!(f, "Critical task `{task_name}` panicked: `{error}`")
|
||||||
|
} else {
|
||||||
|
write!(f, "Critical task `{task_name}` panicked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PanickedTaskError {
|
||||||
|
fn new(task_name: &'static str, error: Box<dyn Any>) -> Self {
|
||||||
|
let error = match error.downcast::<String>() {
|
||||||
|
Ok(value) => Some(*value),
|
||||||
|
Err(error) => match error.downcast::<&str>() {
|
||||||
|
Ok(value) => Some(value.to_string()),
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { task_name, error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A type that can spawn new tokio tasks
|
/// A type that can spawn new tokio tasks
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -196,7 +225,7 @@ pub struct TaskExecutor {
|
|||||||
/// Receiver of the shutdown signal.
|
/// Receiver of the shutdown signal.
|
||||||
on_shutdown: Shutdown,
|
on_shutdown: Shutdown,
|
||||||
/// Sender half for sending panic signals to this type
|
/// Sender half for sending panic signals to this type
|
||||||
panicked_tasks_tx: UnboundedSender<&'static str>,
|
panicked_tasks_tx: UnboundedSender<PanickedTaskError>,
|
||||||
// Task Executor Metrics
|
// Task Executor Metrics
|
||||||
metrics: TaskExecutorMetrics,
|
metrics: TaskExecutorMetrics,
|
||||||
}
|
}
|
||||||
@ -298,9 +327,10 @@ impl TaskExecutor {
|
|||||||
// wrap the task in catch unwind
|
// wrap the task in catch unwind
|
||||||
let task = std::panic::AssertUnwindSafe(fut)
|
let task = std::panic::AssertUnwindSafe(fut)
|
||||||
.catch_unwind()
|
.catch_unwind()
|
||||||
.inspect_err(move |res| {
|
.map_err(move |error| {
|
||||||
error!("Critical task `{name}` panicked: {res:?}");
|
let task_error = PanickedTaskError::new(name, error);
|
||||||
let _ = panicked_tasks_tx.send(name);
|
error!("{task_error}");
|
||||||
|
let _ = panicked_tasks_tx.send(task_error);
|
||||||
})
|
})
|
||||||
.in_current_span();
|
.in_current_span();
|
||||||
|
|
||||||
@ -352,9 +382,10 @@ impl TaskExecutor {
|
|||||||
// wrap the task in catch unwind
|
// wrap the task in catch unwind
|
||||||
let task = std::panic::AssertUnwindSafe(fut)
|
let task = std::panic::AssertUnwindSafe(fut)
|
||||||
.catch_unwind()
|
.catch_unwind()
|
||||||
.inspect_err(move |res| {
|
.map_err(move |error| {
|
||||||
error!("Critical task `{name}` panicked: {res:?}");
|
let task_error = PanickedTaskError::new(name, error);
|
||||||
let _ = panicked_tasks_tx.send(name);
|
error!("{task_error}");
|
||||||
|
let _ = panicked_tasks_tx.send(task_error);
|
||||||
})
|
})
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.in_current_span();
|
.in_current_span();
|
||||||
@ -428,7 +459,8 @@ mod tests {
|
|||||||
|
|
||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
let err = manager.await;
|
let err = manager.await;
|
||||||
assert_eq!(err.0, "this is a critical task");
|
assert_eq!(err.task_name, "this is a critical task");
|
||||||
|
assert_eq!(err.error, Some("intentionally panic".to_string()));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user