1use crossbeam_channel::{bounded, Receiver, Sender, TrySendError};
2use std::sync::atomic::{AtomicU64, Ordering};
3
4#[derive(Debug)]
6pub struct Bus<M> {
7 sender: Sender<M>,
8 receiver: Receiver<M>,
9 capacity: usize,
10 dropped: AtomicU64,
11}
12
13impl<M: Send + 'static> Bus<M> {
14 pub fn new(capacity: usize) -> Self {
15 let (sender, receiver) = bounded(capacity.max(1));
16 Self {
17 sender,
18 receiver,
19 capacity: capacity.max(1),
20 dropped: AtomicU64::new(0),
21 }
22 }
23
24 pub fn sender(&self) -> BusSender<M> {
25 BusSender {
26 inner: self.sender.clone(),
27 dropped: &self.dropped,
28 }
29 }
30
31 pub fn receiver(&self) -> &Receiver<M> {
32 &self.receiver
33 }
34
35 pub fn capacity(&self) -> usize {
36 self.capacity
37 }
38
39 pub fn dropped_count(&self) -> u64 {
40 self.dropped.load(Ordering::Relaxed)
41 }
42}
43
44#[derive(Debug)]
45pub struct BusSender<M> {
46 inner: Sender<M>,
47 dropped: *const AtomicU64,
48}
49
50impl<M> Clone for BusSender<M> {
51 fn clone(&self) -> Self {
52 Self {
53 inner: self.inner.clone(),
54 dropped: self.dropped,
55 }
56 }
57}
58
59impl<M> BusSender<M> {
60 pub fn send(&self, msg: M) -> Result<(), M> {
61 match self.inner.try_send(msg) {
62 Ok(()) => Ok(()),
63 Err(TrySendError::Full(msg)) => {
64 unsafe {
66 (*self.dropped).fetch_add(1, Ordering::Relaxed);
67 }
68 Err(msg)
69 }
70 Err(TrySendError::Disconnected(msg)) => Err(msg),
71 }
72 }
73
74 pub fn send_blocking(&self, msg: M) -> Result<(), M> {
75 self.inner.send(msg).map_err(|e| e.into_inner())
76 }
77}
78
79unsafe impl<M> Send for BusSender<M> {}
80unsafe impl<M> Sync for BusSender<M> {}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn backpressure_counts_drops() {
88 let bus = Bus::<u32>::new(1);
89 let tx = bus.sender();
90 assert!(tx.send(1).is_ok());
91 let dropped = tx.send(2).unwrap_err();
92 assert_eq!(dropped, 2);
93 assert_eq!(bus.dropped_count(), 1);
94 }
95}