1use std::any::Any;
2use std::cell::RefCell;
3use std::panic::{self, AssertUnwindSafe};
4
5thread_local!(static LAST_ERROR: RefCell<Option<Box<dyn Any + Send>>> = {
6 RefCell::new(None)
7});
8
9pub 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
30pub 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