Skip to main content

gpui/
queue.rs

1//! A queue with three priority levels. A sender can be shared by reference across
2//! threads, while cloning a receiver adds another consumer of the same queue.
3//!
4//! When more than one level has items, each pop chooses a level at random in proportion to its
5//! [`Priority::weight`]: high 60, medium 30 and low 10. Items retain FIFO order within a level;
6//! the queue itself is unbounded.
7
8use std::{
9    collections::VecDeque,
10    fmt,
11    iter::FusedIterator,
12    sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError, TryLockError, atomic::AtomicUsize},
13};
14
15use rand::{Rng, SeedableRng, rngs::SmallRng};
16
17use crate::Priority;
18
19struct PriorityQueues<T> {
20    high_priority: VecDeque<T>,
21    medium_priority: VecDeque<T>,
22    low_priority: VecDeque<T>,
23}
24
25impl<T> PriorityQueues<T> {
26    fn is_empty(&self) -> bool {
27        self.high_priority.is_empty()
28            && self.medium_priority.is_empty()
29            && self.low_priority.is_empty()
30    }
31}
32
33struct PriorityQueueState<T> {
34    // std's Wasm atomic backend unlocks and notifies without acquiring another
35    // lock. parking_lot can park during those operations, even after try_lock.
36    queues: Mutex<PriorityQueues<T>>,
37    condvar: Condvar,
38    receiver_count: AtomicUsize,
39    sender_count: AtomicUsize,
40}
41
42impl<T> PriorityQueueState<T> {
43    fn send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
44        if self
45            .receiver_count
46            .load(std::sync::atomic::Ordering::Relaxed)
47            == 0
48        {
49            return Err(SendError(item));
50        }
51
52        let mut queues = self.queues.lock().unwrap_or_else(PoisonError::into_inner);
53        Self::push(&mut queues, priority, item);
54        self.condvar.notify_one();
55        Ok(())
56    }
57
58    fn spin_send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
59        if self
60            .receiver_count
61            .load(std::sync::atomic::Ordering::Relaxed)
62            == 0
63        {
64            return Err(SendError(item));
65        }
66
67        let mut queues = loop {
68            match self.queues.try_lock() {
69                Ok(guard) => break guard,
70                Err(TryLockError::Poisoned(error)) => break error.into_inner(),
71                Err(TryLockError::WouldBlock) => std::hint::spin_loop(),
72            }
73        };
74        Self::push(&mut queues, priority, item);
75        self.condvar.notify_one();
76        Ok(())
77    }
78
79    fn push(queues: &mut PriorityQueues<T>, priority: Priority, item: T) {
80        match priority {
81            Priority::RealtimeAudio => unreachable!(
82                "Realtime audio priority runs on a dedicated thread and is never queued"
83            ),
84            Priority::High => queues.high_priority.push_back(item),
85            Priority::Medium => queues.medium_priority.push_back(item),
86            Priority::Low => queues.low_priority.push_back(item),
87        };
88    }
89
90    fn recv<'a>(&'a self) -> Result<MutexGuard<'a, PriorityQueues<T>>, RecvError> {
91        let mut queues = self.queues.lock().unwrap_or_else(PoisonError::into_inner);
92
93        // Re-checked after every wake: dropping the last sender wakes waiting receivers
94        // so they can end instead of blocking forever on a queue nothing can fill.
95        while queues.is_empty() {
96            if self.sender_count.load(std::sync::atomic::Ordering::Acquire) == 0 {
97                return Err(crate::queue::RecvError);
98            }
99            queues = self
100                .condvar
101                .wait(queues)
102                .unwrap_or_else(PoisonError::into_inner);
103        }
104
105        Ok(queues)
106    }
107
108    fn try_recv<'a>(&'a self) -> Result<Option<MutexGuard<'a, PriorityQueues<T>>>, RecvError> {
109        let queues = self.queues.lock().unwrap_or_else(PoisonError::into_inner);
110
111        let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed);
112        if queues.is_empty() && sender_count == 0 {
113            return Err(crate::queue::RecvError);
114        }
115
116        if queues.is_empty() {
117            Ok(None)
118        } else {
119            Ok(Some(queues))
120        }
121    }
122
123    fn spin_try_recv<'a>(&'a self) -> Result<Option<MutexGuard<'a, PriorityQueues<T>>>, RecvError> {
124        let queues = loop {
125            match self.queues.try_lock() {
126                Ok(guard) => break guard,
127                Err(TryLockError::Poisoned(error)) => break error.into_inner(),
128                Err(TryLockError::WouldBlock) => std::hint::spin_loop(),
129            }
130        };
131
132        let sender_count = self.sender_count.load(std::sync::atomic::Ordering::Relaxed);
133        if queues.is_empty() && sender_count == 0 {
134            return Err(crate::queue::RecvError);
135        }
136
137        if queues.is_empty() {
138            Ok(None)
139        } else {
140            Ok(Some(queues))
141        }
142    }
143}
144
145/// The sending half of a queue made by [`PriorityQueueReceiver::new`].
146///
147/// This type isn't `Clone`. Once the sender is dropped, a receiver finishes after it
148/// drains the queue.
149#[doc(hidden)]
150pub struct PriorityQueueSender<T> {
151    state: Arc<PriorityQueueState<T>>,
152}
153
154impl<T> PriorityQueueSender<T> {
155    fn new(state: Arc<PriorityQueueState<T>>) -> Self {
156        Self { state }
157    }
158
159    /// Adds `item` at the back of the given priority's queue and wakes a waiting receiver.
160    ///
161    /// # Errors
162    ///
163    /// Returns [`SendError`] with the item if every receiver has been dropped.
164    /// Panics if `priority` is [`Priority::RealtimeAudio`], which never goes through this queue.
165    pub fn send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
166        self.state.send(priority, item)?;
167        Ok(())
168    }
169
170    /// Like [`Self::send`], but spins until the queue's lock is free instead of blocking
171    /// the thread on it.
172    ///
173    /// # Errors
174    ///
175    /// Returns [`SendError`] with the item if every receiver has been dropped.
176    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            // Pair the last-sender transition with the receiver's check-and-wait lock.
190            // A receiver that saw a live sender is either waiting or must release this
191            // lock before it can wait, so the notification cannot be lost.
192            drop(
193                self.state
194                    .queues
195                    .lock()
196                    .unwrap_or_else(PoisonError::into_inner),
197            );
198            self.state.condvar.notify_all();
199        }
200    }
201}
202
203/// The receiving half of a priority queue. It pops items in weighted random order across
204/// the three priority levels, while preserving FIFO order within each level.
205///
206/// Cloning a receiver adds another consumer to the same queue.
207#[doc(hidden)]
208pub struct PriorityQueueReceiver<T> {
209    state: Arc<PriorityQueueState<T>>,
210    rand: SmallRng,
211    disconnected: bool,
212}
213
214impl<T> Clone for PriorityQueueReceiver<T> {
215    fn clone(&self) -> Self {
216        self.state
217            .receiver_count
218            .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
219        Self {
220            state: Arc::clone(&self.state),
221            rand: SmallRng::seed_from_u64(0),
222            disconnected: self.disconnected,
223        }
224    }
225}
226
227/// Returned by a send when no receiver is left. Holds the item that wasn't sent.
228#[doc(hidden)]
229pub struct SendError<T>(pub T);
230
231impl<T: fmt::Debug> fmt::Debug for SendError<T> {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        f.debug_tuple("SendError").field(&self.0).finish()
234    }
235}
236
237/// Returned by a receive when the queue is empty and its sender has been dropped.
238#[derive(Debug)]
239#[doc(hidden)]
240pub struct RecvError;
241
242#[allow(dead_code)]
243impl<T> PriorityQueueReceiver<T> {
244    /// Creates an empty queue and returns its sender and receiver.
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// # use gpui::{Priority, queue::PriorityQueueReceiver};
250    /// let (sender, mut receiver) = PriorityQueueReceiver::new();
251    /// sender.send(Priority::High, 1).unwrap();
252    /// assert_eq!(receiver.pop().unwrap(), 1);
253    ///
254    /// // Once the sender is dropped and the queue is empty, `pop` fails.
255    /// drop(sender);
256    /// assert!(receiver.pop().is_err());
257    /// ```
258    pub fn new() -> (PriorityQueueSender<T>, Self) {
259        let state = PriorityQueueState {
260            queues: Mutex::new(PriorityQueues {
261                high_priority: VecDeque::new(),
262                medium_priority: VecDeque::new(),
263                low_priority: VecDeque::new(),
264            }),
265            condvar: Condvar::new(),
266            receiver_count: AtomicUsize::new(1),
267            sender_count: AtomicUsize::new(1),
268        };
269        let state = Arc::new(state);
270
271        let sender = PriorityQueueSender::new(Arc::clone(&state));
272
273        let receiver = PriorityQueueReceiver {
274            state,
275            rand: SmallRng::seed_from_u64(0),
276            disconnected: false,
277        };
278
279        (sender, receiver)
280    }
281
282    /// Returns `true` if no items are queued at any priority.
283    pub fn is_empty(&self) -> bool {
284        self.state
285            .queues
286            .lock()
287            .unwrap_or_else(PoisonError::into_inner)
288            .is_empty()
289    }
290
291    /// Returns the number of queued items across all priorities.
292    pub(crate) fn len(&self) -> usize {
293        let queues = self
294            .state
295            .queues
296            .lock()
297            .unwrap_or_else(PoisonError::into_inner);
298        queues.high_priority.len() + queues.medium_priority.len() + queues.low_priority.len()
299    }
300
301    /// Pops one item without blocking. Returns `Ok(None)` if the queue is empty.
302    ///
303    /// Use this to take a single item. To drain many items, use [`Self::try_iter`].
304    ///
305    /// # Errors
306    ///
307    /// Returns [`RecvError`] when the queue is empty and its sender has been dropped.
308    pub fn try_pop(&mut self) -> Result<Option<T>, RecvError> {
309        self.pop_inner(false)
310    }
311
312    /// Like [`Self::try_pop`], but spins until the queue's lock is free instead of
313    /// blocking the thread on it.
314    ///
315    /// # Errors
316    ///
317    /// Returns [`RecvError`] when the queue is empty and its sender has been dropped.
318    pub fn spin_try_pop(&mut self) -> Result<Option<T>, RecvError> {
319        use Priority as P;
320
321        let Some(mut queues) = self.state.spin_try_recv()? else {
322            return Ok(None);
323        };
324
325        let high = P::High.weight() * !queues.high_priority.is_empty() as u32;
326        let medium = P::Medium.weight() * !queues.medium_priority.is_empty() as u32;
327        let low = P::Low.weight() * !queues.low_priority.is_empty() as u32;
328        let mut mass = high + medium + low;
329
330        if !queues.high_priority.is_empty() {
331            let flip = self.rand.random_ratio(P::High.weight(), mass);
332            if flip {
333                return Ok(queues.high_priority.pop_front());
334            }
335            mass -= P::High.weight();
336        }
337
338        if !queues.medium_priority.is_empty() {
339            let flip = self.rand.random_ratio(P::Medium.weight(), mass);
340            if flip {
341                return Ok(queues.medium_priority.pop_front());
342            }
343            mass -= P::Medium.weight();
344        }
345
346        if !queues.low_priority.is_empty() {
347            let flip = self.rand.random_ratio(P::Low.weight(), mass);
348            if flip {
349                return Ok(queues.low_priority.pop_front());
350            }
351        }
352
353        Ok(None)
354    }
355
356    /// Pops one item, blocking the current thread until one is available.
357    ///
358    /// Use this to take a single item. To drain many items, use [`Self::iter`].
359    ///
360    /// # Errors
361    ///
362    /// Returns [`RecvError`] when the queue is empty and its sender has been dropped.
363    pub fn pop(&mut self) -> Result<T, RecvError> {
364        self.pop_inner(true).map(|e| e.unwrap())
365    }
366
367    /// Returns an iterator that yields the queued items and ends when the queue is empty.
368    ///
369    /// The iterator never blocks. Its last item is `Err(RecvError)` if the sender has
370    /// been dropped, and `None` otherwise.
371    pub fn try_iter(self) -> TryIter<T> {
372        TryIter {
373            receiver: self,
374            ended: false,
375        }
376    }
377
378    /// Returns an iterator that yields items as they arrive, in the same weighted order as
379    /// [`Self::pop`].
380    ///
381    /// The iterator blocks while the queue is empty and ends once the sender has been
382    /// dropped and the queue is drained.
383    pub fn iter(self) -> Iter<T> {
384        Iter(self)
385    }
386
387    #[inline(always)]
388    // algorithm is the loaded die from biased coin from
389    // https://www.keithschwarz.com/darts-dice-coins/
390    fn pop_inner(&mut self, block: bool) -> Result<Option<T>, RecvError> {
391        use Priority as P;
392
393        let mut queues = if !block {
394            let Some(queues) = self.state.try_recv()? else {
395                return Ok(None);
396            };
397            queues
398        } else {
399            self.state.recv()?
400        };
401
402        let high = P::High.weight() * !queues.high_priority.is_empty() as u32;
403        let medium = P::Medium.weight() * !queues.medium_priority.is_empty() as u32;
404        let low = P::Low.weight() * !queues.low_priority.is_empty() as u32;
405        let mut mass = high + medium + low; //%
406
407        if !queues.high_priority.is_empty() {
408            let flip = self.rand.random_ratio(P::High.weight(), mass);
409            if flip {
410                return Ok(queues.high_priority.pop_front());
411            }
412            mass -= P::High.weight();
413        }
414
415        if !queues.medium_priority.is_empty() {
416            let flip = self.rand.random_ratio(P::Medium.weight(), mass);
417            if flip {
418                return Ok(queues.medium_priority.pop_front());
419            }
420            mass -= P::Medium.weight();
421        }
422
423        if !queues.low_priority.is_empty() {
424            let flip = self.rand.random_ratio(P::Low.weight(), mass);
425            if flip {
426                return Ok(queues.low_priority.pop_front());
427            }
428        }
429
430        Ok(None)
431    }
432}
433
434impl<T> Drop for PriorityQueueReceiver<T> {
435    fn drop(&mut self) {
436        self.state
437            .receiver_count
438            .fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
439    }
440}
441
442/// The blocking iterator returned by [`PriorityQueueReceiver::iter`].
443#[doc(hidden)]
444pub struct Iter<T>(PriorityQueueReceiver<T>);
445impl<T> Iterator for Iter<T> {
446    type Item = T;
447
448    fn next(&mut self) -> Option<Self::Item> {
449        self.0.pop().ok()
450    }
451}
452impl<T> FusedIterator for Iter<T> {}
453
454/// The non-blocking iterator returned by [`PriorityQueueReceiver::try_iter`].
455#[doc(hidden)]
456pub struct TryIter<T> {
457    receiver: PriorityQueueReceiver<T>,
458    ended: bool,
459}
460impl<T> Iterator for TryIter<T> {
461    type Item = Result<T, RecvError>;
462
463    fn next(&mut self) -> Option<Self::Item> {
464        if self.ended {
465            return None;
466        }
467
468        let res = self.receiver.try_pop();
469        self.ended = !matches!(&res, Ok(Some(_)));
470
471        res.transpose()
472    }
473}
474impl<T> FusedIterator for TryIter<T> {}
475
476#[cfg(test)]
477mod tests {
478    use collections::HashSet;
479
480    use super::*;
481
482    #[test]
483    fn dropping_the_last_sender_wakes_a_waiting_receiver() {
484        let (sender, mut receiver) = PriorityQueueReceiver::<usize>::new();
485        let (finished, result) = std::sync::mpsc::channel();
486        std::thread::spawn(move || {
487            finished.send(receiver.pop().is_err()).ok();
488        });
489
490        std::thread::sleep(std::time::Duration::from_millis(100));
491        drop(sender);
492        assert_eq!(
493            result.recv_timeout(std::time::Duration::from_secs(5)),
494            Ok(true)
495        );
496    }
497
498    #[test]
499    fn nonblocking_iterator_stays_finished_after_observing_an_empty_queue() {
500        let (sender, receiver) = PriorityQueueReceiver::new();
501        sender.send(Priority::Medium, 1).unwrap();
502        let mut items = receiver.try_iter();
503        assert!(matches!(items.next(), Some(Ok(1))));
504        assert!(items.next().is_none());
505        sender.send(Priority::High, 2).unwrap();
506        assert!(items.next().is_none());
507    }
508
509    #[test]
510    fn blocked_receivers_end_when_the_sender_drops() {
511        let (sender, receiver) = PriorityQueueReceiver::<u32>::new();
512        let (done_sender, done_receiver) = std::sync::mpsc::channel();
513        let workers = (0..2)
514            .map(|_| {
515                let receiver = receiver.clone();
516                let done_sender = done_sender.clone();
517                std::thread::spawn(move || {
518                    let received: Vec<u32> = receiver.iter().collect();
519                    done_sender.send(received).ok();
520                })
521            })
522            .collect::<Vec<_>>();
523        drop(receiver);
524
525        sender.send(Priority::Medium, 7).unwrap();
526        std::thread::sleep(std::time::Duration::from_millis(50));
527        assert!(
528            done_receiver.try_recv().is_err(),
529            "receivers keep waiting while the sender is alive"
530        );
531
532        drop(sender);
533        let mut received = Vec::new();
534        for _ in 0..2 {
535            received.extend(
536                done_receiver
537                    .recv_timeout(std::time::Duration::from_secs(5))
538                    .expect("a waiting receiver should end once the sender is dropped"),
539            );
540        }
541        assert_eq!(received, [7]);
542        for worker in workers {
543            worker.join().expect("worker should exit cleanly");
544        }
545    }
546
547    #[test]
548    fn all_tasks_get_yielded() {
549        let (tx, mut rx) = PriorityQueueReceiver::new();
550        tx.send(Priority::Medium, 20).unwrap();
551        tx.send(Priority::High, 30).unwrap();
552        tx.send(Priority::Low, 10).unwrap();
553        tx.send(Priority::Medium, 21).unwrap();
554        tx.send(Priority::High, 31).unwrap();
555
556        drop(tx);
557
558        assert_eq!(
559            rx.iter().collect::<HashSet<_>>(),
560            [30, 31, 20, 21, 10].into_iter().collect::<HashSet<_>>()
561        )
562    }
563
564    #[test]
565    fn new_high_prio_task_get_scheduled_quickly() {
566        let (tx, mut rx) = PriorityQueueReceiver::new();
567        for _ in 0..100 {
568            tx.send(Priority::Low, 1).unwrap();
569        }
570
571        assert_eq!(rx.pop().unwrap(), 1);
572        tx.send(Priority::High, 3).unwrap();
573        assert_eq!(rx.pop().unwrap(), 3);
574        assert_eq!(rx.pop().unwrap(), 1);
575    }
576}