use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
#[cfg(feature = "lock_tracking")]
mod tracking {
use super::*;
use std::{
collections::VecDeque,
time::{Duration, Instant},
};
use tracing::warn;
#[derive(Debug)]
struct Inner<T> {
last_lock_owner: VecDeque<(&'static str, Duration)>,
value: T,
}
pub(crate) struct Mutex<T> {
inner: std::sync::Mutex<Inner<T>>,
}
impl<T: Debug> std::fmt::Debug for Mutex<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.inner, f)
}
}
impl<T> Mutex<T> {
pub(crate) fn new(value: T) -> Self {
Self {
inner: std::sync::Mutex::new(Inner {
last_lock_owner: VecDeque::new(),
value,
}),
}
}
pub(crate) fn lock(&self, purpose: &'static str) -> MutexGuard<T> {
let now = Instant::now();
let guard = self.inner.lock().unwrap();
let lock_time = Instant::now();
let elapsed = lock_time.duration_since(now);
if elapsed > Duration::from_millis(1) {
warn!(
"Locking the connection for {} took {:?}. Last owners: {:?}",
purpose, elapsed, guard.last_lock_owner
);
}
MutexGuard {
guard,
start_time: lock_time,
purpose,
}
}
}
pub(crate) struct MutexGuard<'a, T> {
guard: std::sync::MutexGuard<'a, Inner<T>>,
start_time: Instant,
purpose: &'static str,
}
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
if self.guard.last_lock_owner.len() == MAX_LOCK_OWNERS {
self.guard.last_lock_owner.pop_back();
}
let duration = self.start_time.elapsed();
if duration > Duration::from_millis(1) {
warn!(
"Utilizing the connection for {} took {:?}",
self.purpose, duration
);
}
self.guard
.last_lock_owner
.push_front((self.purpose, duration));
}
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.guard.value
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard.value
}
}
const MAX_LOCK_OWNERS: usize = 20;
}
#[cfg(feature = "lock_tracking")]
pub(crate) use tracking::Mutex;
#[cfg(not(feature = "lock_tracking"))]
mod non_tracking {
use super::*;
#[derive(Debug)]
pub(crate) struct Mutex<T> {
inner: std::sync::Mutex<T>,
}
impl<T> Mutex<T> {
pub(crate) fn new(value: T) -> Self {
Self {
inner: std::sync::Mutex::new(value),
}
}
pub(crate) fn lock(&self, _purpose: &'static str) -> MutexGuard<T> {
MutexGuard {
guard: self.inner.lock().unwrap(),
}
}
}
pub(crate) struct MutexGuard<'a, T> {
guard: std::sync::MutexGuard<'a, T>,
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.guard.deref()
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.guard.deref_mut()
}
}
}
#[cfg(not(feature = "lock_tracking"))]
pub(crate) use non_tracking::Mutex;