use crate::core::detector::GLOBAL_DETECTOR;
use crate::core::detector::deadlock_handling;
use crate::core::logger;
use crate::core::types::DeadlockInfo;
use crate::core::{Detector, Events, get_current_thread_id};
use crate::{LockId, ThreadId};
#[cfg(feature = "stress-test")]
use std::thread;
impl Detector {
pub fn create_rwlock(&mut self, lock_id: LockId, creator_id: Option<ThreadId>) {
let creator = creator_id.unwrap_or_else(get_current_thread_id);
logger::log_lock_event(lock_id, Some(creator), Events::RwSpawn);
}
pub fn destroy_rwlock(&mut self, lock_id: LockId) {
self.rwlock_writer.remove(&lock_id);
self.rwlock_readers.remove(&lock_id);
for holds in self.thread_holds.values_mut() {
holds.remove(&lock_id);
}
#[cfg(feature = "lock-order-graph")]
if let Some(graph) = &mut self.lock_order_graph {
graph.remove_lock(lock_id);
}
self.lock_waiters.remove(&lock_id);
logger::log_lock_event(lock_id, None, Events::RwExit);
}
pub fn attempt_read<T, F>(
&mut self,
thread_id: ThreadId,
lock_id: LockId,
potential_writer: Option<ThreadId>,
try_acquire_fn: F,
) -> Result<Option<T>, Vec<ThreadId>>
where
F: FnOnce() -> Option<T>,
{
logger::log_interaction_event(thread_id, lock_id, Events::RwReadAttempt);
let effective_writer = self
.rwlock_writer
.get(&lock_id)
.copied()
.or(potential_writer);
if let Some(writer) = effective_writer {
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, writer) {
let filtered_cycle = self.filter_cycle_by_common_locks(&cycle);
if !filtered_cycle.is_empty() {
return Err(cycle);
}
}
return Ok(None);
}
if let Some(guard) = try_acquire_fn() {
self.rwlock_readers
.entry(lock_id)
.or_default()
.insert(thread_id);
#[cfg(feature = "lock-order-graph")]
self.thread_holds
.entry(thread_id)
.or_default()
.insert(lock_id);
self.thread_waits_for.remove(&thread_id);
logger::log_interaction_event(thread_id, lock_id, Events::RwReadAcquired);
Ok(Some(guard))
} else {
if let Some(&writer) = self.rwlock_writer.get(&lock_id) {
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, writer) {
let filtered_cycle = self.filter_cycle_by_common_locks(&cycle);
if !filtered_cycle.is_empty() {
return Err(cycle);
}
}
}
Ok(None)
}
}
pub fn complete_read(&mut self, thread_id: ThreadId, lock_id: LockId) {
self.rwlock_readers
.entry(lock_id)
.or_default()
.insert(thread_id);
#[cfg(feature = "lock-order-graph")]
self.thread_holds
.entry(thread_id)
.or_default()
.insert(lock_id);
self.thread_waits_for.remove(&thread_id);
if let Some(waiters) = self.lock_waiters.get_mut(&lock_id) {
waiters.remove(&thread_id);
if waiters.is_empty() {
self.lock_waiters.remove(&lock_id);
}
}
logger::log_interaction_event(thread_id, lock_id, Events::RwReadAcquired);
}
pub fn release_read(&mut self, thread_id: ThreadId, lock_id: LockId) {
logger::log_interaction_event(thread_id, lock_id, Events::RwReadReleased);
if let Some(readers) = self.rwlock_readers.get_mut(&lock_id) {
readers.remove(&thread_id);
if readers.is_empty() {
self.rwlock_readers.remove(&lock_id);
}
}
#[cfg(feature = "lock-order-graph")]
if let Some(holds) = self.thread_holds.get_mut(&thread_id) {
holds.remove(&lock_id);
if holds.is_empty() {
self.thread_holds.remove(&thread_id);
}
}
if let Some(waiters) = self.lock_waiters.get(&lock_id) {
for &waiter in waiters {
self.wait_for_graph.remove_edge(waiter, thread_id);
}
}
#[cfg(feature = "stress-test")]
self.stress_on_lock_release(thread_id, lock_id);
}
pub fn release_write(&mut self, thread_id: ThreadId, lock_id: LockId) {
logger::log_interaction_event(thread_id, lock_id, Events::RwWriteReleased);
if self.rwlock_writer.get(&lock_id) == Some(&thread_id) {
self.rwlock_writer.remove(&lock_id);
}
if let Some(holds) = self.thread_holds.get_mut(&thread_id) {
holds.remove(&lock_id);
if holds.is_empty() {
self.thread_holds.remove(&thread_id);
}
}
if let Some(waiters) = self.lock_waiters.get(&lock_id) {
for &waiter in waiters {
self.wait_for_graph.remove_edge(waiter, thread_id);
}
}
#[cfg(feature = "stress-test")]
self.stress_on_lock_release(thread_id, lock_id);
}
pub fn acquire_write_slow(
&mut self,
thread_id: ThreadId,
lock_id: LockId,
potential_writer: Option<ThreadId>,
) -> Option<DeadlockInfo> {
logger::log_interaction_event(thread_id, lock_id, Events::RwWriteAttempt);
#[cfg(feature = "lock-order-graph")]
if self.lock_order_graph.is_some()
&& self.thread_holds.get(&thread_id).map_or(0, |h| h.len()) >= 1
&& let Some(lock_cycle) = self.check_lock_order_violation(thread_id, lock_id)
{
return Some(self.extract_lock_order_violation_info(thread_id, lock_id, lock_cycle));
}
if let Some(readers) = self.rwlock_readers.get(&lock_id) {
for &reader in readers {
if reader != thread_id {
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, reader) {
return Some(self.extract_deadlock_info(cycle));
}
}
}
}
let effective_writer = self.rwlock_writer.get(&lock_id).copied().or_else(|| {
if let Some(writer) = potential_writer {
return Some(writer);
}
None
});
if let Some(writer) = effective_writer
&& writer != thread_id
{
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, writer) {
return Some(self.extract_deadlock_info(cycle));
}
}
None
}
pub fn complete_write(&mut self, thread_id: ThreadId, lock_id: LockId) -> Option<DeadlockInfo> {
self.rwlock_writer.insert(lock_id, thread_id);
#[allow(unused_mut)]
let mut deadlock_info = None;
#[cfg(feature = "lock-order-graph")]
if self.lock_order_graph.is_some()
&& self.thread_holds.get(&thread_id).map_or(0, |h| h.len()) >= 1
&& let Some(lock_cycle) = self.check_lock_order_violation(thread_id, lock_id)
{
deadlock_info =
Some(self.extract_lock_order_violation_info(thread_id, lock_id, lock_cycle));
}
self.thread_holds
.entry(thread_id)
.or_default()
.insert(lock_id);
self.thread_waits_for.remove(&thread_id);
if let Some(waiters) = self.lock_waiters.get_mut(&lock_id) {
waiters.remove(&thread_id);
if waiters.is_empty() {
self.lock_waiters.remove(&lock_id);
}
}
self.wait_for_graph.clear_wait_edges(thread_id);
logger::log_interaction_event(thread_id, lock_id, Events::RwWriteAcquired);
deadlock_info
}
}
pub fn create_rwlock(lock_id: LockId, creator_id: Option<ThreadId>) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.create_rwlock(lock_id, creator_id);
}
pub fn destroy_rwlock(lock_id: LockId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.destroy_rwlock(lock_id);
}
pub fn release_read(thread_id: ThreadId, lock_id: LockId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.release_read(thread_id, lock_id);
}
pub fn release_write(thread_id: ThreadId, lock_id: LockId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.release_write(thread_id, lock_id);
}
pub fn attempt_read<T, F>(thread_id: ThreadId, lock_id: LockId, try_acquire_fn: F) -> Option<T>
where
F: FnOnce() -> Option<T>,
{
#[cfg(feature = "stress-test")]
let delay = {
let detector = GLOBAL_DETECTOR.lock();
detector.calculate_stress_delay(thread_id, lock_id)
};
#[cfg(feature = "stress-test")]
if let Some(duration) = delay {
thread::sleep(duration);
}
let (result, deadlock_info) = {
let mut detector = GLOBAL_DETECTOR.lock();
match detector.attempt_read(thread_id, lock_id, None, try_acquire_fn) {
Ok(val) => (val, None),
Err(cycle) => (None, Some(detector.extract_deadlock_info(cycle))),
}
};
if let Some(info) = deadlock_info {
deadlock_handling::process_deadlock(info);
}
result
}
pub fn complete_read(thread_id: ThreadId, lock_id: LockId) {
let mut detector = GLOBAL_DETECTOR.lock();
detector.complete_read(thread_id, lock_id);
}
pub fn acquire_write_slow(
thread_id: ThreadId,
lock_id: LockId,
potential_writer: Option<ThreadId>,
) -> Option<DeadlockInfo> {
#[cfg(feature = "stress-test")]
let delay = {
let detector = GLOBAL_DETECTOR.lock();
detector.calculate_stress_delay(thread_id, lock_id)
};
#[cfg(feature = "stress-test")]
if let Some(duration) = delay {
thread::sleep(duration);
}
{
let mut detector = GLOBAL_DETECTOR.lock();
detector.acquire_write_slow(thread_id, lock_id, potential_writer)
}
}
pub fn complete_write(thread_id: ThreadId, lock_id: LockId) {
let deadlock_info = {
let mut detector = GLOBAL_DETECTOR.lock();
detector.complete_write(thread_id, lock_id)
};
if let Some(info) = deadlock_info {
deadlock_handling::process_deadlock(info);
}
}