use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex as StdMutex;
static NEXT_GUARD_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RawGuardId(usize);
#[derive(Debug)]
struct LockIdContainerInner {
create_backtrace: backtrace::Backtrace,
active_guards: StdMutex<HashMap<RawGuardId, backtrace::Backtrace>>,
}
#[derive(Clone, Debug)]
pub(super) struct LockIdContainer {
inner: Arc<LockIdContainerInner>,
}
impl LockIdContainer {
pub fn next() -> LockIdContainer {
LockIdContainer {
inner: Arc::new(LockIdContainerInner {
create_backtrace: debug_lock_backtrace(),
active_guards: StdMutex::new(HashMap::new()),
}),
}
}
pub fn report_deadlock(&self, desc: &str) -> ! {
let active_guard_backtraces: Vec<String> = {
let active_guards = self.inner.active_guards.lock().unwrap();
active_guards
.values()
.map(|bt| bt.trim().to_string())
.collect()
};
use core::fmt::Write as _;
let mut r = String::new();
let _ = writeln!(
r,
"Deadlock detected! ({})\n\nLock creation backtrace:\n{}\n\nActive guard backtraces:\n",
desc,
self.inner.create_backtrace.trim(),
);
for (n, agb) in active_guard_backtraces.iter().enumerate() {
let _ = writeln!(r, "{}:\n{}\n", n, indent::indent_all_by(4, agb));
}
let report_backtrace = backtrace::Backtrace::new_unresolved().trim();
let _ = writeln!(r, "Panic backtrace:\n{}", report_backtrace);
eprintln!("{r}");
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
{
let _ = std::fs::write(
std::env::temp_dir().join("veilid_async_lock_deadlock.txt"),
r.as_bytes(),
);
}
std::process::exit(1);
}
}
#[derive(Debug)]
pub struct GuardIdContainerInner {
raw_guard_id: RawGuardId,
lock_id_container: LockIdContainer,
}
impl Drop for GuardIdContainerInner {
fn drop(&mut self) {
self.lock_id_container
.inner
.active_guards
.lock()
.unwrap()
.remove(&self.raw_guard_id);
}
}
#[derive(Clone, Debug)]
pub struct GuardIdContainer {
inner: Arc<GuardIdContainerInner>,
}
impl GuardIdContainer {
#[expect(dead_code)]
pub fn id(&self) -> RawGuardId {
self.inner.raw_guard_id
}
pub fn next(lock_id_container: LockIdContainer) -> GuardIdContainer {
let raw_guard_id = RawGuardId(NEXT_GUARD_ID.fetch_add(1, Ordering::Relaxed));
let lock_backtrace = debug_lock_backtrace();
lock_id_container
.inner
.active_guards
.lock()
.unwrap()
.insert(raw_guard_id, lock_backtrace);
GuardIdContainer {
inner: Arc::new(GuardIdContainerInner {
raw_guard_id,
lock_id_container,
}),
}
}
}