1 | use std::any::Any; |
2 | use std::cell::RefCell; |
3 | use std::panic::{self, AssertUnwindSafe}; |
4 | |
5 | thread_local!(static LAST_ERROR: RefCell<Option<Box<dyn Any + Send>>> = { |
6 | RefCell::new(None) |
7 | }); |
8 | |
9 | pub fn catch<T, F: FnOnce() -> T>(f: F) -> Option<T> { |
10 | match LAST_ERROR.try_with(|slot: &RefCell| slot.borrow().is_some()) { |
11 | Ok(true) => return None, |
12 | Ok(false) => {} |
13 | // we're in thread shutdown, so we're for sure not panicking and |
14 | // panicking again will abort, so no need to worry! |
15 | Err(_) => {} |
16 | } |
17 | |
18 | // Note that `AssertUnwindSafe` is used here as we prevent reentering |
19 | // arbitrary code due to the `LAST_ERROR` check above plus propagation of a |
20 | // panic after we return back to user code from C. |
21 | match panic::catch_unwind(AssertUnwindSafe(f)) { |
22 | Ok(ret: T) => Some(ret), |
23 | Err(e: Box) => { |
24 | LAST_ERROR.with(|slot: &RefCell| *slot.borrow_mut() = Some(e)); |
25 | None |
26 | } |
27 | } |
28 | } |
29 | |
30 | pub fn propagate() { |
31 | if let Ok(Some(t: Box)) = LAST_ERROR.try_with(|slot: &RefCell| slot.borrow_mut().take()) { |
32 | panic::resume_unwind(payload:t) |
33 | } |
34 | } |
35 | |