use std::any::Any;
use std::panic::{
AssertUnwindSafe,
catch_unwind,
resume_unwind,
};
use std::task::Waker;
type PanicPayload = Box<dyn Any + Send + 'static>;
pub(crate) struct PanicFanout {
first_panic: Option<PanicPayload>,
}
impl PanicFanout {
#[must_use]
#[inline(always)]
pub(crate) fn new() -> Self {
Self { first_panic: None }
}
pub(crate) fn wake_all(&mut self, wakers: Vec<Waker>) {
for waker in wakers {
self.record(catch_unwind(AssertUnwindSafe(|| waker.wake_by_ref())));
self.record(catch_unwind(AssertUnwindSafe(|| drop(waker))));
}
}
#[inline]
pub(crate) fn resume_first_panic(self) {
if let Some(payload) = self.first_panic {
resume_unwind(payload);
}
}
pub(crate) fn discard_panics(mut self) {
let Some(payload) = self.first_panic.take() else {
return;
};
if let Err(drop_panic) =
catch_unwind(AssertUnwindSafe(|| drop(payload)))
{
std::mem::forget(drop_panic);
}
}
#[inline]
fn record(&mut self, result: Result<(), PanicPayload>) {
if let Err(payload) = result {
if self.first_panic.is_none() {
self.first_panic = Some(payload);
} else {
std::mem::forget(payload);
}
}
}
}