1use std::sync::{
8 atomic::{AtomicU64, Ordering},
9 Arc,
10};
11
12use serde::Serialize;
13use tokio::sync::broadcast;
14
15use crate::error::WsError;
16
17pub struct BroadcastHub<T>
42where
43 T: Clone + Serialize + Send + Sync + 'static,
44{
45 tx: broadcast::Sender<T>,
46 connection_count: Arc<AtomicU64>,
47}
48
49impl<T> Clone for BroadcastHub<T>
50where
51 T: Clone + Serialize + Send + Sync + 'static,
52{
53 fn clone(&self) -> Self {
54 Self {
55 tx: self.tx.clone(),
56 connection_count: Arc::clone(&self.connection_count),
57 }
58 }
59}
60
61impl<T> std::fmt::Debug for BroadcastHub<T>
62where
63 T: Clone + Serialize + Send + Sync + 'static,
64{
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct("BroadcastHub")
67 .field("capacity", &self.capacity())
68 .field("connection_count", &self.connection_count())
69 .field("receiver_count", &self.receiver_count())
70 .finish()
71 }
72}
73
74impl<T> BroadcastHub<T>
75where
76 T: Clone + Serialize + Send + Sync + 'static,
77{
78 pub fn new(capacity: usize) -> Self {
80 let (tx, _) = broadcast::channel(capacity);
81 Self {
82 tx,
83 connection_count: Arc::new(AtomicU64::new(0)),
84 }
85 }
86
87 pub fn from_config(config: &crate::config::WsConfig) -> Self {
89 Self::new(config.broadcast_capacity)
90 }
91
92 pub fn subscribe(&self) -> broadcast::Receiver<T> {
94 self.tx.subscribe()
95 }
96
97 pub fn broadcast(&self, msg: T) -> Result<usize, WsError> {
108 self.tx.send(msg).map_err(|_| WsError::BroadcastFull)
109 }
110
111 pub fn try_broadcast(&self, msg: T) -> usize {
114 self.tx.send(msg).unwrap_or(0)
115 }
116
117 pub fn connection_count(&self) -> u64 {
119 self.connection_count.load(Ordering::Relaxed)
120 }
121
122 pub fn increment_connections(&self, max: Option<usize>) -> Result<u64, WsError> {
128 if let Some(limit) = max {
129 loop {
131 let current = self.connection_count.load(Ordering::Relaxed);
132 if current as usize >= limit {
133 return Err(WsError::TooManyConnections);
134 }
135 match self.connection_count.compare_exchange(
136 current,
137 current + 1,
138 Ordering::Relaxed,
139 Ordering::Relaxed,
140 ) {
141 Ok(_) => return Ok(current + 1),
142 Err(_) => continue,
143 }
144 }
145 } else {
146 Ok(self.connection_count.fetch_add(1, Ordering::Relaxed) + 1)
147 }
148 }
149
150 pub fn decrement_connections(&self) -> u64 {
152 let prev = self
154 .connection_count
155 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |v| {
156 if v == 0 {
157 None
158 } else {
159 Some(v - 1)
160 }
161 });
162 match prev {
163 Ok(v) if v > 0 => v - 1,
164 _ => 0,
165 }
166 }
167
168 pub fn receiver_count(&self) -> usize {
170 self.tx.receiver_count()
171 }
172
173 pub fn capacity(&self) -> usize {
175 0
183 }
184
185 pub fn sender(&self) -> &broadcast::Sender<T> {
187 &self.tx
188 }
189}
190
191impl<T> BroadcastHub<T>
193where
194 T: Clone + Serialize + Send + Sync + 'static,
195{
196 pub fn with_capacity(capacity: usize) -> Self {
198 Self::new(capacity)
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use serde::{Deserialize, Serialize};
206
207 #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
208 struct TestMsg {
209 id: u32,
210 body: String,
211 }
212
213 #[tokio::test]
214 async fn broadcast_and_subscribe() {
215 let hub = BroadcastHub::<TestMsg>::new(16);
216 let mut rx1 = hub.subscribe();
217 let mut rx2 = hub.subscribe();
218 let msg = TestMsg {
219 id: 1,
220 body: "hi".into(),
221 };
222 let n = hub.broadcast(msg.clone()).unwrap();
223 assert_eq!(n, 2);
224 assert_eq!(rx1.recv().await.unwrap(), msg);
225 assert_eq!(rx2.recv().await.unwrap(), msg);
226 }
227
228 #[tokio::test]
229 async fn broadcast_no_receivers_error() {
230 let hub = BroadcastHub::<String>::new(16);
231 let err = hub.broadcast("hello".to_string()).unwrap_err();
233 assert_eq!(err, WsError::BroadcastFull);
234 assert_eq!(hub.try_broadcast("hello".to_string()), 0);
236 }
237
238 #[test]
239 fn connection_count_atomic() {
240 let hub = BroadcastHub::<String>::new(8);
241 assert_eq!(hub.connection_count(), 0);
242 hub.increment_connections(None).unwrap();
243 assert_eq!(hub.connection_count(), 1);
244 hub.increment_connections(None).unwrap();
245 assert_eq!(hub.connection_count(), 2);
246 hub.decrement_connections();
247 assert_eq!(hub.connection_count(), 1);
248 hub.decrement_connections();
249 hub.decrement_connections(); assert_eq!(hub.connection_count(), 0);
251 }
252
253 #[test]
254 fn max_connections_enforced() {
255 let hub = BroadcastHub::<String>::new(8);
256 hub.increment_connections(Some(2)).unwrap();
257 hub.increment_connections(Some(2)).unwrap();
258 let err = hub.increment_connections(Some(2)).unwrap_err();
259 assert_eq!(err, WsError::TooManyConnections);
260 }
261
262 #[test]
263 fn clone_shares_counter() {
264 let hub = BroadcastHub::<String>::new(8);
265 let hub2 = hub.clone();
266 hub.increment_connections(None).unwrap();
267 assert_eq!(hub2.connection_count(), 1);
268 }
269}