#![allow(dead_code)]
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
use std::time::Duration;
use ruststream::runtime::FromRef;
use tokio::runtime::{Handle, RuntimeFlavor};
use tokio::sync::Notify;
pub(crate) const GUARD: Duration = Duration::from_secs(30);
#[derive(Clone, Debug, Default)]
pub(crate) struct Probe(Arc<Inner>);
impl FromRef<Self> for Probe {
fn from_ref(state: &Self) -> Self {
state.clone()
}
}
#[derive(Debug, Default)]
struct Inner {
handled: AtomicUsize,
order: Mutex<Vec<(String, i64)>>,
kept: Mutex<Vec<Box<dyn Any + Send>>>,
open: AtomicBool,
opened: Notify,
wake: Notify,
}
impl Probe {
pub(crate) fn handled(&self, key: &str, id: i64) {
assert_eq!(
Handle::current().runtime_flavor(),
RuntimeFlavor::CurrentThread,
"the handler runs on a dedicated thread"
);
self.0
.order
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push((key.to_owned(), id));
self.0.handled.fetch_add(1, Ordering::AcqRel);
self.0.wake.notify_waiters();
}
pub(crate) fn order(&self) -> Vec<(String, i64)> {
self.0
.order
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
pub(crate) async fn gate(&self) {
loop {
let opened = self.0.opened.notified();
tokio::pin!(opened);
opened.as_mut().enable();
if self.0.open.load(Ordering::Acquire) {
return;
}
opened.await;
}
}
pub(crate) fn open_gate(&self) {
self.0.open.store(true, Ordering::Release);
self.0.opened.notify_waiters();
}
pub(crate) fn keep(&self, value: impl Any + Send) {
self.0
.kept
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push(Box::new(value));
}
pub(crate) fn kept(&self) -> usize {
self.0
.kept
.lock()
.unwrap_or_else(PoisonError::into_inner)
.len()
}
pub(crate) fn release(&self) {
self.0
.kept
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clear();
}
pub(crate) async fn reached(&self, count: usize) {
let wait = async {
loop {
let woken = self.0.wake.notified();
tokio::pin!(woken);
woken.as_mut().enable();
if self.0.handled.load(Ordering::Acquire) >= count {
return;
}
woken.await;
}
};
tokio::time::timeout(GUARD, wait).await.unwrap_or_else(|_| {
panic!(
"the handlers took {} deliveries of {count}",
self.0.handled.load(Ordering::Acquire)
)
});
}
}
pub(crate) async fn until(what: &str, mut check: impl AsyncFnMut() -> bool) {
let wait = async {
while !check().await {
tokio::task::yield_now().await;
}
};
tokio::time::timeout(GUARD, wait)
.await
.unwrap_or_else(|_| panic!("{what} within {GUARD:?}"));
}