use std::future::Future;
use std::panic::{self, AssertUnwindSafe, PanicHookInfo};
use std::thread;
use futures::FutureExt;
use tokio::sync::Mutex;
pub static PANIC_HOOK_GUARD: Mutex<()> = Mutex::const_new(());
type PanicHook = Box<dyn Fn(&PanicHookInfo<'_>) + Sync + Send + 'static>;
struct SilentPanicHook {
previous: Option<PanicHook>,
}
impl SilentPanicHook {
fn install() -> Self {
let previous = panic::take_hook();
panic::set_hook(Box::new(|_info| {}));
Self {
previous: Some(previous),
}
}
fn restore(mut self) {
self.restore_inner();
}
fn restore_inner(&mut self) {
if let Some(hook) = self.previous.take() {
panic::set_hook(hook);
}
}
}
impl Drop for SilentPanicHook {
fn drop(&mut self) {
if !thread::panicking() {
self.restore_inner();
}
}
}
pub async fn with_silent_panic_hook<F, T>(future: F) -> T
where
F: Future<Output = T>,
{
let _hook_guard = PANIC_HOOK_GUARD.lock().await;
run_with_silent_panic_hook(future).await
}
async fn run_with_silent_panic_hook<F, T>(future: F) -> T
where
F: Future<Output = T>,
{
let hook = SilentPanicHook::install();
let result = AssertUnwindSafe(future).catch_unwind().await;
hook.restore();
match result {
Ok(output) => output,
Err(payload) => panic::resume_unwind(payload),
}
}