use std::panic::RefUnwindSafe;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;
#[derive(Debug, Default)]
pub(crate) struct ExecuteStats {
panics: AtomicU64,
}
impl ExecuteStats {
fn incr_panic(&self) {
self.panics.fetch_add(1, Ordering::Relaxed);
}
pub fn panics(&self) -> u64 {
self.panics.load(Ordering::Relaxed)
}
}
pub(crate) fn execute<F>(f: F) -> Arc<ExecuteStats>
where
F: Fn() + Send + Sync + RefUnwindSafe + 'static,
{
let stats = Arc::new(ExecuteStats::default());
spawn_in_thread(Arc::new(f), stats.clone());
stats
}
fn spawn_in_thread<F>(job: Arc<F>, metrics: Arc<ExecuteStats>) -> thread::JoinHandle<()>
where
F: Fn() + Send + Sync + RefUnwindSafe + 'static,
{
thread::spawn(move || {
let mut sentinel = Sentinel::new(&job, &metrics);
job();
sentinel.cancel();
})
}
#[derive(Debug)]
struct Sentinel<'a, F>
where
F: Fn() + Send + Sync + RefUnwindSafe + 'static,
{
job: &'a Arc<F>,
stats: &'a Arc<ExecuteStats>,
active: bool,
}
impl<'a, F> Sentinel<'a, F>
where
F: Fn() + Send + Sync + RefUnwindSafe + 'static,
{
fn new(job: &'a Arc<F>, stats: &'a Arc<ExecuteStats>) -> Sentinel<'a, F> {
Sentinel {
job,
stats,
active: true,
}
}
fn cancel(&mut self) {
self.active = false;
}
}
impl<'a, F> Drop for Sentinel<'a, F>
where
F: Fn() + Send + Sync + RefUnwindSafe + 'static,
{
fn drop(&mut self) {
if self.active {
self.stats.incr_panic();
spawn_in_thread(self.job.clone(), self.stats.clone());
}
}
}