tracing_mutex/util.rs
1//! Utilities related to the internals of dependency tracking.
2use crate::MutexId;
3#[cfg(feature = "experimental")]
4#[cfg(feature = "experimental")]
5use crate::reporting::cycle_handler_storage;
6
7/// Reset the dependencies for the given entity.
8///
9/// # Performance
10///
11/// This function locks the dependency graph to remove the item from it. This is an `O(E)` operation
12/// with `E` being the number of dependencies directly associated with this particular instance. As
13/// such, it is not advisable to call this method from a hot loop.
14///
15/// # Safety
16///
17/// Use of this method invalidates the deadlock prevention guarantees that this library makes. As
18/// such, it should only be used when it is absolutely certain this will not introduce deadlocks
19/// later.
20///
21/// Other than deadlocks, no undefined behaviour can result from the use of this function.
22///
23/// # Example
24///
25/// ```
26/// use tracing_mutex::stdsync::Mutex;
27/// use tracing_mutex::util;
28///
29/// let first = Mutex::new(());
30/// let second = Mutex::new(());
31///
32/// {
33/// let _first_lock = first.lock().unwrap();
34/// second.lock().unwrap();
35/// }
36///
37/// // Reset the dependencies for the first mutex
38/// unsafe { util::reset_dependencies(&first) };
39///
40/// // Now we can unlock the mutexes in the opposite order without a panic.
41/// let _second_lock = second.lock().unwrap();
42/// first.lock().unwrap();
43/// ```
44#[cfg(feature = "experimental")]
45#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
46pub unsafe fn reset_dependencies<T: Traced>(traced: &T) {
47 crate::get_dependency_graph().remove_node(traced.get_id().value());
48}
49
50/// Install a new cycle handler
51///
52/// This function will be called whenever a cycle in the dependency graph is detected. The default
53/// handler will panic with a message describing the panic, but this can be downgraded to logging,
54/// or even some complicated reporting mechanism.
55///
56/// ```
57/// # use tracing_mutex::util::set_cycle_handler;
58/// let mut logged = false;
59///
60/// set_cycle_handler(move |message| {
61/// if !logged {
62/// logged = true;
63/// eprintln!("{message}");
64/// }
65/// });
66/// ```
67///
68/// This handler can be reset to its original state via [`reset_cycle_handler`].
69#[cfg(feature = "experimental")]
70#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
71pub fn set_cycle_handler<T>(handler: T)
72where
73 T: FnMut(&str) + Send + Sync + 'static,
74{
75 *cycle_handler_storage() = Some(Box::new(handler))
76}
77
78/// Reset cycle handler to its original state
79///
80/// This function returns `true` if a custom handler was previously installed, and `false`
81/// otherwise.
82#[cfg(feature = "experimental")]
83#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
84pub fn reset_cycle_handler() -> bool {
85 cycle_handler_storage().take().is_some()
86}
87
88/// Types that participate in dependency tracking
89///
90/// This trait is a public marker trait and is automatically implemented fore all types that
91/// implement the internal dependency tracking features.
92#[cfg(feature = "experimental")]
93#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
94#[allow(private_bounds)]
95pub trait Traced: PrivateTraced {}
96
97#[cfg(feature = "experimental")]
98#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
99impl<T: PrivateTraced> Traced for T {}
100
101/// Private implementation of the traced marker.
102///
103/// This trait is private (and seals the outer trait) to avoid exposing the MutexId type.
104#[cfg_attr(not(feature = "experimental"), allow(unused))]
105pub(crate) trait PrivateTraced {
106 /// Get the mutex id associated with this traced item.
107 fn get_id(&self) -> &MutexId;
108}