Skip to main content

async_selector/
selector.rs

1mod id;
2pub mod iter;
3mod waker;
4mod wrappers;
5
6use std::{
7    cell::Cell,
8    fmt,
9    ops::{ControlFlow, Index, IndexMut, Not},
10    pin::Pin,
11    rc::Rc,
12    sync::{Arc, Weak},
13    task::{Context, Poll},
14};
15
16use futures::Stream;
17
18use crate::{
19    list::{self, List, Node},
20    queue::Receiver,
21    selector::{iter::ExtractIf, waker::NodeWaker},
22    task::Task,
23};
24
25pub use crate::selector::{
26    id::Id,
27    wrappers::{Borrowed, BorrowedMut, Removed},
28};
29
30/// Selector over a dynamic set of [`Task`]s (generalized [`Future`]s/[`Stream`]s).
31///
32/// Inspired by [`FuturesUnordered`](futures::stream::FuturesUnordered),
33/// designed for flexibility and optimal performance when polling a large number of tasks.
34///
35/// Unless you want to exercise the full flexibility of this type,
36/// you can stick to the specializations exposed in the root of this crate
37/// (e.g. [`FutureSelector`](crate::FutureSelector) and [`StreamSelector`](crate::StreamSelector)).
38///
39/// # Removal
40///
41/// The selector creates a heap allocation for each stored task.
42/// Removing a task from the selector does not instantly free that memory.
43/// The memory can only be freed when:
44/// 1. all [`Id`] instances for this task are dropped, AND
45/// 2. [`Removed`] instance is dropped, AND
46/// 3. the [`Waker`](std::task::Waker) (and all its clones)
47///    passed when polling the task is dropped, AND
48/// 4. the selector observes the task removal
49///    (which happens when the selector is polled).
50///
51/// # Wakeups
52///
53/// The selector uses a smart strategy for polling the tasks.
54/// A task is **only** polled in the following cases:
55/// 1. after it is pushed into the selector
56/// 2. after it yields a non-terminal value
57/// 3. after the waker passed to [`Task::poll_progress`] receives a wakeup
58///
59/// To avoid nasty surprises, keep this in mind when:
60/// 1. Modifying a task borrowed from the selector
61/// 2. Changing the strategy used by the selector
62///    (see [example](https://github.com/Razz4780/async-selector/blob/main/examples/custom.rs))
63///
64/// The wakeups are stored in a FIFO queue. This implies that the selector
65/// processes ready tasks in a round-robin fashion.
66///
67/// # Panic
68///
69/// If the [`Task`] implementation panics, the task is removed from the selector and dropped,
70/// and the panic propagates. The selector remains valid.
71pub struct Selector<T, S> {
72    /// Queue of tasks that received a wakeup.
73    queue: Receiver<list::ListProtected<T>>,
74    /// List of all tasks.
75    list: List<T>,
76    /// Strategy that determines how the selector polls the tasks.
77    strategy: S,
78}
79
80impl<T, S> Selector<T, S> {
81    /// Creates an empty selector with the given strategy.
82    pub fn new(strategy: S) -> Self {
83        Self {
84            queue: Default::default(),
85            list: Default::default(),
86            strategy,
87        }
88    }
89
90    /// Pushes the given task into the selector, returning a mutable reference to the task.
91    ///
92    /// The reference can be used to obtain the task's [`Id`].
93    ///
94    /// This method is O(1).
95    pub fn push(&mut self, task: T) -> BorrowedMut<'_, T> {
96        BorrowedMut(self.list.push_back(self.queue.queue(), task))
97    }
98
99    /// Returns the number of tasks stored in the selector.
100    ///
101    /// This method is O(1).
102    pub fn len(&self) -> usize {
103        self.list.len()
104    }
105
106    /// Returns whether the selector is empty.
107    ///
108    /// This method is O(1).
109    pub fn is_empty(&self) -> bool {
110        self.list.is_empty()
111    }
112
113    /// Returns whether the selector contains a task with the given [`Id`].
114    ///
115    /// This method is O(1).
116    pub fn contains(&self, id: &Id<T>) -> bool {
117        self.get(id).is_some()
118    }
119
120    /// If the selector contains a task with the given [`Id`], returns a reference to it.
121    ///
122    /// This method is O(1).
123    pub fn get<'a>(&'a self, id: &Id<T>) -> Option<Borrowed<'a, T>> {
124        if self.created(id.get()).not() {
125            return None;
126        }
127        unsafe { self.list.get(id.get()).map(Borrowed) }
128    }
129
130    /// If the selector contains a task with the given [`Id`], returns a mutable reference to it.
131    ///
132    /// This method is O(1).
133    pub fn get_mut<'a>(&'a mut self, id: &Id<T>) -> Option<BorrowedMut<'a, T>> {
134        if self.created(id.get()).not() {
135            return None;
136        }
137        unsafe { self.list.get_mut(id.get()).map(BorrowedMut) }
138    }
139
140    /// If the selector contains a task with the given [`Id`], removes it.
141    ///
142    /// This method is O(1).
143    pub fn remove(&mut self, id: &Id<T>) -> Option<Removed<T>> {
144        if self.created(id.get()).not() {
145            return None;
146        }
147        unsafe { self.list.remove(id.get()).map(Removed) }
148    }
149
150    /// Returns a reference to the strategy used by this selector.
151    pub fn strategy(&self) -> &S {
152        &self.strategy
153    }
154
155    /// Returns a mutable reference to the strategy used by this selector.
156    pub fn strategy_mut(&mut self) -> &mut S {
157        &mut self.strategy
158    }
159
160    /// Returns a new selector with the same state, but different strategy.
161    pub fn with_strategy<S1>(self, strategy: S1) -> Selector<T, S1> {
162        Selector {
163            queue: self.queue,
164            list: self.list,
165            strategy,
166        }
167    }
168
169    /// Returns an iterator over all tasks in the selector.
170    ///
171    /// The tasks are visited in the insertion order.
172    pub fn iter(&self) -> iter::Iter<'_, T> {
173        iter::Iter(self.list.cursor())
174    }
175
176    /// Returns an iterator that allows for modifying each task in the selector.
177    ///
178    /// The tasks are visited in the insertion order.
179    pub fn iter_mut(&mut self) -> iter::IterMut<'_, T> {
180        iter::IterMut(self.list.cursor_mut())
181    }
182
183    /// Creates an iterator which uses a closure to determine if a task should be removed.
184    ///
185    /// If the closure returns true, the task is removed from the selector and yielded.
186    /// The tasks are visited in the insertion order.
187    ///
188    /// If the returned [`ExtractIf`] is not exhausted, e.g. because it is dropped without iterating or the iteration short-circuits,
189    /// then the remaining tasks will be retained.
190    #[must_use = "ExtractIf does not remove any elements unless consumed"]
191    pub fn extract_if<F>(&mut self, pred: F) -> ExtractIf<'_, T, F>
192    where
193        F: for<'b> FnMut(BorrowedMut<'b, T>) -> bool,
194    {
195        ExtractIf {
196            cursor: self.list.cursor_mut(),
197            pred,
198        }
199    }
200
201    /// Retains only the tasks specified by the predicate.
202    ///
203    /// In other words, remove all tasks for which the predicate returns false.
204    /// The tasks are visited in the insertion order.
205    pub fn retain<F>(&mut self, mut pred: F)
206    where
207        F: for<'b> FnMut(BorrowedMut<'b, T>) -> bool,
208    {
209        for _ in self.extract_if(|borrowed| pred(borrowed).not()) {}
210    }
211
212    /// Manually wakes all tasks in the selector.
213    pub fn wake_all(&self) {
214        self.iter().for_each(|task| task.id().wake());
215    }
216
217    fn created(&self, task: &Node<T>) -> bool {
218        let this_queue_ptr = Arc::as_ptr(self.queue.queue());
219        let task_queue_ptr = Weak::as_ptr(task.queue());
220        this_queue_ptr == task_queue_ptr
221    }
222}
223
224impl<T, S> Extend<T> for Selector<T, S> {
225    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
226        for task in iter {
227            self.push(task);
228        }
229    }
230}
231
232impl<T, S> FromIterator<T> for Selector<T, S>
233where
234    S: Default,
235{
236    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
237        let mut this = Self::default();
238        this.extend(iter);
239        this
240    }
241}
242
243impl<T, S> IntoIterator for Selector<T, S> {
244    type IntoIter = iter::IntoIter<T>;
245    type Item = Removed<T>;
246
247    fn into_iter(self) -> Self::IntoIter {
248        iter::IntoIter(self.list)
249    }
250}
251
252impl<'a, T, S> IntoIterator for &'a Selector<T, S> {
253    type IntoIter = iter::Iter<'a, T>;
254    type Item = Borrowed<'a, T>;
255
256    fn into_iter(self) -> Self::IntoIter {
257        self.iter()
258    }
259}
260
261impl<'a, T, S> IntoIterator for &'a mut Selector<T, S> {
262    type IntoIter = iter::IterMut<'a, T>;
263    type Item = BorrowedMut<'a, T>;
264
265    fn into_iter(self) -> Self::IntoIter {
266        self.iter_mut()
267    }
268}
269
270impl<T, S> Stream for Selector<T, S>
271where
272    T: Task<S>,
273    S: Unpin,
274{
275    type Item = T::Output;
276
277    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
278        let this = self.get_mut();
279
280        let marker = this.queue.register_waker(cx.waker());
281        if marker.is_null() {
282            return if this.list.is_empty() {
283                Poll::Ready(None)
284            } else {
285                Poll::Pending
286            };
287        }
288
289        while let Some(node) = this.queue.dequeue() {
290            let is_last = Arc::as_ptr(node.get()) == marker;
291
292            let guard = unsafe { this.list.access(node.get()) };
293            let Some(mut guard) = guard else {
294                if is_last {
295                    break;
296                } else {
297                    continue;
298                }
299            };
300
301            let node = node.into_inner();
302
303            let result = {
304                let waker = NodeWaker::new(&node);
305                guard
306                    .borrow_mut()
307                    .get_pin_mut()
308                    .poll_progress(&mut this.strategy, &mut Context::from_waker(&waker))
309            };
310
311            match result {
312                Poll::Ready(ControlFlow::Continue(val)) => {
313                    unsafe {
314                        // SAFETY: node was dequeued from this queue
315                        this.queue.queue().enqueue(node);
316                    }
317                    let output =
318                        T::transform_cont(BorrowedMut(guard.borrow_mut()), &mut this.strategy, val);
319                    guard.forget();
320                    if output.is_some() {
321                        return Poll::Ready(output);
322                    }
323                }
324
325                Poll::Ready(ControlFlow::Break(val)) => {
326                    let node = guard.remove_now();
327                    let output = T::transform_break(Removed(node), &mut this.strategy, val);
328                    if output.is_some() {
329                        return Poll::Ready(output);
330                    }
331                }
332
333                Poll::Pending => {
334                    guard.forget();
335                }
336            }
337
338            if is_last {
339                break;
340            }
341        }
342
343        if this.list.is_empty() {
344            Poll::Ready(None)
345        } else {
346            Poll::Pending
347        }
348    }
349
350    fn size_hint(&self) -> (usize, Option<usize>) {
351        (0, self.list.is_empty().then_some(0))
352    }
353}
354
355impl<T, S> Default for Selector<T, S>
356where
357    S: Default,
358{
359    fn default() -> Self {
360        Self::new(Default::default())
361    }
362}
363
364impl<T, S> fmt::Debug for Selector<T, S>
365where
366    S: fmt::Debug,
367{
368    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369        f.debug_struct("Selector")
370            .field("tasks", &self.list.len())
371            .field("strategy", &self.strategy)
372            .field("queue_ptr", &Arc::as_ptr(self.queue.queue()))
373            .finish_non_exhaustive()
374    }
375}
376
377impl<T, S> Index<&Id<T>> for Selector<T, S> {
378    type Output = T;
379
380    fn index(&self, id: &Id<T>) -> &Self::Output {
381        self.get(id).expect("task not found").into_pin().get_ref()
382    }
383}
384
385impl<T, S> Index<Id<T>> for Selector<T, S> {
386    type Output = T;
387
388    fn index(&self, id: Id<T>) -> &Self::Output {
389        &self[&id]
390    }
391}
392
393impl<T, S> IndexMut<&Id<T>> for Selector<T, S>
394where
395    T: Unpin,
396{
397    fn index_mut(&mut self, id: &Id<T>) -> &mut Self::Output {
398        self.get_mut(id)
399            .expect("task not found")
400            .into_pin_mut()
401            .get_mut()
402    }
403}
404
405impl<T, S> IndexMut<Id<T>> for Selector<T, S>
406where
407    T: Unpin,
408{
409    fn index_mut(&mut self, id: Id<T>) -> &mut Self::Output {
410        &mut self[&id]
411    }
412}
413
414unsafe impl<T, S> Send for Selector<T, S>
415where
416    T: Send,
417    S: Send,
418{
419}
420
421unsafe impl<T, S> Sync for Selector<T, S>
422where
423    T: Sync,
424    S: Sync,
425{
426}
427
428static_assertions::assert_impl_all!(Selector<(), ()>: Send, Sync);
429static_assertions::assert_impl_all!(Selector<Cell<()>, Cell<()>>: Send);
430static_assertions::assert_not_impl_any!(Selector<Cell<()>, ()>: Sync);
431static_assertions::assert_not_impl_any!(Selector<(), Cell<()>>: Sync);
432static_assertions::assert_not_impl_any!(Selector<Rc<()>, ()>: Send, Sync);
433static_assertions::assert_not_impl_any!(Selector<(), Rc<()>>: Send, Sync);
434
435#[cfg(test)]
436mod test {
437    use std::{
438        ops::ControlFlow,
439        panic::AssertUnwindSafe,
440        pin::Pin,
441        sync::Arc,
442        task::{Context, Poll, Waker},
443    };
444
445    use futures::{FutureExt, StreamExt};
446
447    use crate::{
448        FutureSelector, StreamSelector,
449        selector::{BorrowedMut, Removed, Selector},
450        task::Task,
451    };
452
453    #[test]
454    fn retain_removes_correct_tasks() {
455        let mut selector = (-3_i32..=3).collect::<FutureSelector<_>>();
456        selector.retain(|task| task.is_positive());
457        let retained = selector
458            .into_iter()
459            .map(Removed::into_inner)
460            .collect::<Vec<_>>();
461        assert_eq!(retained, &[1, 2, 3],);
462    }
463
464    #[test]
465    fn extract_if_removes_correct_tasks() {
466        let mut selector = (-3_i32..=3).collect::<FutureSelector<_>>();
467        let iter = selector.extract_if(|task| task.is_positive());
468        assert_eq!(
469            iter.map(Removed::into_inner).collect::<Vec<_>>(),
470            vec![1, 2, 3],
471        );
472        let retained = selector
473            .into_iter()
474            .map(Removed::into_inner)
475            .collect::<Vec<_>>();
476        assert_eq!(retained, &[-3, -2, -1, 0],);
477    }
478
479    #[test]
480    fn extract_if_retains_tasks_when_dropped() {
481        let mut selector = (-3_i32..=3).collect::<FutureSelector<_>>();
482        let iter = selector.extract_if(|task| task.is_positive());
483        assert_eq!(
484            iter.take(1).map(Removed::into_inner).collect::<Vec<_>>(),
485            vec![1],
486        );
487        let retained = selector
488            .into_iter()
489            .map(Removed::into_inner)
490            .collect::<Vec<_>>();
491        assert_eq!(retained, &[-3, -2, -1, 0, 2, 3],);
492    }
493
494    #[test]
495    fn single_selector_poll_polls_each_task_at_most_once() {
496        #[derive(Clone)]
497        struct Task(usize);
498
499        impl Future for Task {
500            type Output = ();
501
502            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
503                let this = self.get_mut();
504                this.0 += 1;
505                cx.waker().wake_by_ref();
506                Poll::Pending
507            }
508        }
509
510        let mut selector = std::iter::repeat_n(Task(0), 3).collect::<FutureSelector<_>>();
511        for i in 0..=3 {
512            selector
513                .iter()
514                .for_each(|borrowed| assert_eq!(borrowed.get_pin().0, i));
515            assert!(
516                selector
517                    .poll_next_unpin(&mut Context::from_waker(Waker::noop()))
518                    .is_pending()
519            );
520        }
521    }
522
523    #[tokio::test]
524    async fn selector_respects_strategy_and_round_robin_order() {
525        #[derive(Clone)]
526        struct MyTask(usize);
527
528        impl Task for MyTask {
529            type Cont = usize;
530            type Break = usize;
531            type Output = usize;
532
533            fn poll_progress(
534                self: Pin<&mut Self>,
535                _: &mut (),
536                _: &mut Context<'_>,
537            ) -> Poll<ControlFlow<Self::Break, Self::Cont>> {
538                let state = self.get_mut();
539                state.0 += 1;
540                if state.0 > 5 {
541                    Poll::Ready(ControlFlow::Break(state.0))
542                } else {
543                    Poll::Ready(ControlFlow::Continue(state.0))
544                }
545            }
546
547            fn transform_cont(
548                _: BorrowedMut<'_, Self>,
549                _: &mut (),
550                value: Self::Cont,
551            ) -> Option<Self::Output> {
552                value.is_power_of_two().then_some(value)
553            }
554
555            fn transform_break(
556                _: Removed<Self>,
557                _: &mut (),
558                value: Self::Break,
559            ) -> Option<Self::Output> {
560                Some(value * 2)
561            }
562        }
563
564        let selector = std::iter::repeat_n(MyTask(0), 3).collect::<Selector<_, ()>>();
565        let results = selector.collect::<Vec<_>>().await;
566        assert_eq!(results, vec![1, 1, 1, 2, 2, 2, 4, 4, 4, 12, 12, 12],);
567    }
568
569    #[tokio::test]
570    async fn selector_returns_valid_ids() {
571        let mut selector = StreamSelector::default();
572        let id_0 = selector.push(futures::stream::repeat(0)).id().clone();
573        let id_1 = selector.push(futures::stream::repeat(1)).id().clone();
574        assert!(selector.contains(&id_0));
575        assert!(selector.contains(&id_1));
576        assert_eq!(selector.next().await.unwrap(), 0);
577        assert_eq!(selector.next().await.unwrap(), 1);
578        assert_eq!(selector.next().await.unwrap(), 0);
579        assert_eq!(selector.next().await.unwrap(), 1);
580        selector.remove(&id_0);
581        assert_eq!(selector.next().await.unwrap(), 1);
582        assert_eq!(selector.next().await.unwrap(), 1);
583    }
584
585    #[tokio::test]
586    async fn selector_handles_strategy_panic() {
587        struct MyTask(usize);
588
589        impl Task for MyTask {
590            type Cont = usize;
591            type Break = usize;
592            type Output = usize;
593
594            fn poll_progress(
595                self: Pin<&mut Self>,
596                _: &mut (),
597                _: &mut Context<'_>,
598            ) -> Poll<ControlFlow<Self::Break, Self::Cont>> {
599                match self.0 {
600                    1 => panic!("poll panic"),
601                    2 => Poll::Ready(ControlFlow::Continue(2)),
602                    n => Poll::Ready(ControlFlow::Break(n)),
603                }
604            }
605
606            fn transform_cont(
607                _: BorrowedMut<'_, Self>,
608                _: &mut (),
609                _: Self::Cont,
610            ) -> Option<Self::Output> {
611                panic!("cont panic")
612            }
613
614            fn transform_break(
615                _: Removed<Self>,
616                _: &mut (),
617                value: Self::Break,
618            ) -> Option<Self::Output> {
619                if value == 3 {
620                    panic!("break panic")
621                } else {
622                    Some(value)
623                }
624            }
625        }
626
627        let mut selector = (0..=4).map(MyTask).collect::<Selector<_, ()>>();
628        assert_eq!(selector.len(), 5);
629
630        assert_eq!(selector.next().await.unwrap(), 0);
631        assert_eq!(selector.len(), 4);
632
633        let err = AssertUnwindSafe(selector.next())
634            .catch_unwind()
635            .await
636            .unwrap_err();
637        assert_eq!(*err.downcast_ref::<&'static str>().unwrap(), "poll panic",);
638        assert_eq!(selector.len(), 3);
639
640        let err = AssertUnwindSafe(selector.next())
641            .catch_unwind()
642            .await
643            .unwrap_err();
644        assert_eq!(*err.downcast_ref::<&'static str>().unwrap(), "cont panic",);
645        assert_eq!(selector.len(), 2);
646
647        let err = AssertUnwindSafe(selector.next())
648            .catch_unwind()
649            .await
650            .unwrap_err();
651        assert_eq!(*err.downcast_ref::<&'static str>().unwrap(), "break panic",);
652        assert_eq!(selector.len(), 1);
653
654        assert_eq!(selector.next().await.unwrap(), 4);
655        assert_eq!(selector.len(), 0);
656    }
657
658    #[tokio::test]
659    async fn selector_handles_drop_panic() {
660        struct Task(usize);
661
662        impl Future for Task {
663            type Output = usize;
664
665            fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
666                Poll::Ready(self.0)
667            }
668        }
669
670        impl Drop for Task {
671            fn drop(&mut self) {
672                if self.0 < 2 {
673                    panic!();
674                }
675            }
676        }
677
678        let mut selector = (0..3).map(Task).collect::<FutureSelector<_>>();
679        let ids = selector
680            .iter()
681            .map(|task| task.id().clone())
682            .collect::<Vec<_>>();
683
684        AssertUnwindSafe(selector.next())
685            .catch_unwind()
686            .await
687            .unwrap_err();
688        assert_eq!(selector.len(), 2);
689        assert_eq!(Arc::strong_count(ids[0].get()), 1);
690
691        let selector = AssertUnwindSafe(selector);
692        std::panic::catch_unwind(|| drop(selector)).unwrap_err();
693
694        for id in ids {
695            assert_eq!(Arc::strong_count(id.get()), 1);
696        }
697    }
698}