drop_panic/
lib.rs

1#[macro_export]
2macro_rules! drop_panic {
3    ($($t:tt)*) => {
4        let _drop_panic = $crate::guard(|| { $($t)* });
5    };
6}
7
8pub fn guard<F>(f: F) -> DropPanic<F>
9where
10    F: Fn(),
11{
12    DropPanic::new(f)
13}
14
15/// The callback that will be called if the current thread panics
16pub struct DropPanic<F>(F)
17where
18    F: Fn();
19
20impl<F> DropPanic<F>
21where
22    F: Fn(),
23{
24    pub fn new(panic_fn: F) -> Self {
25        Self(panic_fn)
26    }
27}
28
29impl<F> Drop for DropPanic<F>
30where
31    F: Fn(),
32{
33    fn drop(&mut self) {
34        if std::thread::panicking() {
35            (self.0)();
36        }
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use crate::drop_panic;
43    use std::{
44        sync::{
45            atomic::{AtomicBool, Ordering},
46            Arc,
47        },
48        thread,
49    };
50
51    #[test]
52    fn test_drop_panic() {
53        let panicked = Arc::new(AtomicBool::new(false));
54
55        let jh = thread::spawn({
56            let panicked = Arc::clone(&panicked);
57            move || {
58                drop_panic! {
59                    panicked.store(true, Ordering::Release);
60                };
61
62                panic!("boom");
63            }
64        });
65
66        assert!(jh.join().is_err());
67        assert!(panicked.load(Ordering::Acquire));
68    }
69}