use crate::core::detector::GLOBAL_DETECTOR;
use crate::core::detector::deadlock_handling;
use crate::core::logger;
use crate::core::types::{CondvarId, DeadlockInfo, LockId, ThreadId};
use crate::core::{Detector, Events, get_current_thread_id};
use std::collections::VecDeque;
impl Detector {
pub fn create_condvar(&mut self, condvar_id: CondvarId) {
self.cv_waiters.insert(condvar_id, VecDeque::new());
logger::log_lock_event(
condvar_id,
Some(get_current_thread_id()),
Events::CondvarSpawn,
);
}
pub fn destroy_condvar(&mut self, condvar_id: CondvarId) {
self.cv_waiters.remove(&condvar_id);
self.thread_wait_cv
.retain(|_, &mut (cv_id, _)| cv_id != condvar_id);
logger::log_lock_event(condvar_id, None, Events::CondvarExit);
}
pub fn begin_wait(&mut self, thread_id: ThreadId, condvar_id: CondvarId, mutex_id: LockId) {
if let Some(queue) = self.cv_waiters.get_mut(&condvar_id) {
queue.push_back((thread_id, mutex_id));
} else {
self.cv_waiters
.insert(condvar_id, VecDeque::from([(thread_id, mutex_id)]));
}
self.thread_wait_cv
.insert(thread_id, (condvar_id, mutex_id));
logger::log_interaction_event(thread_id, condvar_id, Events::CondvarWaitBegin);
}
pub fn notify_one(
&mut self,
condvar_id: CondvarId,
notifier_id: ThreadId,
) -> Vec<DeadlockInfo> {
let (woken_thread_info, deadlocks) = if let Some(queue) =
self.cv_waiters.get_mut(&condvar_id)
&& let Some((waiter_thread, mutex_id)) = queue.pop_front()
{
self.cv_woken.insert(waiter_thread);
let deadlocks = self.on_mutex_attempt_synthetic_immediate(waiter_thread, mutex_id);
(Some((waiter_thread, mutex_id)), deadlocks)
} else {
(None, Vec::new())
};
let woken_thread_id = woken_thread_info.map(|(t, _)| t);
logger::log_condvar_notify_event(
notifier_id,
condvar_id,
Events::CondvarNotifyOne,
woken_thread_id,
);
if let Some((thread_id, mutex_id)) = woken_thread_info {
logger::log_interaction_event(thread_id, mutex_id, Events::MutexAttempt);
}
deadlocks
}
pub fn notify_all(
&mut self,
condvar_id: CondvarId,
notifier_id: ThreadId,
) -> Vec<DeadlockInfo> {
let waiters_to_wake: Vec<(ThreadId, LockId)> =
if let Some(queue) = self.cv_waiters.get_mut(&condvar_id) {
queue.drain(..).collect()
} else {
Vec::new()
};
let woken_threads: Vec<ThreadId> = waiters_to_wake.iter().map(|(t, _)| *t).collect();
let mut all_deadlocks = Vec::new();
for (waiter_thread, mutex_id) in &waiters_to_wake {
self.cv_woken.insert(*waiter_thread);
let deadlocks = self.on_mutex_attempt_synthetic_immediate(*waiter_thread, *mutex_id);
all_deadlocks.extend(deadlocks);
}
logger::log_condvar_notify_event(
notifier_id,
condvar_id,
Events::CondvarNotifyAll,
woken_threads.first().copied(),
);
for (waiter_thread, mutex_id) in waiters_to_wake {
logger::log_interaction_event(waiter_thread, mutex_id, Events::MutexAttempt);
}
all_deadlocks
}
pub fn end_wait(&mut self, thread_id: ThreadId, _condvar_id: CondvarId, _mutex_id: LockId) {
self.thread_wait_cv.remove(&thread_id);
self.cv_woken.remove(&thread_id);
}
fn on_mutex_attempt_synthetic_immediate(
&mut self,
thread_id: ThreadId,
lock_id: LockId,
) -> Vec<DeadlockInfo> {
let mut deadlocks = Vec::new();
#[cfg(feature = "lock-order-graph")]
let lock_order_violation = if self.lock_order_graph.is_some()
&& self.thread_holds.get(&thread_id).map_or(0, |h| h.len()) >= 1
{
self.check_lock_order_violation(thread_id, lock_id)
} else {
None
};
#[cfg(not(feature = "lock-order-graph"))]
let _lock_order_violation: Option<Vec<LockId>> = None;
let effective_owner = self
.mutex_owners
.get(&lock_id)
.copied()
.or_else(|| Some(get_current_thread_id()));
if let Some(owner) = effective_owner {
self.thread_waits_for.insert(thread_id, lock_id);
self.lock_waiters
.entry(lock_id)
.or_default()
.insert(thread_id);
if let Some(cycle) = self.wait_for_graph.add_edge(thread_id, owner) {
let filtered_cycle = self.filter_cycle_by_common_locks(&cycle);
if !filtered_cycle.is_empty() {
let info = self.extract_deadlock_info(cycle);
deadlocks.push(info);
}
}
}
#[cfg(feature = "lock-order-graph")]
if let Some(lock_cycle) = lock_order_violation {
deadlocks.push(self.extract_lock_order_violation_info(thread_id, lock_id, lock_cycle));
}
deadlocks
}
}
pub fn create_condvar(condvar_id: CondvarId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.create_condvar(condvar_id);
}
pub fn destroy_condvar(condvar_id: CondvarId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.destroy_condvar(condvar_id);
}
pub fn begin_wait(thread_id: ThreadId, condvar_id: CondvarId, mutex_id: LockId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.begin_wait(thread_id, condvar_id, mutex_id);
}
pub fn notify_one(condvar_id: CondvarId, notifier_id: ThreadId) {
let deadlocks = {
let mut detector = GLOBAL_DETECTOR.lock();
detector.notify_one(condvar_id, notifier_id)
};
for info in deadlocks {
deadlock_handling::process_deadlock(info);
}
}
pub fn notify_all(condvar_id: CondvarId, notifier_id: ThreadId) {
let deadlocks = {
let mut detector = GLOBAL_DETECTOR.lock();
detector.notify_all(condvar_id, notifier_id)
};
for info in deadlocks {
deadlock_handling::process_deadlock(info);
}
}
pub fn end_wait(thread_id: ThreadId, condvar_id: CondvarId, mutex_id: LockId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.end_wait(thread_id, condvar_id, mutex_id);
}