Skip to main content

net_lattice_platform/
event_provider.rs

1use std::sync::{Arc, Mutex, mpsc};
2use std::time::Duration;
3
4use net_lattice_core::{Error, Result};
5
6/// Default number of ordinary events buffered for one synchronous watcher.
7pub const DEFAULT_EVENT_QUEUE_CAPACITY: usize = 256;
8
9/// Non-blocking backend producer for a bounded event receiver.
10#[derive(Clone)]
11pub struct EventSender<E> {
12    sender: mpsc::SyncSender<Result<E>>,
13    pending_resync: Arc<Mutex<Option<E>>>,
14}
15
16impl<E> EventSender<E> {
17    /// Never blocks a native callback. On overflow, records one resync event
18    /// that is delivered before a later ordinary event.
19    pub fn send(&self, event: E, resync: E) -> bool {
20        let mut pending = self.pending_resync.lock().expect("event sender poisoned");
21        if let Some(resync) = pending.take() {
22            match self.sender.try_send(Ok(resync)) {
23                Ok(()) => {}
24                Err(mpsc::TrySendError::Full(Ok(resync))) => {
25                    *pending = Some(resync);
26                    return true;
27                }
28                Err(mpsc::TrySendError::Disconnected(_)) => return false,
29                Err(mpsc::TrySendError::Full(Err(_))) => unreachable!(),
30            }
31        }
32        match self.sender.try_send(Ok(event)) {
33            Ok(()) => true,
34            Err(mpsc::TrySendError::Full(Ok(_))) => {
35                *pending = Some(resync);
36                true
37            }
38            Err(mpsc::TrySendError::Disconnected(_)) => false,
39            Err(mpsc::TrySendError::Full(Err(_))) => unreachable!(),
40        }
41    }
42    pub fn send_error(&self, error: Error) -> bool {
43        self.sender.send(Err(error)).is_ok()
44    }
45}
46
47/// A bounded synchronous receiver of network change events.
48///
49/// Receivers are normally created through the facade's `Lattice::watch` or
50/// `Lattice::watch_filtered` methods.
51///
52/// [`Self::recv`] blocks, while [`Self::try_recv`] and
53/// [`Self::recv_timeout`] do not wait indefinitely. Dropping the receiver
54/// also drops any backend-owned subscription guard, allowing its native
55/// watcher to stop. If its producer shuts down, receive methods return
56/// [`Error::Disconnected`]. When a slow consumer fills the bounded queue,
57/// multiple dropped events are coalesced into one resynchronization event
58/// delivered before a later ordinary event.
59///
60/// `EventReceiver<E>` is `Send` when `E` is `Send`; it is not cloneable, so a
61/// watcher has one consuming receiver. It is not guaranteed to be [`Sync`].
62///
63/// Also implements [`Iterator`], yielding the same [`Result`] values as
64/// [`Self::recv`]. Iteration ends only after the watcher disconnects.
65pub struct EventReceiver<E> {
66    receiver: mpsc::Receiver<Result<E>>,
67    // Owns backend-specific cancellation state (for example, a Windows IP
68    // Helper registration or a route-socket reader). It is intentionally
69    // opaque: consumers only receive events, while dropping the receiver
70    // reliably tears down the native subscription.
71    _subscription: Option<Box<dyn Send>>,
72}
73
74impl<E> EventReceiver<E> {
75    /// Creates a bounded event channel using the default capacity.
76    ///
77    /// This constructor is intended for backend implementations. Applications
78    /// normally obtain an `EventReceiver` through `Lattice::watch` or
79    /// `Lattice::watch_filtered`. The returned sender applies Net Lattice's
80    /// bounded-delivery and overflow semantics.
81    pub fn bounded() -> (EventSender<E>, Self) {
82        Self::bounded_with_capacity(DEFAULT_EVENT_QUEUE_CAPACITY)
83    }
84
85    /// Creates a bounded event channel with the requested capacity.
86    ///
87    /// This constructor is intended for backend implementations. Applications
88    /// normally obtain an `EventReceiver` through `Lattice::watch` or
89    /// `Lattice::watch_filtered`.
90    ///
91    /// # Panics
92    ///
93    /// Panics if `capacity` is zero.
94    pub fn bounded_with_capacity(capacity: usize) -> (EventSender<E>, Self) {
95        assert!(capacity > 0, "event queue capacity must be non-zero");
96        let (sender, receiver) = mpsc::sync_channel(capacity);
97        (
98            EventSender {
99                sender,
100                pending_resync: Arc::new(Mutex::new(None)),
101            },
102            Self {
103                receiver,
104                _subscription: None,
105            },
106        )
107    }
108    /// Wraps a channel receiver that a backend watcher thread or task sends
109    /// events into.
110    ///
111    /// This constructor is intended for backend implementations. Applications
112    /// normally obtain an `EventReceiver` through `Lattice::watch` or
113    /// `Lattice::watch_filtered`.
114    pub fn from_channel_receiver(receiver: mpsc::Receiver<Result<E>>) -> Self {
115        Self {
116            receiver,
117            _subscription: None,
118        }
119    }
120
121    /// Wraps a channel receiver and attaches a backend-owned subscription
122    /// guard.
123    ///
124    /// The guard is retained for the lifetime of the returned receiver.
125    /// Dropping the receiver drops the guard, allowing the associated native
126    /// subscription to stop.
127    ///
128    /// This constructor is intended for backend implementations. Applications
129    /// normally obtain an `EventReceiver` through `Lattice::watch` or
130    /// `Lattice::watch_filtered`.
131    pub fn from_receiver_with_subscription<S>(
132        receiver: mpsc::Receiver<Result<E>>,
133        subscription: S,
134    ) -> Self
135    where
136        S: Send + 'static,
137    {
138        Self {
139            receiver,
140            _subscription: Some(Box::new(subscription)),
141        }
142    }
143
144    /// Attaches a backend-owned subscription guard to this receiver.
145    ///
146    /// The guard is retained for the lifetime of the receiver and dropped
147    /// when the receiver is dropped. If a guard is already attached, it is
148    /// dropped and replaced by the new guard.
149    ///
150    /// This method is intended for backend implementations.
151    pub fn with_subscription<S>(mut self, subscription: S) -> Self
152    where
153        S: Send + 'static,
154    {
155        self._subscription = Some(Box::new(subscription));
156        self
157    }
158
159    /// Blocks until the next event is available.
160    ///
161    /// A temporarily empty queue does not cause this method to return. The
162    /// receiver retains any attached subscription guard throughout the wait.
163    /// Returns [`Error::Disconnected`] after the backend watcher has stopped
164    /// and no further events can arrive. Other producer errors are propagated
165    /// unchanged.
166    pub fn recv(&self) -> Result<E> {
167        self.receiver.recv().map_err(|_| Error::Disconnected)?
168    }
169
170    /// Attempts to receive an event without blocking.
171    ///
172    /// Returns `Ok(Some(event))` when an event is already queued, `Ok(None)`
173    /// when no event is currently available, or [`Error::Disconnected`] when
174    /// the watcher has stopped and no further events can arrive. A temporarily
175    /// empty queue is not an error. Other producer errors are propagated
176    /// unchanged.
177    pub fn try_recv(&self) -> Result<Option<E>> {
178        match self.receiver.try_recv() {
179            Ok(Ok(event)) => Ok(Some(event)),
180            Ok(Err(error)) => Err(error),
181            Err(mpsc::TryRecvError::Empty) => Ok(None),
182            Err(mpsc::TryRecvError::Disconnected) => Err(Error::Disconnected),
183        }
184    }
185
186    /// Waits for an event for at most `timeout`.
187    ///
188    /// Returns `Ok(Some(event))` when an event arrives before the timeout,
189    /// `Ok(None)` when the timeout expires, or [`Error::Disconnected`] when
190    /// the watcher has stopped and no further events can arrive. A timeout is
191    /// not an error. Other producer errors are propagated unchanged.
192    pub fn recv_timeout(&self, timeout: Duration) -> Result<Option<E>> {
193        match self.receiver.recv_timeout(timeout) {
194            Ok(Ok(event)) => Ok(Some(event)),
195            Ok(Err(error)) => Err(error),
196            Err(mpsc::RecvTimeoutError::Timeout) => Ok(None),
197            Err(mpsc::RecvTimeoutError::Disconnected) => Err(Error::Disconnected),
198        }
199    }
200}
201
202/// Iterates over events until the backend watcher disconnects.
203///
204/// Calling [`Iterator::next`] blocks in the same way as [`Self::recv`].
205/// Receiver errors are yielded to the caller as `Err` values.
206impl<E> Iterator for EventReceiver<E> {
207    type Item = Result<E>;
208
209    fn next(&mut self) -> Option<Result<E>> {
210        match self.recv() {
211            Ok(event) => Some(Ok(event)),
212            Err(Error::Disconnected) => None,
213            Err(error) => Some(Err(error)),
214        }
215    }
216}
217
218/// Subscribes to filtered change notifications for the domains and objects a
219/// backend supports.
220///
221/// Generic over an associated `Event` type rather than naming
222/// `net_lattice_model::event::Event` directly — `net-lattice-platform` does
223/// not depend on `net-lattice-model` (see ARCHITECTURE.md). The facade
224/// crate (`net-lattice`) is what constrains `Event` to the concrete model
225/// type.
226///
227/// Unlike the other provider traits, this one is inherently push-based:
228/// `watch` starts a backend-owned background watcher (a Netlink multicast
229/// subscription, a BSD routing-socket reader, a Windows
230/// `NotifyRouteChange2`-style callback, ...) and returns an
231/// [`EventReceiver`] fed by it. The watcher runs for as long as the
232/// returned `EventReceiver` (and whatever the backend keeps alive to feed
233/// it) is alive.
234pub trait EventProvider {
235    type Event;
236    type EventFilter;
237
238    fn watch(&self) -> Result<EventReceiver<Self::Event>>;
239    fn watch_filtered(&self, filter: Self::EventFilter) -> Result<EventReceiver<Self::Event>>;
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use std::sync::atomic::{AtomicUsize, Ordering};
246    use std::thread;
247
248    struct DropGuard(Arc<AtomicUsize>);
249
250    impl Drop for DropGuard {
251        fn drop(&mut self) {
252            self.0.fetch_add(1, Ordering::SeqCst);
253        }
254    }
255
256    #[test]
257    fn recv_returns_disconnected_once_the_sender_is_dropped() {
258        let (sender, receiver) = EventReceiver::<u32>::bounded();
259        drop(sender);
260        assert!(matches!(receiver.recv(), Err(Error::Disconnected)));
261    }
262
263    #[test]
264    fn try_recv_returns_none_when_empty_but_still_connected() {
265        let (_sender, receiver) = EventReceiver::<u32>::bounded();
266        assert!(matches!(receiver.try_recv(), Ok(None)));
267    }
268
269    #[test]
270    fn iterator_ends_when_the_sender_is_dropped() {
271        let (sender, receiver) = EventReceiver::<u32>::bounded();
272        thread::spawn(move || {
273            assert!(sender.send(1, 0));
274            assert!(sender.send(2, 0));
275        });
276        let received: Vec<Result<u32>> = receiver.collect();
277        assert!(matches!(received.as_slice(), [Ok(1), Ok(2)]));
278    }
279
280    #[test]
281    fn iterator_yields_a_producer_error() {
282        let (sender, mut receiver) = EventReceiver::<u32>::bounded();
283        assert!(sender.send_error(Error::InvalidState));
284        assert!(matches!(receiver.next(), Some(Err(Error::InvalidState))));
285    }
286
287    #[test]
288    fn dropping_receiver_drops_subscription_guard() {
289        let drops = Arc::new(AtomicUsize::new(0));
290        let (_sender, receiver) = EventReceiver::<u32>::bounded();
291        drop(receiver.with_subscription(DropGuard(Arc::clone(&drops))));
292        assert_eq!(drops.load(Ordering::SeqCst), 1);
293    }
294
295    #[test]
296    fn replacing_subscription_drops_the_previous_guard() {
297        let first = Arc::new(AtomicUsize::new(0));
298        let second = Arc::new(AtomicUsize::new(0));
299        let (_sender, receiver) = EventReceiver::<u32>::bounded();
300        let receiver = receiver.with_subscription(DropGuard(Arc::clone(&first)));
301        let receiver = receiver.with_subscription(DropGuard(Arc::clone(&second)));
302        assert_eq!(first.load(Ordering::SeqCst), 1);
303        assert_eq!(second.load(Ordering::SeqCst), 0);
304        drop(receiver);
305        assert_eq!(second.load(Ordering::SeqCst), 1);
306    }
307
308    #[test]
309    #[should_panic(expected = "event queue capacity must be non-zero")]
310    fn zero_capacity_is_rejected() {
311        let _ = EventReceiver::<u32>::bounded_with_capacity(0);
312    }
313
314    #[test]
315    fn recv_timeout_returns_none_on_timeout_without_disconnecting() {
316        let (sender, receiver) = EventReceiver::<u32>::bounded();
317        assert!(matches!(
318            receiver.recv_timeout(Duration::from_millis(10)),
319            Ok(None)
320        ));
321        assert!(sender.send(7, 0));
322        assert_eq!(
323            receiver.recv_timeout(Duration::from_secs(1)).unwrap(),
324            Some(7)
325        );
326    }
327
328    #[test]
329    fn overflow_delivers_resync_before_a_later_event() {
330        let (sender, receiver) = EventReceiver::bounded_with_capacity(1);
331        assert!(sender.send(1, 99));
332        assert!(sender.send(2, 99));
333        assert_eq!(receiver.recv().unwrap(), 1);
334        assert!(sender.send(3, 99));
335        assert_eq!(receiver.recv().unwrap(), 99);
336    }
337
338    #[test]
339    fn background_error_is_returned() {
340        let (sender, receiver) = EventReceiver::<u32>::bounded();
341        assert!(sender.send_error(Error::InvalidState));
342        assert!(matches!(receiver.recv(), Err(Error::InvalidState)));
343    }
344}