| 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 |  | 
|---|