1use std::{
7 collections::VecDeque,
8 fmt,
9 iter::FusedIterator,
10 sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError, TryLockError, atomic::AtomicUsize},
11};
12
13use rand::{Rng, SeedableRng, rngs::SmallRng};
14
15use crate::Priority;
16
17struct PriorityQueues<T> {
18 high_priority: VecDeque<T>,
19 medium_priority: VecDeque<T>,
20 low_priority: VecDeque<T>,
21}
22
23impl<T> PriorityQueues<T> {
24 fn is_empty(&self) -> bool {
25 self.high_priority.is_empty()
26 && self.medium_priority.is_empty()
27 && self.low_priority.is_empty()
28 }
29}
30
31struct PriorityQueueState<T> {
32 queues: Mutex<PriorityQueues<T>>,
35 condvar: Condvar,
36 receiver_count: AtomicUsize,
37 sender_count: AtomicUsize,
38}
39
40impl<T> PriorityQueueState<T> {
41 fn send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
42 if self
43 .receiver_count
44 .load(std::sync::atomic::Ordering::Relaxed)
45 == 0
46 {
47 return Err(SendError(item));
48 }
49
50 let mut queues = self.queues.lock().unwrap_or_else(PoisonError::into_inner);
51 Self::push(&mut queues, priority, item);
52 self.condvar.notify_one();
53 Ok(())
54 }
55
56 fn spin_send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
57 if self
58 .receiver_count
59 .load(std::sync::atomic::Ordering::Relaxed)
60 == 0
61 {
62 return Err(SendError(item));
63 }
64
65 let mut queues = loop {
66 match self.queues.try_lock() {
67 Ok(guard) => break guard,
68 Err(TryLockError::Poisoned(error)) => break error.into_inner(),
69 Err(TryLockError::WouldBlock) => std::hint::spin_loop(),
70 }
71 };
72 Self::push(&mut queues, priority, item);
73 self.condvar.notify_one();
74 Ok(())
75 }
76
77 fn push(queues: &mut PriorityQueues<T>, priority: Priority, item: T) {
78 match priority {
79 Priority::RealtimeAudio => unreachable!(
80 "Realtime audio priority runs on a dedicated thread and is never queued"
81 ),
82 Priority::High => queues.high_priority.push_back(item),
83 Priority::Medium => queues.medium_priority.push_back(item),
84 Priority::Low => queues.low_priority.push_back(item),
85 };
86 }
87
88 fn recv<'a>(&'a self) -> Result<MutexGuard<'a, PriorityQueues<T>>, RecvError> {
89 let mut queues = self.queues.lock().unwrap_or_else(PoisonError::into_inner);
90
91 while queues.is_empty() {
94 if self.sender_count.load(std::sync::atomic::Ordering::Acquire) == 0 {
95 return Err(crate::queue::RecvError);
96 }
97 queues = self
98 .condvar
99 .wait(queues)
100 .unwrap_or_else(PoisonError::into_inner);
101 }
102
103 Ok(queues)
104 }
105
106 fn try_recv<'a>(&'a self) -> Result<Option<MutexGuard<'a, PriorityQueues<T>>>, RecvError> {
107 let queues = self.queues.lock().unwrap_or_else(PoisonError::into_inner);
108
109 let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed);
110 if queues.is_empty() && sender_count == 0 {
111 return Err(crate::queue::RecvError);
112 }
113
114 if queues.is_empty() {
115 Ok(None)
116 } else {
117 Ok(Some(queues))
118 }
119 }
120
121 fn spin_try_recv<'a>(&'a self) -> Result<Option<MutexGuard<'a, PriorityQueues<T>>>, RecvError> {
122 let queues = loop {
123 match self.queues.try_lock() {
124 Ok(guard) => break guard,
125 Err(TryLockError::Poisoned(error)) => break error.into_inner(),
126 Err(TryLockError::WouldBlock) => std::hint::spin_loop(),
127 }
128 };
129
130 let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed);
131 if queues.is_empty() && sender_count == 0 {
132 return Err(crate::queue::RecvError);
133 }
134
135 if queues.is_empty() {
136 Ok(None)
137 } else {
138 Ok(Some(queues))
139 }
140 }
141}
142
143#[doc(hidden)]
144pub struct PriorityQueueSender<T> {
145 state: Arc<PriorityQueueState<T>>,
146}
147
148impl<T> PriorityQueueSender<T> {
149 fn new(state: Arc<PriorityQueueState<T>>) -> Self {
150 Self { state }
151 }
152
153 pub fn send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
163 self.state.send(priority, item)?;
164 Ok(())
165 }
166
167 pub fn spin_send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
177 self.state.spin_send(priority, item)?;
178 Ok(())
179 }
180}
181
182impl<T> Drop for PriorityQueueSender<T> {
183 fn drop(&mut self) {
184 let previous = self
185 .state
186 .sender_count
187 .fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
188 if previous == 1 {
189 drop(
195 self.state
196 .queues
197 .lock()
198 .unwrap_or_else(PoisonError::into_inner),
199 );
200 self.state.condvar.notify_all();
201 }
202 }
203}
204
205#[doc(hidden)]
206pub struct PriorityQueueReceiver<T> {
207 state: Arc<PriorityQueueState<T>>,
208 rand: SmallRng,
209 disconnected: bool,
210}
211
212impl<T> Clone for PriorityQueueReceiver<T> {
213 fn clone(&self) -> Self {
214 self.state
215 .receiver_count
216 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
217 Self {
218 state: Arc::clone(&self.state),
219 rand: SmallRng::seed_from_u64(0),
220 disconnected: self.disconnected,
221 }
222 }
223}
224
225#[doc(hidden)]
226pub struct SendError<T>(pub T);
227
228impl<T: fmt::Debug> fmt::Debug for SendError<T> {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 f.debug_tuple("SendError").field(&self.0).finish()
231 }
232}
233
234#[derive(Debug)]
235#[doc(hidden)]
236pub struct RecvError;
237
238#[allow(dead_code)]
239impl<T> PriorityQueueReceiver<T> {
240 pub fn new() -> (PriorityQueueSender<T>, Self) {
252 let state = PriorityQueueState {
253 queues: Mutex::new(PriorityQueues {
254 high_priority: VecDeque::new(),
255 medium_priority: VecDeque::new(),
256 low_priority: VecDeque::new(),
257 }),
258 condvar: Condvar::new(),
259 receiver_count: AtomicUsize::new(1),
260 sender_count: AtomicUsize::new(1),
261 };
262 let state = Arc::new(state);
263
264 let sender = PriorityQueueSender::new(Arc::clone(&state));
265
266 let receiver = PriorityQueueReceiver {
267 state,
268 rand: SmallRng::seed_from_u64(0),
269 disconnected: false,
270 };
271
272 (sender, receiver)
273 }
274
275 pub fn is_empty(&self) -> bool {
277 self.state
278 .queues
279 .lock()
280 .unwrap_or_else(PoisonError::into_inner)
281 .is_empty()
282 }
283
284 pub(crate) fn len(&self) -> usize {
286 let queues = self
287 .state
288 .queues
289 .lock()
290 .unwrap_or_else(PoisonError::into_inner);
291 queues.high_priority.len() + queues.medium_priority.len() + queues.low_priority.len()
292 }
293
294 pub fn try_pop(&mut self) -> Result<Option<T>, RecvError> {
305 self.pop_inner(false)
306 }
307
308 pub fn spin_try_pop(&mut self) -> Result<Option<T>, RecvError> {
317 use Priority as P;
318
319 let Some(mut queues) = self.state.spin_try_recv()? else {
320 return Ok(None);
321 };
322
323 let high = P::High.weight() * !queues.high_priority.is_empty() as u32;
324 let medium = P::Medium.weight() * !queues.medium_priority.is_empty() as u32;
325 let low = P::Low.weight() * !queues.low_priority.is_empty() as u32;
326 let mut mass = high + medium + low;
327
328 if !queues.high_priority.is_empty() {
329 let flip = self.rand.random_ratio(P::High.weight(), mass);
330 if flip {
331 return Ok(queues.high_priority.pop_front());
332 }
333 mass -= P::High.weight();
334 }
335
336 if !queues.medium_priority.is_empty() {
337 let flip = self.rand.random_ratio(P::Medium.weight(), mass);
338 if flip {
339 return Ok(queues.medium_priority.pop_front());
340 }
341 mass -= P::Medium.weight();
342 }
343
344 if !queues.low_priority.is_empty() {
345 let flip = self.rand.random_ratio(P::Low.weight(), mass);
346 if flip {
347 return Ok(queues.low_priority.pop_front());
348 }
349 }
350
351 Ok(None)
352 }
353
354 pub fn pop(&mut self) -> Result<T, RecvError> {
363 self.pop_inner(true).map(|e| e.unwrap())
364 }
365
366 pub fn try_iter(self) -> TryIter<T> {
369 TryIter {
370 receiver: self,
371 ended: false,
372 }
373 }
374
375 pub fn iter(self) -> Iter<T> {
378 Iter(self)
379 }
380
381 #[inline(always)]
382 fn pop_inner(&mut self, block: bool) -> Result<Option<T>, RecvError> {
385 use Priority as P;
386
387 let mut queues = if !block {
388 let Some(queues) = self.state.try_recv()? else {
389 return Ok(None);
390 };
391 queues
392 } else {
393 self.state.recv()?
394 };
395
396 let high = P::High.weight() * !queues.high_priority.is_empty() as u32;
397 let medium = P::Medium.weight() * !queues.medium_priority.is_empty() as u32;
398 let low = P::Low.weight() * !queues.low_priority.is_empty() as u32;
399 let mut mass = high + medium + low; if !queues.high_priority.is_empty() {
402 let flip = self.rand.random_ratio(P::High.weight(), mass);
403 if flip {
404 return Ok(queues.high_priority.pop_front());
405 }
406 mass -= P::High.weight();
407 }
408
409 if !queues.medium_priority.is_empty() {
410 let flip = self.rand.random_ratio(P::Medium.weight(), mass);
411 if flip {
412 return Ok(queues.medium_priority.pop_front());
413 }
414 mass -= P::Medium.weight();
415 }
416
417 if !queues.low_priority.is_empty() {
418 let flip = self.rand.random_ratio(P::Low.weight(), mass);
419 if flip {
420 return Ok(queues.low_priority.pop_front());
421 }
422 }
423
424 Ok(None)
425 }
426}
427
428impl<T> Drop for PriorityQueueReceiver<T> {
429 fn drop(&mut self) {
430 self.state
431 .receiver_count
432 .fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
433 }
434}
435
436#[doc(hidden)]
437pub struct Iter<T>(PriorityQueueReceiver<T>);
438impl<T> Iterator for Iter<T> {
439 type Item = T;
440
441 fn next(&mut self) -> Option<Self::Item> {
442 self.0.pop().ok()
443 }
444}
445impl<T> FusedIterator for Iter<T> {}
446
447#[doc(hidden)]
448pub struct TryIter<T> {
449 receiver: PriorityQueueReceiver<T>,
450 ended: bool,
451}
452impl<T> Iterator for TryIter<T> {
453 type Item = Result<T, RecvError>;
454
455 fn next(&mut self) -> Option<Self::Item> {
456 if self.ended {
457 return None;
458 }
459
460 let res = self.receiver.try_pop();
461 self.ended = res.is_err();
462
463 res.transpose()
464 }
465}
466impl<T> FusedIterator for TryIter<T> {}
467
468#[cfg(test)]
469mod tests {
470 use collections::HashSet;
471
472 use super::*;
473
474 #[test]
475 fn blocked_receivers_end_when_the_sender_drops() {
476 let (sender, receiver) = PriorityQueueReceiver::<u32>::new();
477 let (done_sender, done_receiver) = std::sync::mpsc::channel();
478 let workers = (0..2)
479 .map(|_| {
480 let receiver = receiver.clone();
481 let done_sender = done_sender.clone();
482 std::thread::spawn(move || {
483 let received: Vec<u32> = receiver.iter().collect();
484 done_sender.send(received).ok();
485 })
486 })
487 .collect::<Vec<_>>();
488 drop(receiver);
489
490 sender.send(Priority::Medium, 7).unwrap();
491 std::thread::sleep(std::time::Duration::from_millis(50));
492 assert!(
493 done_receiver.try_recv().is_err(),
494 "receivers keep waiting while the sender is alive"
495 );
496
497 drop(sender);
498 let mut received = Vec::new();
499 for _ in 0..2 {
500 received.extend(
501 done_receiver
502 .recv_timeout(std::time::Duration::from_secs(5))
503 .expect("a waiting receiver should end once the sender is dropped"),
504 );
505 }
506 assert_eq!(received, [7]);
507 for worker in workers {
508 worker.join().expect("worker should exit cleanly");
509 }
510 }
511
512 #[test]
513 fn all_tasks_get_yielded() {
514 let (tx, rx) = PriorityQueueReceiver::new();
515 tx.send(Priority::Medium, 20).unwrap();
516 tx.send(Priority::High, 30).unwrap();
517 tx.send(Priority::Low, 10).unwrap();
518 tx.send(Priority::Medium, 21).unwrap();
519 tx.send(Priority::High, 31).unwrap();
520
521 drop(tx);
522
523 assert_eq!(
524 rx.iter().collect::<HashSet<_>>(),
525 [30, 31, 20, 21, 10].into_iter().collect::<HashSet<_>>()
526 )
527 }
528
529 #[test]
530 fn new_high_prio_task_get_scheduled_quickly() {
531 let (tx, mut rx) = PriorityQueueReceiver::new();
532 for _ in 0..100 {
533 tx.send(Priority::Low, 1).unwrap();
534 }
535
536 assert_eq!(rx.pop().unwrap(), 1);
537 tx.send(Priority::High, 3).unwrap();
538 assert_eq!(rx.pop().unwrap(), 3);
539 assert_eq!(rx.pop().unwrap(), 1);
540 }
541}