use std::{
sync::Arc,
task::{Context, Waker}
};
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
struct Inner {
wake_on_drop: bool,
wakers: FxHashMap<u32, Waker>,
idgen: u32
}
#[repr(transparent)]
#[derive(Clone)]
pub struct Wakers(Arc<Mutex<Inner>>);
impl Wakers {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn wake_on_drop(&self) {
let mut inner = self.0.lock();
inner.wake_on_drop = true;
}
pub fn wake_all(&self) {
self
.0
.lock()
.wakers
.drain()
.for_each(|(_, waker)| waker.wake());
}
#[must_use]
pub fn waiter(&self) -> Waiter {
Waiter {
sh: Arc::clone(&self.0),
id: None
}
}
pub fn waiter_ctx(&self, ctx: &mut Context<'_>) -> Waiter {
let mut waiter = self.waiter();
waiter.prime(ctx);
waiter
}
}
impl Default for Wakers {
fn default() -> Self {
let inner = Inner {
wake_on_drop: false,
wakers: FxHashMap::default(),
idgen: 0
};
Self(Arc::new(Mutex::new(inner)))
}
}
impl Drop for Wakers {
fn drop(&mut self) {
let mut inner = self.0.lock();
if inner.wake_on_drop {
inner.wakers.drain().for_each(|(_, waker)| waker.wake());
}
}
}
pub struct Waiter {
sh: Arc<Mutex<Inner>>,
id: Option<u32>
}
impl Waiter {
pub fn prime(&mut self, ctx: &mut Context<'_>) {
let mut g = self.sh.lock();
let id = loop {
g.idgen = g.idgen.wrapping_add(1);
if !g.wakers.contains_key(&g.idgen) {
break g.idgen;
}
};
self.id = Some(id);
g.wakers.insert(id, ctx.waker().clone());
}
}
impl Drop for Waiter {
fn drop(&mut self) {
let mut g = self.sh.lock();
if let Some(id) = self.id {
g.wakers.remove(&id);
}
}
}