use std::future::Future;
use std::sync::{
atomic::{fence, AtomicUsize, Ordering},
Arc,
};
use tokio_02::sync::mpsc;
#[derive(Debug)]
pub(super) struct Rx {
rx: mpsc::UnboundedReceiver<()>,
spawned: Arc<AtomicUsize>,
}
#[derive(Clone, Debug)]
pub(super) struct Idle {
tx: mpsc::UnboundedSender<()>,
spawned: Arc<AtomicUsize>,
}
pub(super) struct Track(Idle);
impl Idle {
pub(super) fn new() -> (Self, Rx) {
let (tx, rx) = mpsc::unbounded_channel();
let this = Self {
tx,
spawned: Arc::new(AtomicUsize::new(0)),
};
let rx = Rx {
rx,
spawned: this.spawned.clone(),
};
(this, rx)
}
pub(super) fn reserve(&self) -> Track {
self.spawned.fetch_add(1, Ordering::Relaxed);
Track(self.clone())
}
}
impl Rx {
pub(super) async fn idle(&mut self) {
while self.spawned.load(Ordering::Acquire) != 0 {
let _ = self.rx.recv().await;
}
}
}
impl Track {
pub(super) async fn with<T>(self, f: impl Future<Output = T>) -> T {
let result = f.await;
let spawned = self.0.spawned.fetch_sub(1, Ordering::Release);
if spawned == 1 {
fence(Ordering::Acquire);
let _ = self.0.tx.send(());
}
result
}
}