use std::borrow::Borrow;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::hash::RandomState;
use std::sync::Arc;
use crate::internal::Mutex;
use crate::internal::OnceTable;
use crate::internal::OnceTableEntry;
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct Group<K, V, S = RandomState> {
map: Mutex<OnceTable<K, V, S>>,
}
struct WorkCleanupGuard<'a, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
group: &'a Group<K, V, S>,
entry: Option<Arc<OnceTableEntry<K, V>>>,
}
impl<'a, K, V, S> WorkCleanupGuard<'a, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
fn new(group: &'a Group<K, V, S>, key: K) -> Self {
let entry = {
let mut map = group.map.lock();
Arc::clone(map.get_or_insert(key))
};
Self {
group,
entry: Some(entry),
}
}
fn entry(&self) -> &Arc<OnceTableEntry<K, V>> {
self.entry.as_ref().unwrap()
}
fn dismiss(mut self) {
drop(self.entry.take());
}
}
impl<K, V, S> Drop for WorkCleanupGuard<'_, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
fn drop(&mut self) {
let Some(entry) = self.entry.take() else {
return;
};
let mut table = self.group.map.lock();
if Arc::strong_count(&entry) == 2 && !entry.initialized() {
table.remove_entry(&entry);
}
drop(entry);
}
}
impl<K, V, S> Default for Group<K, V, S>
where
K: Eq + Hash,
V: Clone,
S: BuildHasher + Default,
{
fn default() -> Self {
Self::with_hasher(S::default())
}
}
impl<K, V> Group<K, V, RandomState>
where
K: Eq + Hash,
V: Clone,
{
pub fn new() -> Self {
Self {
map: Mutex::new(OnceTable::with_hasher(RandomState::new())),
}
}
}
impl<K, V, S> Group<K, V, S>
where
K: Eq + Hash,
V: Clone,
S: BuildHasher,
{
pub fn with_hasher(hasher: S) -> Self {
Self {
map: Mutex::new(OnceTable::with_hasher(hasher)),
}
}
pub async fn work<F>(&self, key: K, func: F) -> V
where
F: AsyncFnOnce() -> V,
{
let guard = WorkCleanupGuard::new(self, key);
let entry = guard.entry();
let result = entry
.get_or_init(async || {
let result = func().await;
self.map.lock().remove_entry(entry);
result
})
.await
.clone();
guard.dismiss();
result
}
pub async fn try_work<E, F>(&self, key: K, func: F) -> Result<V, E>
where
F: AsyncFnOnce() -> Result<V, E>,
{
let guard = WorkCleanupGuard::new(self, key);
let entry = guard.entry();
let result = entry
.get_or_try_init(async || {
let result = func().await?;
self.map.lock().remove_entry(entry);
Ok(result)
})
.await?
.clone();
guard.dismiss();
Ok(result)
}
pub fn forget<Q>(&self, key: &Q)
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let mut map = self.map.lock();
map.remove(key);
}
}