use std::{
fmt,
ops::Not,
sync::{Arc, Mutex},
task::{Context, Poll},
};
use tokio::sync::mpsc;
pub struct RoundRobinBus<T> {
tx: mpsc::UnboundedSender<Node<T>>,
rx: mpsc::UnboundedReceiver<Node<T>>,
}
impl<T: MaybeReady> RoundRobinBus<T> {
pub fn register_node(&self, value: T) -> Node<T> {
Node(Arc::new(NodeInner {
tx: self.tx.downgrade(),
value: Mutex::new((value, false)),
}))
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<DequeuedNode<T>> {
self.rx
.poll_recv(cx)
.map(|node| node.expect("this channel is never closed").0)
.map(DequeuedNode)
}
}
impl<T> Default for RoundRobinBus<T> {
fn default() -> Self {
let (tx, rx) = mpsc::unbounded_channel();
Self { tx, rx }
}
}
impl<T> fmt::Debug for RoundRobinBus<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RoundRobinBus")
.field("len", &self.rx.len())
.finish()
}
}
#[derive(Debug)]
pub struct Node<T>(Arc<NodeInner<T>>);
impl<T: MaybeReady> Node<T> {
pub fn inspect<R, F>(&self, with: F) -> R
where
F: FnOnce(&mut T) -> R,
{
with(&mut self.0.value.lock().unwrap().0)
}
pub fn modify<R, F>(&self, with: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut guard = self.0.value.lock().unwrap();
let result = with(&mut guard.0);
if guard.1 || guard.0.is_ready().not() {
return result;
}
guard.1 = true;
drop(guard);
if let Some(tx) = self.0.tx.upgrade() {
let _ = tx.send(self.clone());
}
result
}
}
impl<T> Clone for Node<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
#[derive(Debug)]
pub struct DequeuedNode<T>(Arc<NodeInner<T>>);
impl<T: MaybeReady> DequeuedNode<T> {
pub fn modify<R, F>(self, with: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut guard = self.0.value.lock().unwrap();
let result = with(&mut guard.0);
if guard.0.is_ready().not() {
guard.1 = false;
return result;
}
drop(guard);
if let Some(tx) = self.0.tx.upgrade() {
let _ = tx.send(Node(self.0));
}
result
}
}
#[derive(Debug)]
struct NodeInner<T> {
tx: mpsc::WeakUnboundedSender<Node<T>>,
value: Mutex<(T, bool)>,
}
pub trait MaybeReady {
fn is_ready(&self) -> bool;
}