use std::any::Any;
use std::panic::{AssertUnwindSafe, catch_unwind};
pub type TaskPanicPayload = Box<dyn Any + Send + 'static>;
#[cfg_attr(coverage_nightly, coverage(off))] pub fn discard_task_panic_payload(mut panic: TaskPanicPayload) {
const MAX_DISPOSAL_ATTEMPTS: usize = 32;
for _ in 0..MAX_DISPOSAL_ATTEMPTS {
match catch_unwind(AssertUnwindSafe(|| drop(panic))) {
Ok(()) => return,
Err(secondary) => panic = secondary,
}
}
abort_after_repeated_payload_panic(panic);
}
#[cfg_attr(coverage_nightly, coverage(off))] fn abort_after_repeated_payload_panic(_panic: TaskPanicPayload) -> ! {
std::process::abort();
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::*;
struct PanicOnce(Arc<AtomicUsize>);
impl Drop for PanicOnce {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
panic!("panic payload destructor");
}
}
#[test]
fn captured_panic_with_panicking_destructor_is_discarded() {
let drops = Arc::new(AtomicUsize::new(0));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe({
let drops = Arc::clone(&drops);
move || discard_task_panic_payload(Box::new(PanicOnce(drops)))
}));
result.unwrap();
assert_eq!(drops.load(Ordering::Relaxed), 1);
}
}