noq/
mutex.rs

1use std::{
2    fmt::Debug,
3    ops::{Deref, DerefMut},
4};
5
6#[cfg(feature = "lock_tracking")]
7mod tracking {
8    use super::*;
9    use crate::{Duration, Instant};
10    use std::collections::VecDeque;
11    use tracing::warn;
12
13    #[derive(Debug)]
14    struct Inner<T> {
15        last_lock_owner: VecDeque<(&'static str, Duration)>,
16        value: T,
17    }
18
19    /// A Mutex which optionally allows to track the time a lock was held and
20    /// emit warnings in case of excessive lock times
21    pub(crate) struct Mutex<T> {
22        inner: std::sync::Mutex<Inner<T>>,
23    }
24
25    impl<T: Debug> Debug for Mutex<T> {
26        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27            Debug::fmt(&self.inner, f)
28        }
29    }
30
31    impl<T> Mutex<T> {
32        pub(crate) fn new(value: T) -> Self {
33            Self {
34                inner: std::sync::Mutex::new(Inner {
35                    last_lock_owner: VecDeque::new(),
36                    value,
37                }),
38            }
39        }
40
41        /// Acquires the lock for a certain purpose
42        ///
43        /// The purpose will be recorded in the list of last lock owners
44        pub(crate) fn lock(&self, purpose: &'static str) -> MutexGuard<'_, T> {
45            // We don't bother dispatching through Runtime::now because they're pure performance
46            // diagnostics.
47            let now = Instant::now();
48            let guard = self.inner.lock().unwrap();
49
50            let lock_time = Instant::now();
51            let elapsed = lock_time.duration_since(now);
52
53            if elapsed > Duration::from_millis(1) {
54                warn!(
55                    "Locking the connection for {} took {:?}. Last owners: {:?}",
56                    purpose, elapsed, guard.last_lock_owner
57                );
58            }
59
60            MutexGuard {
61                guard,
62                start_time: lock_time,
63                purpose,
64            }
65        }
66    }
67
68    pub(crate) struct MutexGuard<'a, T> {
69        guard: std::sync::MutexGuard<'a, Inner<T>>,
70        start_time: Instant,
71        purpose: &'static str,
72    }
73
74    impl<T> Drop for MutexGuard<'_, T> {
75        fn drop(&mut self) {
76            if self.guard.last_lock_owner.len() == MAX_LOCK_OWNERS {
77                self.guard.last_lock_owner.pop_back();
78            }
79
80            let duration = self.start_time.elapsed();
81
82            if duration > Duration::from_millis(1) {
83                warn!(
84                    "Utilizing the connection for {} took {:?}",
85                    self.purpose, duration
86                );
87            }
88
89            self.guard
90                .last_lock_owner
91                .push_front((self.purpose, duration));
92        }
93    }
94
95    impl<T> Deref for MutexGuard<'_, T> {
96        type Target = T;
97
98        fn deref(&self) -> &Self::Target {
99            &self.guard.value
100        }
101    }
102
103    impl<T> DerefMut for MutexGuard<'_, T> {
104        fn deref_mut(&mut self) -> &mut Self::Target {
105            &mut self.guard.value
106        }
107    }
108
109    const MAX_LOCK_OWNERS: usize = 20;
110}
111
112#[cfg(feature = "lock_tracking")]
113pub(crate) use tracking::{Mutex, MutexGuard};
114
115#[cfg(not(feature = "lock_tracking"))]
116mod non_tracking {
117    use super::*;
118
119    /// A Mutex which optionally allows to track the time a lock was held and
120    /// emit warnings in case of excessive lock times
121    #[derive(Debug)]
122    pub(crate) struct Mutex<T> {
123        inner: std::sync::Mutex<T>,
124    }
125
126    impl<T> Mutex<T> {
127        pub(crate) fn new(value: T) -> Self {
128            Self {
129                inner: std::sync::Mutex::new(value),
130            }
131        }
132
133        /// Acquires the lock for a certain purpose
134        ///
135        /// The purpose will be recorded in the list of last lock owners
136        pub(crate) fn lock(&self, _purpose: &'static str) -> MutexGuard<'_, T> {
137            MutexGuard {
138                guard: self.inner.lock().unwrap(),
139            }
140        }
141    }
142
143    pub(crate) struct MutexGuard<'a, T> {
144        guard: std::sync::MutexGuard<'a, T>,
145    }
146
147    impl<T> Deref for MutexGuard<'_, T> {
148        type Target = T;
149
150        fn deref(&self) -> &Self::Target {
151            self.guard.deref()
152        }
153    }
154
155    impl<T> DerefMut for MutexGuard<'_, T> {
156        fn deref_mut(&mut self) -> &mut Self::Target {
157            self.guard.deref_mut()
158        }
159    }
160}
161
162#[cfg(not(feature = "lock_tracking"))]
163pub(crate) use non_tracking::{Mutex, MutexGuard};