1use std::sync::{
8 Arc,
9 atomic::{AtomicU64, Ordering},
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 { None } else { Some(v - 1) }
157 });
158 match prev {
159 Ok(v) if v > 0 => v - 1,
160 _ => 0,
161 }
162 }
163
164 pub fn receiver_count(&self) -> usize {
166 self.tx.receiver_count()
167 }
168
169 pub fn capacity(&self) -> usize {
171 0
179 }
180
181 pub fn sender(&self) -> &broadcast::Sender<T> {
183 &self.tx
184 }
185}
186
187impl<T> BroadcastHub<T>
189where
190 T: Clone + Serialize + Send + Sync + 'static,
191{
192 pub fn with_capacity(capacity: usize) -> Self {
194 Self::new(capacity)
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use serde::{Deserialize, Serialize};
202
203 #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
204 struct TestMsg {
205 id: u32,
206 body: String,
207 }
208
209 #[tokio::test]
210 async fn broadcast_and_subscribe() {
211 let hub = BroadcastHub::<TestMsg>::new(16);
212 let mut rx1 = hub.subscribe();
213 let mut rx2 = hub.subscribe();
214 let msg = TestMsg {
215 id: 1,
216 body: "hi".into(),
217 };
218 let n = hub.broadcast(msg.clone()).unwrap();
219 assert_eq!(n, 2);
220 assert_eq!(rx1.recv().await.unwrap(), msg);
221 assert_eq!(rx2.recv().await.unwrap(), msg);
222 }
223
224 #[tokio::test]
225 async fn broadcast_no_receivers_error() {
226 let hub = BroadcastHub::<String>::new(16);
227 let err = hub.broadcast("hello".to_string()).unwrap_err();
229 assert_eq!(err, WsError::BroadcastFull);
230 assert_eq!(hub.try_broadcast("hello".to_string()), 0);
232 }
233
234 #[test]
235 fn connection_count_atomic() {
236 let hub = BroadcastHub::<String>::new(8);
237 assert_eq!(hub.connection_count(), 0);
238 hub.increment_connections(None).unwrap();
239 assert_eq!(hub.connection_count(), 1);
240 hub.increment_connections(None).unwrap();
241 assert_eq!(hub.connection_count(), 2);
242 hub.decrement_connections();
243 assert_eq!(hub.connection_count(), 1);
244 hub.decrement_connections();
245 hub.decrement_connections(); assert_eq!(hub.connection_count(), 0);
247 }
248
249 #[test]
250 fn max_connections_enforced() {
251 let hub = BroadcastHub::<String>::new(8);
252 hub.increment_connections(Some(2)).unwrap();
253 hub.increment_connections(Some(2)).unwrap();
254 let err = hub.increment_connections(Some(2)).unwrap_err();
255 assert_eq!(err, WsError::TooManyConnections);
256 }
257
258 #[test]
259 fn clone_shares_counter() {
260 let hub = BroadcastHub::<String>::new(8);
261 let hub2 = hub.clone();
262 hub.increment_connections(None).unwrap();
263 assert_eq!(hub2.connection_count(), 1);
264 }
265}