use std::sync::{Arc, Mutex, mpsc};
use std::time::Duration;
use net_lattice_core::{Error, Result};
pub const DEFAULT_EVENT_QUEUE_CAPACITY: usize = 256;
#[derive(Clone)]
pub struct EventSender<E> {
sender: mpsc::SyncSender<Result<E>>,
pending_resync: Arc<Mutex<Option<E>>>,
}
impl<E> EventSender<E> {
pub fn send(&self, event: E, resync: E) -> bool {
let mut pending = self.pending_resync.lock().expect("event sender poisoned");
if let Some(resync) = pending.take() {
match self.sender.try_send(Ok(resync)) {
Ok(()) => {}
Err(mpsc::TrySendError::Full(event)) => {
let _ = event.ok().map(|event| *pending = Some(event));
return true;
}
Err(mpsc::TrySendError::Disconnected(_)) => return false,
}
}
match self.sender.try_send(Ok(event)) {
Ok(()) => true,
Err(mpsc::TrySendError::Full(_)) => {
*pending = Some(resync);
true
}
Err(mpsc::TrySendError::Disconnected(_)) => false,
}
}
pub fn send_error(&self, error: Error) -> bool {
self.sender.send(Err(error)).is_ok()
}
}
pub struct EventReceiver<E> {
receiver: mpsc::Receiver<Result<E>>,
_subscription: Option<Box<dyn Send>>,
}
impl<E> EventReceiver<E> {
pub fn bounded() -> (EventSender<E>, Self) {
Self::bounded_with_capacity(DEFAULT_EVENT_QUEUE_CAPACITY)
}
pub fn bounded_with_capacity(capacity: usize) -> (EventSender<E>, Self) {
assert!(capacity > 0, "event queue capacity must be non-zero");
let (sender, receiver) = mpsc::sync_channel(capacity);
(
EventSender {
sender,
pending_resync: Arc::new(Mutex::new(None)),
},
Self {
receiver,
_subscription: None,
},
)
}
pub fn from_channel_receiver(receiver: mpsc::Receiver<Result<E>>) -> Self {
Self {
receiver,
_subscription: None,
}
}
pub fn from_receiver_with_subscription<S>(
receiver: mpsc::Receiver<Result<E>>,
subscription: S,
) -> Self
where
S: Send + 'static,
{
Self {
receiver,
_subscription: Some(Box::new(subscription)),
}
}
pub fn with_subscription<S>(mut self, subscription: S) -> Self
where
S: Send + 'static,
{
self._subscription = Some(Box::new(subscription));
self
}
pub fn recv(&self) -> Result<E> {
match self.receiver.recv() {
Ok(Ok(event)) => Ok(event),
Ok(Err(error)) => Err(error),
Err(_) => Err(Error::Disconnected),
}
}
pub fn try_recv(&self) -> Result<Option<E>> {
match self.receiver.try_recv() {
Ok(Ok(event)) => Ok(Some(event)),
Ok(Err(error)) => Err(error),
Err(mpsc::TryRecvError::Empty) => Ok(None),
Err(mpsc::TryRecvError::Disconnected) => Err(Error::Disconnected),
}
}
pub fn recv_timeout(&self, timeout: Duration) -> Result<Option<E>> {
match self.receiver.recv_timeout(timeout) {
Ok(Ok(event)) => Ok(Some(event)),
Ok(Err(error)) => Err(error),
Err(mpsc::RecvTimeoutError::Timeout) => Ok(None),
Err(mpsc::RecvTimeoutError::Disconnected) => Err(Error::Disconnected),
}
}
}
impl<E> Iterator for EventReceiver<E> {
type Item = Result<E>;
fn next(&mut self) -> Option<Result<E>> {
match self.recv() {
Ok(event) => Some(Ok(event)),
Err(Error::Disconnected) => None,
Err(error) => Some(Err(error)),
}
}
}
pub trait EventProvider {
type Event;
type EventFilter;
fn watch(&self) -> Result<EventReceiver<Self::Event>>;
fn watch_filtered(&self, filter: Self::EventFilter) -> Result<EventReceiver<Self::Event>>;
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
struct DropGuard(Arc<AtomicUsize>);
impl Drop for DropGuard {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
#[test]
fn recv_returns_disconnected_once_the_sender_is_dropped() {
let (sender, receiver) = EventReceiver::<u32>::bounded();
drop(sender);
assert!(receiver.recv().is_err());
}
#[test]
fn recv_reports_disconnect_after_the_sender_is_dropped() {
let (sender, receiver) = EventReceiver::<u32>::bounded();
drop(sender);
assert!(receiver.recv().is_err());
}
#[test]
fn pending_resync_preserves_a_queued_producer_error() {
let (sender, receiver) = EventReceiver::bounded_with_capacity(1);
assert!(sender.send(1, 99));
assert!(sender.send(2, 99));
assert_eq!(receiver.recv().unwrap(), 1);
assert!(sender.send_error(Error::InvalidState));
assert!(sender.send(3, 99));
assert!(receiver.recv().is_err());
}
#[test]
fn try_recv_returns_none_when_empty_but_still_connected() {
let (_sender, receiver) = EventReceiver::<u32>::bounded();
assert!(receiver.try_recv().unwrap().is_none());
}
#[test]
fn iterator_ends_when_the_sender_is_dropped() {
let (sender, receiver) = EventReceiver::<u32>::bounded();
thread::spawn(move || {
assert!(sender.send(1, 0));
assert!(sender.send(2, 0));
});
let received: Vec<Result<u32>> = receiver.collect();
assert_eq!(received.len(), 2);
assert_eq!(received[0].as_ref().unwrap(), &1);
assert_eq!(received[1].as_ref().unwrap(), &2);
}
#[test]
fn iterator_yields_a_producer_error() {
let (sender, mut receiver) = EventReceiver::<u32>::bounded();
assert!(sender.send_error(Error::InvalidState));
assert!(receiver.next().unwrap().is_err());
}
#[test]
fn dropping_receiver_drops_subscription_guard() {
let drops = Arc::new(AtomicUsize::new(0));
let (_sender, receiver) = EventReceiver::<u32>::bounded();
drop(receiver.with_subscription(DropGuard(Arc::clone(&drops))));
assert_eq!(drops.load(Ordering::SeqCst), 1);
}
#[test]
fn replacing_subscription_drops_the_previous_guard() {
let first = Arc::new(AtomicUsize::new(0));
let second = Arc::new(AtomicUsize::new(0));
let (_sender, receiver) = EventReceiver::<u32>::bounded();
let receiver = receiver.with_subscription(DropGuard(Arc::clone(&first)));
let receiver = receiver.with_subscription(DropGuard(Arc::clone(&second)));
assert_eq!(first.load(Ordering::SeqCst), 1);
assert_eq!(second.load(Ordering::SeqCst), 0);
drop(receiver);
assert_eq!(second.load(Ordering::SeqCst), 1);
}
#[test]
#[should_panic(expected = "event queue capacity must be non-zero")]
fn zero_capacity_is_rejected() {
let _ = EventReceiver::<u32>::bounded_with_capacity(0);
}
#[test]
fn recv_timeout_returns_none_on_timeout_without_disconnecting() {
let (sender, receiver) = EventReceiver::<u32>::bounded();
assert!(
receiver
.recv_timeout(Duration::from_millis(10))
.unwrap()
.is_none()
);
assert!(sender.send(7, 0));
assert_eq!(
receiver.recv_timeout(Duration::from_secs(1)).unwrap(),
Some(7)
);
}
#[test]
fn overflow_delivers_resync_before_a_later_event() {
let (sender, receiver) = EventReceiver::bounded_with_capacity(1);
assert!(sender.send(1, 99));
assert!(sender.send(2, 99));
assert_eq!(receiver.recv().unwrap(), 1);
assert!(sender.send(3, 99));
assert_eq!(receiver.recv().unwrap(), 99);
}
#[test]
fn background_error_is_returned() {
let (sender, receiver) = EventReceiver::<u32>::bounded();
assert!(sender.send_error(Error::InvalidState));
assert!(receiver.recv().is_err());
}
#[test]
fn backend_channel_constructors_preserve_events_and_guards() {
let (sender, raw_receiver) = mpsc::channel();
assert!(sender.send(Ok(7_u32)).is_ok());
let receiver = EventReceiver::from_channel_receiver(raw_receiver);
assert_eq!(receiver.recv().unwrap(), 7);
let drops = Arc::new(AtomicUsize::new(0));
let (_sender, raw_receiver) = mpsc::channel::<Result<u32>>();
drop(EventReceiver::from_receiver_with_subscription(
raw_receiver,
DropGuard(Arc::clone(&drops)),
));
assert_eq!(drops.load(Ordering::SeqCst), 1);
}
#[test]
fn sender_reports_disconnected_consumer() {
let (sender, receiver) = EventReceiver::<u32>::bounded_with_capacity(1);
drop(receiver);
assert!(!sender.send(1, 0));
assert!(!sender.send_error(Error::InvalidState));
}
#[test]
fn sender_reports_disconnected_string_consumer() {
let (sender, receiver) = EventReceiver::<String>::bounded_with_capacity(1);
drop(receiver);
assert!(!sender.send("event".to_owned(), "resync".to_owned()));
}
#[test]
fn string_sender_covers_pending_and_error_paths() {
let (sender, receiver) = EventReceiver::<String>::bounded_with_capacity(1);
assert!(sender.send("first".into(), "resync".into()));
assert!(sender.send("second".into(), "resync".into()));
assert!(sender.send("third".into(), "resync".into()));
assert_eq!(receiver.recv().unwrap(), "first");
assert!(sender.send("fourth".into(), "resync".into()));
assert_eq!(receiver.recv().unwrap(), "resync");
assert!(sender.send_error(Error::InvalidState));
assert!(receiver.recv().is_err());
let (sender, receiver) = EventReceiver::<String>::bounded();
drop(sender);
assert!(receiver.recv().is_err());
}
#[test]
fn default_integer_sender_covers_pending_full_and_disconnect() {
let (sender, receiver) = EventReceiver::bounded_with_capacity(1);
assert!(sender.send(1, 99));
assert!(sender.send(2, 99));
assert!(sender.send(3, 99));
drop(receiver);
assert!(!sender.send(4, 99));
let (sender, receiver) = EventReceiver::<i32>::bounded();
drop(sender);
assert!(receiver.recv().is_err());
}
fn exercise_sender_paths<E: Clone>(event: E, resync: E) {
let (sender, receiver) = EventReceiver::bounded_with_capacity(1);
assert!(sender.send(event.clone(), resync.clone()));
assert!(sender.send(event.clone(), resync.clone()));
assert!(sender.send(event.clone(), resync.clone()));
assert!(receiver.recv().is_ok());
assert!(sender.send(event.clone(), resync.clone()));
assert!(receiver.recv().is_ok());
drop(receiver);
assert!(!sender.send(event, resync));
}
#[test]
fn sender_paths_cover_common_generic_instantiations() {
exercise_sender_paths::<u8>(1, 9);
exercise_sender_paths::<u16>(1, 9);
exercise_sender_paths::<u32>(1, 9);
exercise_sender_paths::<u64>(1, 9);
exercise_sender_paths::<i32>(1, 9);
exercise_sender_paths::<usize>(1, 9);
exercise_sender_paths::<()>((), ());
}
#[test]
fn receive_methods_propagate_queued_errors_and_disconnects() {
let (sender, receiver) = EventReceiver::<u32>::bounded();
assert!(sender.send_error(Error::InvalidState));
assert!(receiver.try_recv().is_err());
drop(sender);
assert!(receiver.try_recv().is_err());
let (sender, receiver) = EventReceiver::<u32>::bounded();
assert!(sender.send_error(Error::InvalidState));
assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
drop(sender);
assert!(receiver.recv_timeout(Duration::ZERO).is_err());
}
#[test]
fn pending_resync_handles_full_and_disconnected_channels() {
let (sender, receiver) = EventReceiver::bounded_with_capacity(1);
assert!(sender.send(1, 99));
assert!(sender.send(2, 99));
assert!(sender.send(3, 99));
drop(receiver);
assert!(!sender.send(4, 99));
}
#[test]
fn try_recv_returns_an_already_queued_event() {
let (sender, receiver) = EventReceiver::bounded();
assert!(sender.send(7_u32, 0));
assert_eq!(receiver.try_recv().unwrap(), Some(7));
}
#[test]
#[should_panic(expected = "event sender poisoned")]
fn poisoned_event_sender_mutex_is_reported() {
let (sender, _receiver) = EventReceiver::<u32>::bounded();
let pending = Arc::clone(&sender.pending_resync);
let _ = std::thread::spawn(move || {
let _guard = pending.lock().unwrap();
panic!("poison event sender");
})
.join();
let _ = sender.send(1, 0);
}
}