1//! Cycle reporting primitives
2//!
3//! This module exposes [`Dep`], which resolves to either something that tracks dependencies or to
4//! something that doesn't. It should only be assumed to implement the [`Reportable`] trait.
5use std::backtrace::Backtrace;
6use std::borrow::Cow;
7use std::fmt::Write;
8use std::sync::Arc;
910#[cfg(feature = "backtraces")]
11pub type Dep = MutexDep<Arc<Backtrace>>;
12#[cfg(not(feature = "backtraces"))]
13pub type Dep = MutexDep<()>;
1415// Base message to be reported when cycle is detected
16const BASE_MESSAGE: &str = "Found cycle in mutex dependency graph:";
1718pub trait Reportable: Clone {
19/// Capture the current state
20fn capture() -> Self;
2122/// Format a trace of state for human readable consumption.
23fn panic_message(trace: &[Self]) -> Cow<'static, str>;
24}
2526#[derive(Clone)]
27pub struct MutexDep<T>(T);
2829/// Use a unit as tracing data: no tracing.
30///
31/// This should have no runtime overhead for capturing traces and should therefore be cheap enough
32/// for most purposes.
33impl Reportable for MutexDep<()> {
34fn capture() -> Self {
35Self(())
36 }
3738fn panic_message(_trace: &[Self]) -> Cow<'static, str> {
39 Cow::Borrowed(BASE_MESSAGE)
40 }
41}
4243/// Use a full backtrace as tracing data
44///
45/// Capture the entire backtrace which may be expensive. This implementation does not force capture
46/// in the event that backtraces are disabled at runtime, so the exact overhead can still be
47/// controlled a little.
48///
49/// N.B. the [`Backtrace`] needs to be wrapped in an Arc as backtraces are not [`Clone`].
50impl Reportable for MutexDep<Arc<Backtrace>> {
51fn capture() -> Self {
52Self(Arc::new(Backtrace::capture()))
53 }
5455fn panic_message(trace: &[Self]) -> Cow<'static, str> {
56let mut message = format!("{BASE_MESSAGE}\n");
5758for entry in trace {
59let _ = writeln!(message, "{}", entry.0);
60 }
6162 message.into()
63 }
64}