use std::panic;
use std::sync::{Mutex, Once};
use log::error;
use crate::event::Event;
use crate::must::Must;
use crate::runtime::execution::ExecutionState;
pub(crate) fn persist_task_failure(message: String, pos: Option<Event>) -> String {
if let PanicHookState::Persisted(persisted_message) =
PANIC_HOOK.with(|lock| lock.lock().unwrap().clone())
{
return persisted_message;
}
if let Some(must) = Must::current() {
if let Ok(mut must) = must.try_borrow_mut() {
must.store_replay_information(pos);
} else {
error!("Couldn't generate a counterexample because Must::current is borrowed");
}
} else {
error!("Couldn't generate a counterexample because Must::current returned None");
}
let persisted_message = message;
PANIC_HOOK
.with(|lock| *lock.lock().unwrap() = PanicHookState::Persisted(persisted_message.clone()));
println!("{}", persisted_message);
persisted_message
}
#[derive(Clone)]
enum PanicHookState {
Disarmed,
Armed,
Persisted(String),
}
thread_local! {
static PANIC_HOOK: Mutex<PanicHookState> = const { Mutex::new(PanicHookState::Disarmed) };
}
#[derive(Debug)]
#[non_exhaustive]
pub struct PanicHookGuard;
impl Drop for PanicHookGuard {
fn drop(&mut self) {
if std::thread::panicking() { return; }
PANIC_HOOK.with(|lock| *lock.lock().unwrap() = PanicHookState::Disarmed);
}
}
#[must_use = "the panic hook will be disarmed when the returned guard is dropped"]
pub(crate) fn init_panic_hook() -> PanicHookGuard {
static INIT: Once = Once::new();
INIT.call_once(|| {
let original_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
let state = PANIC_HOOK.with(|lock| {
std::mem::replace(&mut *lock.lock().unwrap(), PanicHookState::Disarmed)
});
if let PanicHookState::Armed = state {
if let Some((name, pos)) = ExecutionState::failure_info() {
persist_task_failure(name, Some(pos));
} else {
persist_task_failure("A panic was detected".to_string(), None);
}
}
original_hook(panic_info);
}));
});
PANIC_HOOK.with(|lock| *lock.lock().unwrap() = PanicHookState::Armed);
PanicHookGuard
}