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