Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ mod reporting;
pub mod stdsync;
pub mod util;

#[cfg(feature = "experimental")]
pub use reporting::{set_panic_action, PanicAction};

thread_local! {
/// Stack to track which locks are held
///
Expand Down Expand Up @@ -188,7 +191,7 @@ impl MutexId {
});

if let Some(cycle) = opt_cycle {
panic!("{}", Dep::panic_message(&cycle))
reporting::report_cycle(&cycle);
}

HELD_LOCKS.with(|locks| locks.borrow_mut().push(self.value()));
Expand Down
40 changes: 37 additions & 3 deletions src/reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use std::backtrace::Backtrace;
use std::borrow::Cow;
use std::fmt::Write;
use std::sync::Arc;
use std::sync::atomic::AtomicU8;
use std::sync::atomic::Ordering;

#[cfg(feature = "backtraces")]
pub type Dep = MutexDep<Arc<Backtrace>>;
Expand All @@ -15,12 +17,44 @@ pub type Dep = MutexDep<()>;
// Base message to be reported when cycle is detected
const BASE_MESSAGE: &str = "Found cycle in mutex dependency graph:";

/// Action to take when a cycle is detected.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PanicAction {
/// Panic when a cycle is detected.
Panic = 0,
/// Log the cycle to stderr but do not panic.
#[cfg(feature = "experimental")]
Log = 1,
}

static PANIC_ACTION: AtomicU8 = AtomicU8::new(0);

/// Set the action to take when a cycle is detected.
///
/// This is useful for incrementally adopting tracing-mutex to a large codebase compiled with
/// `panic=abort`, as it allows you to continue running your program even when a cycle is detected.
#[cfg(feature = "experimental")]
pub fn set_panic_action(action: PanicAction) {
PANIC_ACTION.store(action as u8, Ordering::Relaxed);
}

pub(crate) fn report_cycle(cycle: &[Dep]) {
let message = Dep::message(cycle);
let action = PANIC_ACTION.load(Ordering::Relaxed);
if action == PanicAction::Panic as u8 {
panic!("{message}");
} else {
eprintln!("{message}");
}
}

pub trait Reportable: Clone {
/// Capture the current state
fn capture() -> Self;

/// Format a trace of state for human readable consumption.
fn panic_message(trace: &[Self]) -> Cow<'static, str>;
fn message(trace: &[Self]) -> Cow<'static, str>;
}

#[derive(Clone)]
Expand All @@ -35,7 +69,7 @@ impl Reportable for MutexDep<()> {
Self(())
}

fn panic_message(_trace: &[Self]) -> Cow<'static, str> {
fn message(_trace: &[Self]) -> Cow<'static, str> {
Cow::Borrowed(BASE_MESSAGE)
}
}
Expand All @@ -52,7 +86,7 @@ impl Reportable for MutexDep<Arc<Backtrace>> {
Self(Arc::new(Backtrace::capture()))
}

fn panic_message(trace: &[Self]) -> Cow<'static, str> {
fn message(trace: &[Self]) -> Cow<'static, str> {
let mut message = format!("{BASE_MESSAGE}\n");

for entry in trace {
Expand Down
Loading