Skip to main content

gpui/
queue.rs

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    // std's Wasm atomic backend unlocks and notifies without acquiring another
28    // lock. parking_lot can park during those operations, even after try_lock.
29    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        // Re-checked after every wake: dropping the last sender wakes waiting receivers
87        // so they can end instead of blocking forever on a queue nothing can fill.
88        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            // A receiver checks the sender count and starts waiting under the queue lock,
167            // atomically. Acquiring that lock once, even with nothing to do under it,
168            // means any receiver that saw a live sender is already waiting, so the
169            // notification below reaches it; without this, a receiver between its check
170            // and its wait would miss the wake-up and block forever.
171            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    /// Returns whether the queue currently contains no elements.
242    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    /// Returns the number of queued elements across all priorities.
251    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    /// Tries to pop one element from the priority queue without blocking.
261    ///
262    /// This will early return if there are no elements in the queue.
263    ///
264    /// This method is best suited if you only intend to pop one element, for better performance
265    /// on large queues see [`Self::try_iter`]
266    ///
267    /// # Errors
268    ///
269    /// If the sender was dropped
270    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    /// Pops an element from the priority queue blocking if necessary.
313    ///
314    /// This method is best suited if you only intend to pop one element, for better performance
315    /// on large queues see [`Self::iter``]
316    ///
317    /// # Errors
318    ///
319    /// If the sender was dropped
320    pub fn pop(&mut self) -> Result<T, RecvError> {
321        self.pop_inner(true).map(|e| e.unwrap())
322    }
323
324    /// Returns an iterator over the elements of the queue
325    /// this iterator will end when all elements have been consumed and will not wait for new ones.
326    pub fn try_iter(self) -> TryIter<T> {
327        TryIter {
328            receiver: self,
329            ended: false,
330        }
331    }
332
333    /// Returns an iterator over the elements of the queue
334    /// this iterator will wait for new elements if the queue is empty.
335    pub fn iter(self) -> Iter<T> {
336        Iter(self)
337    }
338
339    #[inline(always)]
340    // algorithm is the loaded die from biased coin from
341    // https://www.keithschwarz.com/darts-dice-coins/
342    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; //%
358
359        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}