thread_local_panic_hook/
panic.rs1use std::cell::RefCell;
2use std::ops::Deref;
3use std::panic::PanicInfo;
4use std::sync::Once;
5use std::{panic, thread};
6
7static SET_HOOK: Once = Once::new();
8static mut DEFAULT_HOOK: Option<Hook> = None;
9
10thread_local! {
11 static PANIC_HOOK: RefCell<Option<Hook>> = RefCell::new(None);
12}
13
14#[derive(Copy, Clone)]
15struct Hook(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send));
16
17impl Hook {
18 pub fn new(f: impl Fn(&PanicInfo<'_>) + 'static + Sync + Send) -> Self {
19 Self(Box::into_raw(Box::new(f)))
20 }
21}
22
23pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
25 common();
26
27 PANIC_HOOK.with(|cell| {
28 cell.replace(Some(Hook::new(hook)));
29 });
30}
31
32pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
34 common();
35
36 PANIC_HOOK.with(|cell| match cell.take() {
37 None => unsafe { Box::from_raw(DEFAULT_HOOK.unwrap().0) },
38 Some(Hook(ptr)) => unsafe { Box::from_raw(ptr) },
39 })
40}
41
42pub fn update_hook<F>(hook_fn: F)
44where
45 F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>)
46 + Sync
47 + Send
48 + 'static,
49{
50 let old = take_hook();
51 set_hook(Box::new(move |e| {
52 hook_fn(&old, e);
53 }));
54}
55
56#[inline]
57fn common() {
58 if thread::panicking() {
59 panic!("cannot modify the panic hook from a panicking thread");
60 }
61
62 SET_HOOK.call_once(|| {
63 unsafe {
64 DEFAULT_HOOK = Some(Hook::new(panic::take_hook()));
65 }
66 panic::set_hook(Box::new(default));
67 });
68}
69
70fn default(e: &PanicInfo) {
71 PANIC_HOOK.with(move |cell| {
72 let borrow = cell.borrow();
73 unsafe {
74 let hook = borrow.deref().unwrap_or_else(|| DEFAULT_HOOK.unwrap());
75 let ptr = hook.0;
76 (*ptr)(e);
77 }
78 })
79}