use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::Notify;
struct Inner {
count: AtomicUsize,
notify: Notify,
}
pub struct WaitCounter {
inner: Arc<Inner>,
}
impl WaitCounter {
pub fn new() -> Self {
let inner = Inner {
count: AtomicUsize::new(1),
notify: Notify::new(),
};
Self {
inner: Arc::new(inner),
}
}
pub fn wake_clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
pub async fn wait(&self) {
loop {
let current = self.inner.count.load(Ordering::Acquire);
if current == 1 {
break;
}
self.inner.notify.notified().await;
}
}
}
impl Clone for WaitCounter {
fn clone(&self) -> Self {
self.inner.count.fetch_add(1, Ordering::Relaxed);
Self {
inner: self.inner.clone(),
}
}
}
impl Drop for WaitCounter {
fn drop(&mut self) {
let prev = self.inner.count.fetch_sub(1, Ordering::Release);
if prev == 2 {
self.inner.notify.notify_waiters();
}
}
}
#[cfg(test)]
mod test {
use crate::WaitCounter;
use std::time::Duration;
#[tokio::test]
async fn test_wait_counter() {
let counter = WaitCounter::new();
let cloned = counter.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(1000)).await;
drop(cloned);
});
counter.wait().await;
println!("Counter reached 1");
}
}