rskit_stream/
broadcaster.rs1use std::fmt;
16use std::pin::Pin;
17use std::sync::Arc;
18
19use futures::Stream;
20use parking_lot::Mutex;
21use tokio::sync::mpsc;
22use tokio_stream::wrappers::ReceiverStream;
23use tokio_util::sync::CancellationToken;
24
25pub type BroadcastStream<T> = Pin<Box<dyn Stream<Item = T> + Send + 'static>>;
30
31pub const DEFAULT_BROADCAST_BUFFER: usize = 64;
36
37pub struct Broadcaster<T> {
44 senders: Arc<Mutex<Vec<mpsc::Sender<T>>>>,
45 buffer: usize,
46}
47
48impl<T> Clone for Broadcaster<T> {
49 fn clone(&self) -> Self {
50 Self {
51 senders: Arc::clone(&self.senders),
52 buffer: self.buffer,
53 }
54 }
55}
56
57impl<T> fmt::Debug for Broadcaster<T> {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 f.debug_struct("Broadcaster")
60 .field("buffer", &self.buffer)
61 .field("subscribers", &self.subscriber_count())
62 .finish_non_exhaustive()
63 }
64}
65
66impl<T> Default for Broadcaster<T>
67where
68 T: Clone + Send + 'static,
69{
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl<T> Broadcaster<T>
76where
77 T: Clone + Send + 'static,
78{
79 #[must_use]
82 pub fn new() -> Self {
83 Self::with_buffer(DEFAULT_BROADCAST_BUFFER)
84 }
85
86 #[must_use]
91 pub fn with_buffer(buffer: usize) -> Self {
92 Self {
93 senders: Arc::new(Mutex::new(Vec::new())),
94 buffer: buffer.max(1),
95 }
96 }
97}
98
99impl<T> Broadcaster<T> {
100 #[must_use]
102 pub const fn buffer(&self) -> usize {
103 self.buffer
104 }
105
106 #[must_use]
112 pub fn subscriber_count(&self) -> usize {
113 self.senders.lock().len()
114 }
115
116 pub fn subscribe(&self, cancel: CancellationToken) -> BroadcastStream<T>
122 where
123 T: Send + 'static,
124 {
125 use futures::StreamExt as _;
126
127 let (tx, rx) = mpsc::channel(self.buffer);
128 self.senders.lock().push(tx);
129 Box::pin(ReceiverStream::new(rx).take_until(cancel.cancelled_owned()))
130 }
131
132 pub fn broadcast(&self, item: &T)
138 where
139 T: Clone,
140 {
141 self.senders
142 .lock()
143 .retain(|tx| match tx.try_send(item.clone()) {
144 Ok(()) | Err(mpsc::error::TrySendError::Full(_)) => true,
145 Err(mpsc::error::TrySendError::Closed(_)) => false,
146 });
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use futures::StreamExt as _;
154
155 #[tokio::test]
156 async fn delivers_to_all_subscribers() {
157 let bc = Broadcaster::<u32>::new();
158 let cancel = CancellationToken::new();
159 let mut a = bc.subscribe(cancel.clone());
160 let mut b = bc.subscribe(cancel.clone());
161
162 bc.broadcast(&7);
163
164 assert_eq!(a.next().await, Some(7));
165 assert_eq!(b.next().await, Some(7));
166 }
167
168 #[tokio::test]
169 async fn stream_terminates_on_cancel() {
170 let bc = Broadcaster::<u32>::new();
171 let cancel = CancellationToken::new();
172 let mut sub = bc.subscribe(cancel.clone());
173
174 bc.broadcast(&1);
175 assert_eq!(sub.next().await, Some(1));
176
177 cancel.cancel();
178 assert_eq!(sub.next().await, None);
179 }
180
181 #[tokio::test]
182 async fn dropped_subscriber_is_pruned() {
183 let bc = Broadcaster::<u32>::new();
184 let cancel = CancellationToken::new();
185 let sub = bc.subscribe(cancel);
186 assert_eq!(bc.subscriber_count(), 1);
187
188 drop(sub);
189 bc.broadcast(&1);
191 assert_eq!(bc.subscriber_count(), 0);
192 }
193
194 #[tokio::test]
195 async fn full_subscriber_drops_overflow_without_blocking() {
196 let bc = Broadcaster::<u32>::with_buffer(2);
197 let cancel = CancellationToken::new();
198 let mut sub = bc.subscribe(cancel.clone());
199
200 for i in 0..5 {
202 bc.broadcast(&i);
203 }
204
205 let mut received = Vec::new();
206 while let Ok(Some(v)) =
208 tokio::time::timeout(std::time::Duration::from_millis(50), sub.next()).await
209 {
210 received.push(v);
211 }
212 assert_eq!(received, vec![0, 1]);
213 }
214
215 #[tokio::test]
216 async fn zero_buffer_is_clamped_to_one() {
217 let bc = Broadcaster::<u32>::with_buffer(0);
218 assert_eq!(bc.buffer(), 1);
219 }
220
221 #[tokio::test]
222 async fn clones_share_subscriber_set() {
223 let bc = Broadcaster::<u32>::new();
224 let cancel = CancellationToken::new();
225 let mut sub = bc.subscribe(cancel.clone());
226
227 let clone = bc.clone();
228 clone.broadcast(&42);
229
230 assert_eq!(sub.next().await, Some(42));
231 }
232}