use std::{future::Future, hash::Hash, sync::Arc};
use async_stream::stream;
use dashmap::DashMap;
use futures::{future::BoxFuture, stream::BoxStream, Stream};
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::{LockKind, NameLocker};
enum LockGuard<'g> {
Read(RwLockReadGuard<'g, ()>),
Write(RwLockWriteGuard<'g, ()>),
}
pub struct InmemNameLocker<Name>
where
Name: Ord + Hash + Clone + Send + Sync + 'static,
{
lock_table: Arc<DashMap<Name, Arc<RwLock<()>>>>,
}
impl<Name> Default for InmemNameLocker<Name>
where
Name: Ord + Hash + Clone + Send + Sync + 'static,
{
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<Name> InmemNameLocker<Name>
where
Name: Ord + Hash + Clone + Send + Sync + 'static,
{
#[inline]
pub fn new() -> Self {
Self {
lock_table: Arc::new(DashMap::new()),
}
}
#[inline]
fn get_or_insert_lock(&self, name: Name) -> Arc<RwLock<()>> {
self.lock_table
.entry(name) .or_insert(Arc::new(RwLock::new(())))
.clone()
}
fn _remove_lock_if_no_contention(locks: Arc<DashMap<Name, Arc<RwLock<()>>>>, name: &Name) {
let mut guard = None;
locks.remove_if_mut(name, |_, v| {
if Arc::strong_count(v) == 1 {
if let Ok(g) = v.clone().try_write_owned() {
guard = Some(g);
return true;
}
}
false
});
}
}
impl<Name> NameLocker for InmemNameLocker<Name>
where
Name: Ord + Hash + Clone + Send + Sync + 'static,
{
type Name = Name;
fn poll_with_lock<Output, Task>(
&self,
task: Task,
name: Option<Self::Name>,
lock_kind: LockKind,
) -> BoxFuture<'static, Output>
where
Task: Future<Output = Output> + Send + 'static,
{
if let Some(name) = name {
let lock_table = self.lock_table.clone();
let name_lock = self.get_or_insert_lock(name.clone());
Box::pin(async move {
let name_guard = match lock_kind {
LockKind::Shared => LockGuard::Read(name_lock.read().await),
LockKind::Exclusive => LockGuard::Write(name_lock.write().await),
};
let output = task.await;
drop(name_guard);
drop(name_lock);
Self::_remove_lock_if_no_contention(lock_table, &name);
output
})
} else {
Box::pin(task)
}
}
fn poll_read_with_lock<'s, S>(
&self,
in_stream: S,
name: Option<Self::Name>,
lock_kind: LockKind,
) -> BoxStream<'s, S::Item>
where
S: Stream + Send + 's,
<S as Stream>::Item: Send,
{
if let Some(name) = name {
let name_lock = self.get_or_insert_lock(name.clone());
let lock_table = self.lock_table.clone();
Box::pin(stream! {
let name_guard = match lock_kind {
LockKind::Shared => LockGuard::Read(name_lock.read().await),
LockKind::Exclusive => LockGuard::Write(name_lock.write().await),
};
for await item in in_stream {
yield item;
}
drop(name_guard);
drop(name_lock);
Self::_remove_lock_if_no_contention(lock_table, &name);
})
} else {
Box::pin(in_stream)
}
}
}