use std::{borrow::Borrow, collections::hash_map::Entry, hash::Hash, hint::spin_loop, thread};
use event_listener::{Event, Listener};
use gxhash::HashMap;
use parking_lot::Mutex;
const SPIN_LIMIT: u32 = 32;
const YIELD_LIMIT: u32 = 64;
pub struct EventWorkSet<K, V> {
entries: Mutex<HashMap<K, V>>,
event: Event,
}
impl<K: Eq + Hash, V> Default for EventWorkSet<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Eq + Hash, V> EventWorkSet<K, V> {
pub fn new() -> Self {
Self {
entries: Mutex::new(HashMap::default()),
event: Event::new(),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entries.lock().is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.entries.lock().len()
}
#[inline]
pub fn contains<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.entries.lock().contains_key(key)
}
pub fn wait_for_completion<Q>(&self, key: &Q)
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let mut spins = 0u32;
while self.contains(key) {
if spins < SPIN_LIMIT {
spin_loop();
spins += 1;
} else if spins < YIELD_LIMIT {
thread::yield_now();
spins += 1;
} else {
let listener = self.event.listen();
if !self.contains(key) {
break;
}
listener.wait();
}
}
}
#[inline]
pub fn try_add(&self, key: K, value: V) -> bool {
let mut map = self.entries.lock();
if let Entry::Vacant(e) = map.entry(key) {
e.insert(value);
true
} else {
false
}
}
#[inline]
pub fn try_complete<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let removed = self.entries.lock().remove(key).is_some();
if removed {
self.event.notify(usize::MAX);
}
removed
}
pub fn snapshot(&self) -> Vec<(K, V)>
where
K: Clone,
V: Clone,
{
self
.entries
.lock()
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
}