1#![allow(clippy::await_holding_lock)]
3
4use std::{
5 collections::{BTreeSet, HashMap},
6 future::Future,
7 sync::Arc,
8};
9
10use futures::lock::Mutex;
11
12use crate::{broadcast::Broadcast, proto::TinyVec, util::retransmit_limit};
13
14struct Inner<B: Broadcast> {
15 q: BTreeSet<LimitedBroadcast<B>>,
16 m: HashMap<B::Id, LimitedBroadcast<B>>,
17 id_gen: u64,
22}
23
24impl<B: Broadcast> Inner<B> {
25 fn remove(&mut self, item: &LimitedBroadcast<B>) {
26 self.q.remove(item);
27 if let Some(id) = item.broadcast.id() {
28 self.m.remove(id);
29 }
30 }
31
32 fn insert(&mut self, item: LimitedBroadcast<B>) {
33 if let Some(id) = item.broadcast.id() {
34 self.m.insert(id.clone(), item.clone());
35 }
36 self.q.insert(item);
37 }
38
39 fn get_transmit_range(&self) -> (usize, usize) {
43 if self.q.is_empty() {
44 return (0, 0);
45 }
46 let (min, max) = (self.q.first(), self.q.last());
47 match (min, max) {
48 (Some(min), Some(max)) => (min.transmits, max.transmits),
49 _ => (0, 0),
50 }
51 }
52}
53
54pub trait NodeCalculator: Send + Sync + 'static {
56 fn num_nodes(&self) -> impl Future<Output = usize> + Send;
58}
59
60macro_rules! impl_node_calculator {
61 ($($ty:ty),+$(,)?) => {
62 $(
63 impl NodeCalculator for ::std::sync::Arc<$ty> {
64 async fn num_nodes(&self) -> usize {
65 self.load(::std::sync::atomic::Ordering::SeqCst) as usize
66 }
67 }
68 )*
69 };
70}
71
72impl_node_calculator! {
73 ::core::sync::atomic::AtomicUsize,
74 ::core::sync::atomic::AtomicIsize,
75 ::core::sync::atomic::AtomicU8,
76 ::core::sync::atomic::AtomicI8,
77 ::core::sync::atomic::AtomicU16,
78 ::core::sync::atomic::AtomicI16,
79 ::core::sync::atomic::AtomicU32,
80 ::core::sync::atomic::AtomicI32,
81 ::core::sync::atomic::AtomicU64,
82 ::core::sync::atomic::AtomicI64,
83}
84
85pub struct TransmitLimitedQueue<B: Broadcast, N> {
90 num_nodes: N,
92 retransmit_mult: usize,
95 inner: Mutex<Inner<B>>,
96}
97
98impl<B: Broadcast, N: NodeCalculator> TransmitLimitedQueue<B, N> {
99 pub fn new(retransmit_mult: usize, calc: N) -> Self {
114 Self {
115 num_nodes: calc,
116 retransmit_mult,
117 inner: Mutex::new(Inner {
118 q: BTreeSet::new(),
119 m: HashMap::new(),
120 id_gen: 0,
121 }),
122 }
123 }
124
125 pub async fn num_queued(&self) -> usize {
127 self.inner.lock().await.q.len()
128 }
129
130 pub async fn get_broadcasts(&self, limit: usize) -> TinyVec<B::Message> {
132 self.get_broadcast_with_prepend(TinyVec::new(), limit).await
133 }
134
135 pub(crate) async fn get_broadcast_with_prepend(
136 &self,
137 prepend: TinyVec<B::Message>,
138 limit: usize,
139 ) -> TinyVec<B::Message> {
140 let mut to_send = prepend;
141 let mut inner = self.inner.lock().await;
142 if inner.q.is_empty() {
143 return to_send;
144 }
145
146 let transmit_limit = retransmit_limit(self.retransmit_mult, self.num_nodes.num_nodes().await);
147
148 let (min_tr, max_tr) = inner.get_transmit_range();
151 let mut bytes_used = 0usize;
152 let mut transmits = min_tr;
153 let mut reinsert = Vec::new();
154 while transmits <= max_tr {
155 let free = limit.saturating_sub(bytes_used);
156 if free == 0 {
157 break;
158 }
159
160 let greater_or_equal = Cmp {
161 transmits,
162 msg_len: free as u64,
163 id: u64::MAX,
164 };
165
166 let less_than = Cmp {
167 transmits: transmits + 1,
168 msg_len: u64::MAX,
169 id: u64::MAX,
170 };
171
172 let keep = inner
173 .q
174 .iter()
175 .filter(|item| greater_or_equal <= item && less_than > item)
176 .find(|item| B::encoded_len(item.broadcast.message()) <= free)
177 .cloned();
178
179 match keep {
180 Some(mut keep) => {
181 let msg = keep.broadcast.message();
182 bytes_used += B::encoded_len(msg);
183 to_send.push(msg.clone());
185
186 inner.remove(&keep);
188 if keep.transmits + 1 >= transmit_limit {
189 keep.broadcast.finished().await;
190 } else {
191 keep.transmits += 1;
197 reinsert.push(keep);
198 }
199 }
200 None => {
201 transmits += 1;
202 continue;
203 }
204 }
205 }
206
207 for item in reinsert {
208 inner.insert(item);
209 }
210
211 to_send
212 }
213
214 pub async fn queue_broadcast(&self, b: B) {
216 self.queue_broadcast_in(b, 0).await
217 }
218
219 async fn queue_broadcast_in(&self, b: B, initial_transmits: usize) {
220 let mut inner = self.inner.lock().await;
221 if inner.id_gen == u64::MAX {
222 inner.id_gen = 1;
223 } else {
224 inner.id_gen += 1;
225 }
226 let id = inner.id_gen;
227
228 let lb = LimitedBroadcast {
229 transmits: initial_transmits,
230 msg_len: B::encoded_len(b.message()) as u64,
231 id,
232 broadcast: Arc::new(b),
233 };
234
235 let unique = lb.broadcast.is_unique();
236
237 if let Some(bid) = lb.broadcast.id() {
239 if let Some(old) = inner.m.remove(bid) {
240 old.broadcast.finished().await;
241 inner.remove(&old);
242 }
243 } else if !unique {
244 for item in inner.q.iter() {
246 let keep = lb.broadcast.invalidates(&item.broadcast);
247 if keep {
248 item.broadcast.finished().await;
249 }
250 }
251 inner
252 .q
253 .retain(|item| !lb.broadcast.invalidates(&item.broadcast));
254 }
255
256 inner.insert(lb);
258 }
259
260 pub async fn reset(&self) {
262 let q = {
263 let mut inner = self.inner.lock().await;
264 inner.m.clear();
265 inner.id_gen = 0;
266 std::mem::take(&mut inner.q)
267 };
268
269 for l in q {
270 l.broadcast.finished().await;
271 }
272 }
273
274 pub async fn prune(&self, max_retain: usize) {
277 let mut inner = self.inner.lock().await;
278 while inner.q.len() > max_retain {
280 if let Some(item) = inner.q.pop_last() {
281 item.broadcast.finished().await;
282 inner.remove(&item);
283 } else {
284 break;
285 }
286 }
287 }
288}
289
290#[derive(Debug)]
291pub(crate) struct LimitedBroadcast<B: Broadcast> {
292 transmits: usize,
294 msg_len: u64,
296 id: u64,
298 pub(crate) broadcast: Arc<B>,
299}
300
301impl<B: Broadcast> core::clone::Clone for LimitedBroadcast<B> {
302 fn clone(&self) -> Self {
303 Self {
304 broadcast: self.broadcast.clone(),
305 ..*self
306 }
307 }
308}
309
310impl<B: Broadcast> PartialEq for LimitedBroadcast<B> {
311 fn eq(&self, other: &Self) -> bool {
312 self.transmits == other.transmits && self.msg_len == other.msg_len && self.id == other.id
313 }
314}
315
316impl<B: Broadcast> Eq for LimitedBroadcast<B> {}
317
318impl<B: Broadcast> PartialOrd for LimitedBroadcast<B> {
319 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
320 Some(self.cmp(other))
321 }
322}
323
324impl<B: Broadcast> Ord for LimitedBroadcast<B> {
325 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
326 self
327 .transmits
328 .cmp(&other.transmits)
329 .then_with(|| other.msg_len.cmp(&self.msg_len))
330 .then_with(|| other.id.cmp(&self.id))
331 }
332}
333
334struct Cmp {
335 transmits: usize,
336 msg_len: u64,
337 id: u64,
338}
339
340impl<B: Broadcast> PartialEq<&LimitedBroadcast<B>> for Cmp {
341 fn eq(&self, other: &&LimitedBroadcast<B>) -> bool {
342 self.transmits == other.transmits && self.msg_len == other.msg_len && self.id == other.id
343 }
344}
345
346impl<B: Broadcast> PartialOrd<&LimitedBroadcast<B>> for Cmp {
347 fn partial_cmp(&self, other: &&LimitedBroadcast<B>) -> Option<std::cmp::Ordering> {
348 Some(
349 self
350 .transmits
351 .cmp(&other.transmits)
352 .then_with(|| other.msg_len.cmp(&self.msg_len))
353 .then_with(|| other.id.cmp(&self.id)),
354 )
355 }
356}
357
358#[cfg(any(test, feature = "test"))]
359impl<B: Broadcast> Inner<B> {
360 fn walk_read_only<F>(&self, reverse: bool, f: F)
361 where
362 F: FnMut(&LimitedBroadcast<B>) -> bool,
363 {
364 fn iter<'a, B: Broadcast, F>(it: impl Iterator<Item = &'a LimitedBroadcast<B>>, mut f: F)
365 where
366 F: FnMut(&LimitedBroadcast<B>) -> bool,
367 {
368 for item in it {
369 let prev_transmits = item.transmits;
370 let prev_msg_len = item.msg_len;
371 let prev_id = item.id;
372
373 let keep_going = f(item);
374
375 if prev_transmits != item.transmits || prev_msg_len != item.msg_len || prev_id != item.id {
376 panic!("edited queue while walking read only");
377 }
378
379 if !keep_going {
380 break;
381 }
382 }
383 }
384 if reverse {
385 iter(self.q.iter().rev(), f)
386 } else {
387 iter(self.q.iter(), f)
388 }
389 }
390}
391
392#[cfg(any(test, feature = "test"))]
393impl<B: Broadcast, N: NodeCalculator> TransmitLimitedQueue<B, N> {
394 pub(crate) async fn ordered_view(&self, reverse: bool) -> TinyVec<LimitedBroadcast<B>> {
395 let inner = self.inner.lock().await;
396
397 let mut out = TinyVec::new();
398 inner.walk_read_only(reverse, |b| {
399 out.push(b.clone());
400 true
401 });
402 out
403 }
404}
405
406#[cfg(test)]
407mod tests;