use std::any::Any;
use std::pin::Pin;
use std::sync::{
Arc, Mutex,
atomic::{AtomicBool, Ordering},
};
use std::task::{Context, Poll};
use net_lattice_core::Result;
pub struct TokioEventSender<E> {
sender: tokio::sync::mpsc::Sender<Result<E>>,
resync_pending: Arc<AtomicBool>,
terminal_error: Arc<Mutex<Option<net_lattice_core::Error>>>,
}
pub struct TokioEventReceiver<E> {
receiver: tokio::sync::mpsc::Receiver<Result<E>>,
subscription: Option<Box<dyn Any + Send>>,
terminal_error: Arc<Mutex<Option<net_lattice_core::Error>>>,
}
impl<E> TokioEventReceiver<E> {
pub fn bounded() -> (TokioEventSender<E>, Self) {
let (sender, receiver) = tokio::sync::mpsc::channel(EventReceiverCapacity::VALUE);
let pending = Arc::new(AtomicBool::new(false));
let terminal_error = Arc::new(Mutex::new(None));
(
TokioEventSender {
sender,
resync_pending: pending,
terminal_error: Arc::clone(&terminal_error),
},
Self {
receiver,
subscription: None,
terminal_error,
},
)
}
pub fn with_subscription<S>(mut self, subscription: S) -> Self
where
S: Any + Send + 'static,
{
self.subscription = Some(Box::new(subscription));
self
}
pub fn poll_recv(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<E>>> {
match Pin::new(&mut self.receiver).poll_recv(cx) {
Poll::Ready(None) => Poll::Ready(
self.terminal_error
.lock()
.expect("Tokio event sender poisoned")
.take()
.map(Err),
),
other => other,
}
}
}
impl<E> TokioEventSender<E> {
pub fn send(&self, event: E, resync: impl FnOnce() -> E) -> bool {
if self.resync_pending.swap(false, Ordering::AcqRel) {
match self.sender.try_send(Ok(resync())) {
Ok(()) => {}
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
self.resync_pending.store(true, Ordering::Release);
return true;
}
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => return false,
}
}
match self.sender.try_send(Ok(event)) {
Ok(()) => true,
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
self.resync_pending.store(true, Ordering::Release);
true
}
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => false,
}
}
pub fn send_error(&self, error: net_lattice_core::Error) -> bool {
if self.sender.is_closed() {
return false;
}
*self
.terminal_error
.lock()
.expect("Tokio event sender poisoned") = Some(error);
true
}
}
struct EventReceiverCapacity;
impl EventReceiverCapacity {
const VALUE: usize = 256;
}
#[cfg(test)]
mod tests {
use super::*;
fn poll<E>(receiver: &mut TokioEventReceiver<E>) -> Poll<Option<Result<E>>> {
let waker = std::task::Waker::noop();
let mut context = Context::from_waker(waker);
Pin::new(receiver).poll_recv(&mut context)
}
#[test]
fn overflow_delivers_resync_before_a_later_event() {
let (sender, mut receiver) = TokioEventReceiver::bounded();
for event in 0..EventReceiverCapacity::VALUE {
assert!(sender.send(event, || 999));
}
assert!(sender.send(256, || 999));
for event in 0..EventReceiverCapacity::VALUE {
assert!(matches!(poll(&mut receiver), Poll::Ready(Some(Ok(value))) if value == event));
}
assert!(sender.send(257, || 999));
assert!(matches!(poll(&mut receiver), Poll::Ready(Some(Ok(999)))));
}
#[test]
fn terminal_error_survives_a_full_queue() {
let (sender, mut receiver) = TokioEventReceiver::bounded();
for event in 0..EventReceiverCapacity::VALUE {
assert!(sender.send(event, || 999));
}
assert!(sender.send_error(net_lattice_core::Error::InvalidState));
drop(sender);
for _ in 0..EventReceiverCapacity::VALUE {
assert!(matches!(poll(&mut receiver), Poll::Ready(Some(Ok(_)))));
}
assert!(matches!(
poll(&mut receiver),
Poll::Ready(Some(Err(net_lattice_core::Error::InvalidState)))
));
}
}