use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use tokio::sync::Notify;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use super::completion::{OutcomeTx, RemovalCompletion};
use crate::{core::actor::ActorExitReason, identity::TaskId};
pub(super) struct Handle {
pub(super) join: JoinHandle<ActorExitReason>,
pub(super) cancel: CancellationToken,
pub(super) done: Option<OutcomeTx>,
pub(super) completion: RemovalCompletion,
}
pub(super) enum EntryState {
Registered(Handle),
Removing { completion: RemovalCompletion },
}
pub(super) struct Entry {
pub(super) label: Arc<str>,
pub(super) state: EntryState,
}
#[derive(Default)]
pub(super) struct Inner {
pub(super) tasks: HashMap<TaskId, Entry>,
pub(super) by_label: HashMap<Arc<str>, TaskId>,
}
#[derive(Default)]
struct PendingInner {
counts: HashMap<TaskId, usize>,
labels: HashMap<TaskId, Arc<str>>,
}
#[derive(Default)]
pub(super) struct PendingJoins {
inner: Mutex<PendingInner>,
drained: Notify,
}
impl PendingJoins {
pub(super) fn inc(&self, id: TaskId) {
let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner());
*g.counts.entry(id).or_insert(0) += 1;
}
pub(super) fn label(&self, id: TaskId, label: Arc<str>) {
let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner());
if g.counts.contains_key(&id) {
g.labels.insert(id, label);
}
}
pub(super) fn dec(&self, id: TaskId) {
let mut g = self.inner.lock().unwrap_or_else(|e| e.into_inner());
if let Some(n) = g.counts.get_mut(&id) {
*n -= 1;
if *n == 0 {
g.counts.remove(&id);
g.labels.remove(&id);
}
}
if g.counts.is_empty() {
self.drained.notify_waiters();
}
}
#[cfg(test)]
pub(super) fn contains(&self, id: TaskId) -> bool {
self.inner
.lock()
.unwrap_or_else(|e| e.into_inner())
.counts
.contains_key(&id)
}
pub(super) fn is_empty(&self) -> bool {
self.inner
.lock()
.unwrap_or_else(|e| e.into_inner())
.counts
.is_empty()
}
pub(super) fn pending_labels(&self) -> Vec<Arc<str>> {
self.inner
.lock()
.unwrap_or_else(|e| e.into_inner())
.labels
.values()
.cloned()
.collect()
}
pub(super) async fn wait_drained(&self) {
loop {
let notified = self.drained.notified();
tokio::pin!(notified);
notified.as_mut().enable();
if self.is_empty() {
return;
}
notified.await;
}
}
}