use std::collections::HashMap;
use std::panic;
use std::sync::*;
use std::thread::{self, Thread, ThreadId};
use std::time::*;
const POISON_MSG: &str = "panic_monitor: Inner lock poisoned (please submit a bug report)";
pub struct PanicMonitor {
panicked: Mutex<HashMap<ThreadId, Thread>>, cvar: Condvar,
}
impl PanicMonitor {
pub fn new() -> PanicMonitor {
PanicMonitor {
panicked: Mutex::new(HashMap::new()),
cvar: Condvar::new(),
}
}
pub fn init(&'static self) {
let hook = panic::take_hook();
panic::set_hook(Box::new(move|x| {
let mut panicked = self.panicked.lock().expect(POISON_MSG);
let current = thread::current();
panicked.insert(current.id(), current);
self.cvar.notify_all();
hook(x);
}));
}
pub fn wait(&self, watch_list: &[ThreadId]) -> Vec<Thread> {
let mut watched_panicked = vec![];
let mut panicked = self.panicked.lock().expect(POISON_MSG);
loop {
for tid in watch_list {
if let Some(t) = panicked.get(tid) {
watched_panicked.push(t.clone());
}
}
if watched_panicked.len() > 0 { return watched_panicked; }
panicked = self.cvar.wait(panicked).expect(POISON_MSG);
}
}
pub fn wait_timeout(&self, watch_list: &[ThreadId], mut dur: Duration) -> Vec<Thread> {
let mut watched_panicked = vec![];
let mut panicked = self.panicked.lock().expect(POISON_MSG);
loop {
for tid in watch_list {
if let Some(t) = panicked.get(tid) {
watched_panicked.push(t.clone());
}
}
if watched_panicked.len() > 0 { return watched_panicked; }
let now = Instant::now();
let (guard, res) = self.cvar.wait_timeout(panicked, dur).expect(POISON_MSG);
let elapsed = now.elapsed();
panicked = guard;
if res.timed_out() || elapsed >= dur { return vec![]; }
dur -= elapsed; }
}
pub fn check(&self, watch_list: &[ThreadId]) -> Vec<Thread> {
let mut watched_panicked = vec![];
let panicked = self.panicked.lock().expect(POISON_MSG);
for tid in watch_list {
if let Some(t) = panicked.get(tid) {
watched_panicked.push(t.clone());
}
}
watched_panicked
}
}