pub mod condvar;
pub mod deadlock_handling;
pub mod mutex;
pub mod rwlock;
mod stress;
pub mod thread;
#[cfg(feature = "stress-test")]
use crate::core::StressConfig;
#[cfg(feature = "stress-test")]
use crate::core::StressMode;
#[cfg(feature = "lock-order-graph")]
use crate::core::graph::LockOrderGraph;
use crate::core::graph::WaitForGraph;
#[cfg(feature = "logging-and-visualization")]
use crate::core::logger::{self, EventLogger};
use crate::core::types::{CondvarId, DeadlockInfo, LockId, ThreadId};
#[cfg(feature = "logging-and-visualization")]
use anyhow::Result;
use fxhash::{FxHashMap, FxHashSet};
use parking_lot::Mutex;
use std::collections::VecDeque;
use std::sync::mpsc::{Sender, channel};
use std::sync::{Arc, OnceLock};
pub struct DetectorConfig {
pub callback: Box<dyn Fn(DeadlockInfo) + Send + Sync>,
#[cfg(feature = "lock-order-graph")]
pub check_lock_order: bool,
#[cfg(feature = "stress-test")]
pub stress_mode: StressMode,
#[cfg(feature = "stress-test")]
pub stress_config: Option<StressConfig>,
#[cfg(feature = "logging-and-visualization")]
pub logger: Option<EventLogger>,
}
lazy_static::lazy_static! {
static ref DISPATCHER: Dispatcher = {
Dispatcher::new()
};
}
static CALLBACK: OnceLock<Arc<dyn Fn(DeadlockInfo) + Send + Sync>> = OnceLock::new();
struct Dispatcher {
sender: Sender<DeadlockInfo>,
_thread_handle: std::thread::JoinHandle<()>,
}
impl Dispatcher {
fn new() -> Self {
let (tx, rx) = channel::<DeadlockInfo>();
let thread_handle = std::thread::spawn(move || {
while let Ok(info) = rx.recv() {
if let Some(cb) = CALLBACK.get() {
cb(info);
}
}
});
Dispatcher {
sender: tx,
_thread_handle: thread_handle,
}
}
fn send(&self, info: DeadlockInfo) {
let _ = self.sender.send(info);
}
}
pub struct Detector {
wait_for_graph: WaitForGraph,
#[cfg(feature = "lock-order-graph")]
lock_order_graph: Option<LockOrderGraph>,
thread_waits_for: FxHashMap<ThreadId, LockId>,
thread_holds: FxHashMap<ThreadId, FxHashSet<LockId>>,
mutex_owners: FxHashMap<LockId, ThreadId>,
rwlock_readers: FxHashMap<LockId, FxHashSet<ThreadId>>,
rwlock_writer: FxHashMap<LockId, ThreadId>,
cv_waiters: FxHashMap<CondvarId, VecDeque<(ThreadId, LockId)>>,
thread_wait_cv: FxHashMap<ThreadId, (CondvarId, LockId)>,
cv_woken: FxHashSet<ThreadId>,
lock_waiters: FxHashMap<LockId, FxHashSet<ThreadId>>,
#[cfg(feature = "stress-test")]
stress_mode: StressMode,
#[cfg(feature = "stress-test")]
stress_config: Option<StressConfig>,
}
impl Default for Detector {
fn default() -> Self {
Self::new()
}
}
impl Detector {
pub fn new() -> Self {
Detector {
wait_for_graph: WaitForGraph::new(),
#[cfg(feature = "lock-order-graph")]
lock_order_graph: None, thread_waits_for: FxHashMap::default(),
thread_holds: FxHashMap::default(),
mutex_owners: FxHashMap::default(),
rwlock_readers: FxHashMap::default(),
rwlock_writer: FxHashMap::default(),
cv_waiters: FxHashMap::default(),
thread_wait_cv: FxHashMap::default(),
cv_woken: FxHashSet::default(),
lock_waiters: FxHashMap::default(),
#[cfg(feature = "stress-test")]
stress_mode: StressMode::None,
#[cfg(feature = "stress-test")]
stress_config: None,
}
}
pub fn set_deadlock_callback<F>(&mut self, callback: F)
where
F: Fn(DeadlockInfo) + Send + Sync + 'static,
{
let cb: Arc<dyn Fn(DeadlockInfo) + Send + Sync> = Arc::new(callback);
CALLBACK.set(cb).ok();
}
#[cfg(feature = "lock-order-graph")]
fn check_lock_order_violation(
&mut self,
thread_id: ThreadId,
lock_id: LockId,
) -> Option<Vec<LockId>> {
let graph = self.lock_order_graph.as_mut()?;
if let Some(held_locks) = self.thread_holds.get(&thread_id) {
for &held_lock in held_locks {
if let Some(lock_cycle) = graph.add_edge(held_lock, lock_id) {
return Some(lock_cycle);
}
}
}
None
}
}
lazy_static::lazy_static! {
static ref GLOBAL_DETECTOR: Mutex<Detector> = Mutex::new(Detector::new());
}
pub fn init_detector(config: DetectorConfig) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.set_deadlock_callback(config.callback);
#[cfg(feature = "logging-and-visualization")]
if let Some(logger) = config.logger {
logger::init_logger(logger);
}
#[cfg(feature = "lock-order-graph")]
if config.check_lock_order {
detector.lock_order_graph = Some(LockOrderGraph::new());
}
#[cfg(not(feature = "lock-order-graph"))]
#[cfg(feature = "lock-order-graph")]
{}
#[cfg(feature = "stress-test")]
{
detector.stress_mode = config.stress_mode;
detector.stress_config = config.stress_config;
}
}
#[cfg(feature = "logging-and-visualization")]
pub fn flush_global_detector_logs() -> Result<()> {
logger::flush_logs()
}