| 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 |
| 32 | panic::resume_unwind(payload:t) |
| 33 | } |
| 34 | } |
| 35 |
