use crate::{common::get_program_name, nix::isatty, threads::is_main_thread};
use fish_common::read_blocked;
use libc::STDIN_FILENO;
use std::{
panic::{set_hook, take_hook, UnwindSafe},
sync::{
atomic::{AtomicBool, Ordering},
OnceLock,
},
time::Duration,
};
pub static AT_EXIT: OnceLock<Box<dyn Fn() + Send + Sync>> = OnceLock::new();
pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
if isatty(STDIN_FILENO) && std::env::var_os("FISH_FAST_FAIL").is_none() {
let standard_hook = take_hook();
set_hook(Box::new(move |panic_info| {
standard_hook(panic_info);
static PANICKING: AtomicBool = AtomicBool::new(false);
if PANICKING
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return;
}
if is_main_thread() {
if let Some(at_exit) = AT_EXIT.get() {
(at_exit)();
}
}
eprintf!("%s crashed, please report a bug.", get_program_name());
if !is_main_thread() {
eprintf!("\n");
std::thread::sleep(Duration::from_secs(1));
} else {
eprintf!(" Debug PID %d or press Enter to exit\n", unsafe {
libc::getpid()
});
let mut buf = [0_u8; 1];
while let Ok(n) = read_blocked(STDIN_FILENO, &mut buf) {
if n == 0 || matches!(buf[0], b'q' | b'\n' | b'\r') {
eprintf!("\n");
break;
}
}
}
std::process::abort();
}));
}
let exit_status = main();
if let Some(at_exit) = AT_EXIT.get() {
(at_exit)();
}
std::process::exit(exit_status)
}