thread_local_panic_hook/
panic.rs

1use 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
23/// See [`std::panic::set_hook`]
24pub 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
32/// See [`std::panic::take_hook`]
33pub 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
42/// See [`std::panic::update_hook`]
43pub 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}