use std::collections::HashMap;
use std::sync::{Arc, Mutex as StdMutex, Weak};
use tokio::sync::Mutex;
const SWEEP_MIN: usize = 16;
#[derive(Debug)]
pub(crate) struct ThreadLockMap {
what: &'static str,
inner: StdMutex<Inner>,
}
#[derive(Debug)]
struct Inner {
locks: HashMap<String, Weak<Mutex<()>>>,
sweep_at: usize,
}
impl ThreadLockMap {
pub(crate) fn new(what: &'static str) -> Self {
Self {
what,
inner: StdMutex::new(Inner {
locks: HashMap::new(),
sweep_at: SWEEP_MIN,
}),
}
}
pub(crate) fn lock_for(&self, thread_id: &str) -> Arc<Mutex<()>> {
let mut inner = self
.inner
.lock()
.unwrap_or_else(|_| panic!("{} poisoned", self.what));
if let Some(existing) = inner.locks.get(thread_id).and_then(Weak::upgrade) {
return existing;
}
let lock = Arc::new(Mutex::new(()));
inner
.locks
.insert(thread_id.to_string(), Arc::downgrade(&lock));
if inner.locks.len() >= inner.sweep_at {
inner.locks.retain(|_, weak| weak.strong_count() > 0);
inner.sweep_at = (inner.locks.len() * 2).max(SWEEP_MIN);
}
lock
}
#[cfg(test)]
pub(crate) fn entry_count(&self) -> usize {
self.inner
.lock()
.unwrap_or_else(|_| panic!("{} poisoned", self.what))
.locks
.len()
}
}
#[cfg(test)]
mod test;