Make dependency tracking a compile time setting

This commit is contained in:
2023-09-09 11:10:08 +02:00
parent 068303d81d
commit a8e8af6351
3 changed files with 77 additions and 41 deletions

View File

@@ -46,7 +46,6 @@
//!
//! [paper]: https://whileydave.com/publications/pk07_jea/
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::backtrace::Backtrace;
use std::cell::RefCell;
use std::fmt;
use std::marker::PhantomData;
@@ -54,7 +53,6 @@ use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::OnceLock;
@@ -66,6 +64,8 @@ pub use lock_api;
#[cfg(feature = "parkinglot")]
#[cfg_attr(docsrs, doc(cfg(feature = "parkinglot")))]
pub use parking_lot;
use reporting::Dep;
use reporting::Reportable;
use crate::graph::DiGraph;
@@ -76,6 +76,7 @@ pub mod lockapi;
#[cfg(feature = "parkinglot")]
#[cfg_attr(docsrs, doc(cfg(feature = "parkinglot")))]
pub mod parkinglot;
mod reporting;
pub mod stdsync;
thread_local! {
@@ -143,24 +144,14 @@ impl MutexId {
if let Some(&previous) = locks.borrow().last() {
let mut graph = get_dependency_graph();
graph
.add_edge(previous, self.value(), MutexDep::capture)
.err()
graph.add_edge(previous, self.value(), Dep::capture).err()
} else {
None
}
});
if let Some(cycle) = opt_cycle {
// Panic without holding the lock to avoid needlessly poisoning it
let mut message = String::from("Found cycle in mutex dependency graph:\n");
for entry in cycle {
use std::fmt::Write;
let _ = writeln!(message, "{entry}");
}
panic!("{message}");
panic!("{}", Dep::panic_message(&cycle))
}
HELD_LOCKS.with(|locks| locks.borrow_mut().push(self.value()));
@@ -270,24 +261,9 @@ impl<'a> Drop for BorrowedMutex<'a> {
}
}
#[derive(Clone)]
struct MutexDep(Arc<Backtrace>);
impl MutexDep {
pub fn capture() -> Self {
Self(Arc::new(Backtrace::capture()))
}
}
impl fmt::Display for MutexDep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
/// Get a reference to the current dependency graph
fn get_dependency_graph() -> impl DerefMut<Target = DiGraph<usize, MutexDep>> {
static DEPENDENCY_GRAPH: OnceLock<Mutex<DiGraph<usize, MutexDep>>> = OnceLock::new();
fn get_dependency_graph() -> impl DerefMut<Target = DiGraph<usize, Dep>> {
static DEPENDENCY_GRAPH: OnceLock<Mutex<DiGraph<usize, Dep>>> = OnceLock::new();
DEPENDENCY_GRAPH
.get_or_init(Default::default)
@@ -318,17 +294,11 @@ mod tests {
let c = LazyMutexId::new();
let mut graph = get_dependency_graph();
assert!(graph
.add_edge(a.value(), b.value(), MutexDep::capture)
.is_ok());
assert!(graph
.add_edge(b.value(), c.value(), MutexDep::capture)
.is_ok());
assert!(graph.add_edge(a.value(), b.value(), Dep::capture).is_ok());
assert!(graph.add_edge(b.value(), c.value(), Dep::capture).is_ok());
// Creating an edge c → a should fail as it introduces a cycle.
assert!(graph
.add_edge(c.value(), a.value(), MutexDep::capture)
.is_err());
assert!(graph.add_edge(c.value(), a.value(), Dep::capture).is_err());
// Drop graph handle so we can drop vertices without deadlocking
drop(graph);
@@ -337,7 +307,7 @@ mod tests {
// If b's destructor correctly ran correctly we can now add an edge from c to a.
assert!(get_dependency_graph()
.add_edge(c.value(), a.value(), MutexDep::capture)
.add_edge(c.value(), a.value(), Dep::capture)
.is_ok());
}