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(Ok(resync))) => {
*pending = Some(resync);
return true;
}
Err(mpsc::TrySendError::Disconnected(_)) => return false,
Err(mpsc::TrySendError::Full(Err(_))) => unreachable!(),
}
}
match self.sender.try_send(Ok(event)) {
Ok(()) => true,
Err(mpsc::TrySendError::Full(Ok(_))) => {
*pending = Some(resync);
true
}
Err(mpsc::TrySendError::Disconnected(_)) => false,
Err(mpsc::TrySendError::Full(Err(_))) => unreachable!(),
}
}
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 new(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> {
self.receiver.recv().map_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 = E;
fn next(&mut self) -> Option<E> {
self.recv().ok()
}
}
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!(matches!(receiver.recv(), Err(Error::Disconnected)));
}
#[test]
fn try_recv_returns_none_when_empty_but_still_connected() {
let (_sender, receiver) = EventReceiver::<u32>::bounded();
assert!(matches!(receiver.try_recv(), Ok(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<u32> = receiver.collect();
assert_eq!(received, vec![1, 2]);
}
#[test]
fn iterator_terminates_on_a_producer_error() {
let (sender, mut receiver) = EventReceiver::<u32>::bounded();
assert!(sender.send_error(Error::InvalidState));
assert_eq!(receiver.next(), None);
}
#[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!(matches!(
receiver.recv_timeout(Duration::from_millis(10)),
Ok(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!(matches!(receiver.recv(), Err(Error::InvalidState)));
}
}