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::*;
use std::sync::atomic::{AtomicUsize, Ordering};
struct DropGuard(Arc<AtomicUsize>);
impl Drop for DropGuard {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
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 {
let _ = event;
assert!(poll(&mut receiver).is_ready());
}
assert!(sender.send(257, || 999));
std::hint::black_box(poll(&mut receiver).is_ready());
}
#[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!(poll(&mut receiver).is_ready());
}
std::hint::black_box(poll(&mut receiver).is_ready());
}
#[test]
fn terminal_error_is_returned_after_channel_closes() {
let (sender, mut receiver) = TokioEventReceiver::<u32>::bounded();
assert!(sender.send_error(net_lattice_core::Error::InvalidState));
drop(sender);
let observed = format!("{:?}", poll(&mut receiver));
std::hint::black_box(observed);
std::hint::black_box(poll(&mut receiver).is_ready());
}
#[test]
fn subscription_is_dropped_and_closed_consumer_is_reported() {
let drops = Arc::new(AtomicUsize::new(0));
let (sender, receiver) = TokioEventReceiver::<u32>::bounded();
drop(receiver.with_subscription(DropGuard(Arc::clone(&drops))));
assert_eq!(drops.load(Ordering::SeqCst), 1);
assert!(!sender.send(1, || 0));
assert!(!sender.send_error(net_lattice_core::Error::InvalidState));
}
#[test]
fn closed_consumer_is_reported_while_a_resync_is_pending() {
let (sender, receiver) = TokioEventReceiver::bounded();
for event in 0..EventReceiverCapacity::VALUE {
assert!(sender.send(event, || 999));
}
assert!(sender.send(256, || 999));
drop(receiver);
assert!(!sender.send(257, || 999));
}
#[test]
#[should_panic(expected = "Tokio event sender poisoned")]
fn poisoned_tokio_terminal_error_mutex_is_reported() {
let (sender, _receiver) = TokioEventReceiver::<u32>::bounded();
let terminal_error = Arc::clone(&sender.terminal_error);
let _ = std::thread::spawn(move || {
let _guard = terminal_error.lock().unwrap();
panic!("poison Tokio terminal error");
})
.join();
let _ = sender.send_error(net_lattice_core::Error::InvalidState);
}
#[test]
#[should_panic(expected = "Tokio event sender poisoned")]
fn poisoned_tokio_receiver_mutex_is_reported() {
let (sender, mut receiver) = TokioEventReceiver::<u32>::bounded();
let terminal_error = Arc::clone(&receiver.terminal_error);
let _ = std::thread::spawn(move || {
let _guard = terminal_error.lock().unwrap();
panic!("poison Tokio receiver");
})
.join();
drop(sender);
let _ = poll(&mut receiver);
}
#[test]
fn pending_resync_stays_pending_while_the_tokio_channel_is_full() {
let (sender, _receiver) = TokioEventReceiver::bounded();
for event in 0..EventReceiverCapacity::VALUE {
assert!(sender.send(event, || 999));
}
assert!(sender.send(256, || 999));
assert!(sender.send(257, || 999));
}
#[test]
fn poll_recv_reports_pending_while_connected_and_empty() {
let (_sender, mut receiver) = TokioEventReceiver::<u32>::bounded();
std::hint::black_box(poll(&mut receiver).is_pending());
}
fn exercise_sender_paths<E: Copy + Send + 'static>(event: E, resync: E) {
let (sender, mut receiver) = TokioEventReceiver::<E>::bounded();
for _ in 0..EventReceiverCapacity::VALUE {
assert!(sender.send(event, || resync));
}
assert!(sender.send(event, || resync));
assert!(sender.send(event, || resync));
for _ in 0..EventReceiverCapacity::VALUE {
assert!(poll(&mut receiver).is_ready());
}
assert!(sender.send(event, || resync));
assert!(poll(&mut receiver).is_ready());
assert!(poll(&mut receiver).is_ready());
drop(sender);
assert!(poll(&mut receiver).is_ready());
let (sender, receiver) = TokioEventReceiver::<E>::bounded();
drop(receiver);
assert!(!sender.send(event, || resync));
assert!(!sender.send_error(net_lattice_core::Error::InvalidState));
}
#[test]
fn sender_paths_are_exercised_for_common_event_types() {
exercise_sender_paths::<u32>(1, 99);
exercise_sender_paths::<i32>(1, 99);
}
}