Skip to main content

commonware_utils/
futures.rs

1//! Utilities for working with futures.
2
3use core::ops::{Deref, DerefMut};
4use futures::{
5    StreamExt,
6    future::{self, AbortHandle, Abortable, Aborted},
7    stream::{FuturesUnordered, SelectNextSome},
8};
9use pin_project::pin_project;
10use std::{collections::BTreeMap, future::Future, pin::Pin, task::Poll};
11
12/// A future type that can be used in [Pool].
13type PooledFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
14
15/// An unordered pool of futures.
16///
17/// Futures can be added to the pool, and removed from the pool as they resolve.
18///
19/// **Note:** This pool is not thread-safe and should not be used across threads without external
20/// synchronization.
21pub struct Pool<'a, T> {
22    pool: FuturesUnordered<PooledFuture<'a, T>>,
23}
24
25impl<'a, T: Send> Default for Pool<'a, T> {
26    fn default() -> Self {
27        // Insert a dummy future (that never resolves) to prevent the stream from being empty.
28        // Else, the `select_next_some()` function returns `None` instantly.
29        let pool = FuturesUnordered::new();
30        pool.push(Self::create_dummy_future());
31        Self { pool }
32    }
33}
34
35impl<'a, T: Send> Pool<'a, T> {
36    /// Returns the number of futures in the pool.
37    pub fn len(&self) -> usize {
38        // Subtract the dummy future.
39        self.pool.len().checked_sub(1).unwrap()
40    }
41
42    /// Returns `true` if the pool is empty.
43    pub fn is_empty(&self) -> bool {
44        self.len() == 0
45    }
46
47    /// Adds a future to the pool.
48    ///
49    /// The future must be `Send` and outlive `'a` to ensure it can be safely stored and executed.
50    pub fn push(&mut self, future: impl Future<Output = T> + Send + 'a) {
51        self.pool.push(Box::pin(future));
52    }
53
54    /// Returns a futures that resolves to the next future in the pool that resolves.
55    ///
56    /// If the pool is empty, the future will never resolve.
57    pub fn next_completed(&mut self) -> SelectNextSome<'_, FuturesUnordered<PooledFuture<'a, T>>> {
58        self.pool.select_next_some()
59    }
60
61    /// Cancels all futures in the pool.
62    ///
63    /// Excludes the dummy future.
64    pub fn cancel_all(&mut self) {
65        self.pool.clear();
66        self.pool.push(Self::create_dummy_future());
67    }
68
69    /// Creates a dummy future that never resolves.
70    fn create_dummy_future() -> PooledFuture<'a, T> {
71        Box::pin(async { future::pending::<T>().await })
72    }
73}
74
75/// A handle that can be used to abort a specific future in an [AbortablePool].
76///
77/// When the aborter is dropped, the associated future is aborted.
78pub struct Aborter {
79    inner: AbortHandle,
80}
81
82impl Drop for Aborter {
83    fn drop(&mut self) {
84        self.inner.abort();
85    }
86}
87
88/// A future type that can be used in [AbortablePool].
89type AbortablePooledFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, Aborted>> + Send + 'a>>;
90
91/// An unordered pool of futures that can be individually aborted.
92///
93/// Each future added to the pool returns an [Aborter]. When the aborter is dropped,
94/// the associated future is aborted.
95///
96/// **Note:** This pool is not thread-safe and should not be used across threads without external
97/// synchronization.
98pub struct AbortablePool<'a, T> {
99    pool: FuturesUnordered<AbortablePooledFuture<'a, T>>,
100}
101
102impl<'a, T: Send> Default for AbortablePool<'a, T> {
103    fn default() -> Self {
104        // Insert a dummy future (that never resolves) to prevent the stream from being empty.
105        // Else, the `select_next_some()` function returns `None` instantly.
106        let pool = FuturesUnordered::new();
107        pool.push(Self::create_dummy_future());
108        Self { pool }
109    }
110}
111
112impl<'a, T: Send> AbortablePool<'a, T> {
113    /// Returns the number of futures in the pool.
114    pub fn len(&self) -> usize {
115        // Subtract the dummy future.
116        self.pool.len().checked_sub(1).unwrap()
117    }
118
119    /// Returns `true` if the pool is empty.
120    pub fn is_empty(&self) -> bool {
121        self.len() == 0
122    }
123
124    /// Adds a future to the pool and returns an [Aborter] that can be used to abort it.
125    ///
126    /// The future must be `Send` and outlive `'a` to ensure it can be safely stored and executed.
127    /// When the returned [Aborter] is dropped, the future will be aborted.
128    pub fn push(&mut self, future: impl Future<Output = T> + Send + 'a) -> Aborter {
129        let (handle, registration) = AbortHandle::new_pair();
130        let abortable_future = Abortable::new(future, registration);
131        self.pool.push(Box::pin(abortable_future));
132        Aborter { inner: handle }
133    }
134
135    /// Returns a future that resolves to the next future in the pool that resolves.
136    ///
137    /// If the pool is empty, the future will never resolve.
138    /// Returns `Ok(T)` for successful completion or `Err(Aborted)` for aborted futures.
139    pub fn next_completed(
140        &mut self,
141    ) -> SelectNextSome<'_, FuturesUnordered<AbortablePooledFuture<'a, T>>> {
142        self.pool.select_next_some()
143    }
144
145    /// Creates a dummy future that never resolves.
146    fn create_dummy_future() -> AbortablePooledFuture<'a, T> {
147        Box::pin(async { Ok(future::pending::<T>().await) })
148    }
149}
150
151/// An optional future that yields [Poll::Pending] when [None]. Useful within `select!` macros,
152/// where a future may be conditionally present.
153///
154/// Not to be confused with [futures::future::OptionFuture], which resolves to [None] immediately
155/// when the inner future is `None`.
156#[pin_project]
157pub struct OptionFuture<F: Future>(#[pin] Option<F>);
158
159impl<F: Future> Default for OptionFuture<F> {
160    fn default() -> Self {
161        Self(None)
162    }
163}
164
165impl<F: Future> From<Option<F>> for OptionFuture<F> {
166    fn from(opt: Option<F>) -> Self {
167        Self(opt)
168    }
169}
170
171impl<F: Future> Deref for OptionFuture<F> {
172    type Target = Option<F>;
173
174    fn deref(&self) -> &Self::Target {
175        &self.0
176    }
177}
178
179impl<F: Future> DerefMut for OptionFuture<F> {
180    fn deref_mut(&mut self) -> &mut Self::Target {
181        &mut self.0
182    }
183}
184
185impl<F: Future> Future for OptionFuture<F> {
186    type Output = F::Output;
187
188    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
189        let this = self.project();
190        this.0
191            .as_pin_mut()
192            .map_or_else(|| Poll::Pending, |fut| fut.poll(cx))
193    }
194}
195
196/// A consuming mutation's return value: the threaded value first, then any extra outputs.
197pub trait Threaded<T> {
198    /// The outputs beyond the threaded value.
199    type Rest;
200
201    /// Splits into the threaded value and the extra outputs.
202    fn split(self) -> (T, Self::Rest);
203}
204
205impl<T> Threaded<T> for T {
206    type Rest = ();
207
208    fn split(self) -> (T, ()) {
209        (self, ())
210    }
211}
212
213impl<T, A> Threaded<T> for (T, A) {
214    type Rest = A;
215
216    fn split(self) -> (T, A) {
217        self
218    }
219}
220
221impl<T, A, B> Threaded<T> for (T, A, B) {
222    type Rest = (A, B);
223
224    fn split(self) -> (T, (A, B)) {
225        let (value, a, b) = self;
226        (value, (a, b))
227    }
228}
229
230/// Threads the value in `slot` through a consuming mutation, restoring the returned
231/// value and yielding the mutation's extra outputs.
232///
233/// On error the value stays absent, matching the contract of consuming mutators: the
234/// handle is destroyed.
235///
236/// # Panics
237///
238/// Panics when `slot` is empty.
239pub async fn rebind<T, Out, Fut, E>(
240    slot: &mut Option<T>,
241    op: impl FnOnce(T) -> Fut,
242) -> Result<Out::Rest, E>
243where
244    Out: Threaded<T>,
245    Fut: Future<Output = Result<Out, E>>,
246{
247    let value = slot.take().expect("cannot rebind an empty slot");
248    let (value, rest) = op(value).await?.split();
249    *slot = Some(value);
250    Ok(rest)
251}
252
253/// Threads the value at `key` in `map` through a consuming mutation, restoring the
254/// returned value and yielding the mutation's extra outputs.
255///
256/// On error the entry stays absent, matching the contract of consuming mutators: the
257/// handle is destroyed.
258///
259/// # Panics
260///
261/// Panics when `key` is absent from `map`.
262pub async fn rebind_entry<K, V, Out, Fut, E>(
263    map: &mut BTreeMap<K, V>,
264    key: &K,
265    op: impl FnOnce(V) -> Fut,
266) -> Result<Out::Rest, E>
267where
268    K: Ord,
269    Out: Threaded<V>,
270    Fut: Future<Output = Result<Out, E>>,
271{
272    let (key, value) = map
273        .remove_entry(key)
274        .expect("cannot rebind a missing entry");
275    let (value, rest) = op(value).await?.split();
276    map.insert(key, value);
277    Ok(rest)
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283    use crate::channel::oneshot;
284    use futures::{
285        executor::block_on,
286        future::{self, Either, select},
287        pin_mut,
288    };
289    use std::{
290        sync::{
291            Arc,
292            atomic::{AtomicBool, Ordering},
293        },
294        thread,
295        time::Duration,
296    };
297
298    /// A future that resolves after a given duration.
299    fn delay(duration: Duration) -> impl Future<Output = ()> {
300        let (sender, receiver) = oneshot::channel();
301        thread::spawn(move || {
302            thread::sleep(duration);
303            sender.send(()).unwrap();
304        });
305        async move {
306            let _ = receiver.await;
307        }
308    }
309
310    #[test]
311    fn test_initialization() {
312        let pool = Pool::<i32>::default();
313        assert_eq!(pool.len(), 0);
314        assert!(pool.is_empty());
315    }
316
317    #[test]
318    fn test_dummy_future_doesnt_resolve() {
319        block_on(async {
320            let mut pool = Pool::<i32>::default();
321            let stream_future = pool.next_completed();
322            let timeout_future = async {
323                delay(Duration::from_millis(100)).await;
324            };
325            pin_mut!(stream_future);
326            pin_mut!(timeout_future);
327            let result = select(stream_future, timeout_future).await;
328            match result {
329                Either::Left((_, _)) => panic!("Stream resolved unexpectedly"),
330                Either::Right((_, _)) => {
331                    // Timeout occurred, which is expected
332                }
333            }
334        });
335    }
336
337    #[test]
338    fn test_adding_futures() {
339        let mut pool = Pool::<i32>::default();
340        assert_eq!(pool.len(), 0);
341        assert!(pool.is_empty());
342
343        pool.push(async { 42 });
344        assert_eq!(pool.len(), 1);
345        assert!(!pool.is_empty(),);
346
347        pool.push(async { 43 });
348        assert_eq!(pool.len(), 2,);
349    }
350
351    #[test]
352    fn test_streaming_resolved_futures() {
353        block_on(async move {
354            let mut pool = Pool::<i32>::default();
355            pool.push(future::ready(42));
356            let result = pool.next_completed().await;
357            assert_eq!(result, 42,);
358            assert!(pool.is_empty(),);
359        });
360    }
361
362    #[test]
363    fn test_multiple_futures() {
364        block_on(async move {
365            let mut pool = Pool::<i32>::default();
366
367            // Futures resolve in order of completion, not addition order
368            let (finisher_1, finished_1) = oneshot::channel();
369            let (finisher_3, finished_3) = oneshot::channel();
370            pool.push(async move {
371                finished_1.await.unwrap();
372                finisher_3.send(()).unwrap();
373                1
374            });
375            pool.push(async move {
376                finisher_1.send(()).unwrap();
377                2
378            });
379            pool.push(async move {
380                finished_3.await.unwrap();
381                3
382            });
383
384            let first = pool.next_completed().await;
385            assert_eq!(first, 2, "First resolved should be 2");
386            let second = pool.next_completed().await;
387            assert_eq!(second, 1, "Second resolved should be 1");
388            let third = pool.next_completed().await;
389            assert_eq!(third, 3, "Third resolved should be 3");
390            assert!(pool.is_empty(),);
391        });
392    }
393
394    #[test]
395    fn test_cancel_all() {
396        block_on(async move {
397            let flag = Arc::new(AtomicBool::new(false));
398            let flag_clone = flag.clone();
399            let mut pool = Pool::<i32>::default();
400
401            // Push a future that will set the flag to true when it resolves.
402            let (finisher, finished) = oneshot::channel();
403            pool.push(async move {
404                finished.await.unwrap();
405                flag_clone.store(true, Ordering::SeqCst);
406                42
407            });
408            assert_eq!(pool.len(), 1);
409
410            // Cancel all futures.
411            pool.cancel_all();
412            assert!(pool.is_empty());
413            assert!(!flag.load(Ordering::SeqCst));
414
415            // Send the finisher signal (should be ignored).
416            let _ = finisher.send(());
417
418            // Stream should not resolve future after cancellation.
419            let stream_future = pool.next_completed();
420            let timeout_future = async {
421                delay(Duration::from_millis(100)).await;
422            };
423            pin_mut!(stream_future);
424            pin_mut!(timeout_future);
425            let result = select(stream_future, timeout_future).await;
426            match result {
427                Either::Left((_, _)) => panic!("Stream resolved after cancellation"),
428                Either::Right((_, _)) => {
429                    // Wait for the timeout to trigger.
430                }
431            }
432            assert!(!flag.load(Ordering::SeqCst));
433
434            // Push and await a new future.
435            pool.push(future::ready(42));
436            assert_eq!(pool.len(), 1);
437            let result = pool.next_completed().await;
438            assert_eq!(result, 42);
439            assert!(pool.is_empty());
440        });
441    }
442
443    #[test]
444    fn test_many_futures() {
445        block_on(async move {
446            let mut pool = Pool::<i32>::default();
447            let num_futures = 1000;
448            for i in 0..num_futures {
449                pool.push(future::ready(i));
450            }
451            assert_eq!(pool.len(), num_futures as usize);
452
453            let mut sum = 0;
454            for _ in 0..num_futures {
455                let value = pool.next_completed().await;
456                sum += value;
457            }
458            let expected_sum = (0..num_futures).sum::<i32>();
459            assert_eq!(
460                sum, expected_sum,
461                "Sum of resolved values should match expected"
462            );
463            assert!(
464                pool.is_empty(),
465                "Pool should be empty after all futures resolve"
466            );
467        });
468    }
469
470    #[test]
471    fn test_borrowing_futures() {
472        block_on(async {
473            let values = vec![1, 2, 3];
474
475            // The pool borrows `values`, so it cannot outlive it.
476            let mut pool = Pool::<&i32>::default();
477            for value in &values {
478                pool.push(async move { value });
479            }
480            assert_eq!(pool.len(), values.len());
481
482            let mut sum = 0;
483            for _ in 0..values.len() {
484                sum += *pool.next_completed().await;
485            }
486            assert_eq!(sum, 6);
487            assert!(pool.is_empty());
488        });
489    }
490
491    #[test]
492    fn test_abortable_pool_initialization() {
493        let pool = AbortablePool::<i32>::default();
494        assert_eq!(pool.len(), 0);
495        assert!(pool.is_empty());
496    }
497
498    #[test]
499    fn test_abortable_pool_adding_futures() {
500        let mut pool = AbortablePool::<i32>::default();
501        assert_eq!(pool.len(), 0);
502        assert!(pool.is_empty());
503
504        let _hook1 = pool.push(async { 42 });
505        assert_eq!(pool.len(), 1);
506        assert!(!pool.is_empty());
507
508        let _hook2 = pool.push(async { 43 });
509        assert_eq!(pool.len(), 2);
510    }
511
512    #[test]
513    fn test_abortable_pool_successful_completion() {
514        block_on(async move {
515            let mut pool = AbortablePool::<i32>::default();
516            let _hook = pool.push(future::ready(42));
517            let result = pool.next_completed().await;
518            assert_eq!(result, Ok(42));
519            assert!(pool.is_empty());
520        });
521    }
522
523    #[test]
524    fn test_abortable_pool_aborts_pre_polled_ready_future() {
525        block_on(async move {
526            let mut pool = AbortablePool::<i32>::default();
527            let hook = pool.push(future::ready(42));
528            drop(hook);
529            let result = pool.next_completed().await;
530            assert!(result.is_err());
531        });
532    }
533
534    #[test]
535    fn test_abortable_pool_drop_abort() {
536        block_on(async move {
537            let mut pool = AbortablePool::<i32>::default();
538
539            let (sender, receiver) = oneshot::channel();
540            let hook = pool.push(async move {
541                receiver.await.unwrap();
542                42
543            });
544
545            drop(hook);
546
547            let result = pool.next_completed().await;
548            assert!(result.is_err());
549            assert!(pool.is_empty());
550
551            let _ = sender.send(());
552        });
553    }
554
555    #[test]
556    fn test_abortable_pool_partial_abort() {
557        block_on(async move {
558            let mut pool = AbortablePool::<i32>::default();
559
560            let _hook1 = pool.push(future::ready(1));
561            let (sender, receiver) = oneshot::channel();
562            let hook2 = pool.push(async move {
563                receiver.await.unwrap();
564                2
565            });
566            let _hook3 = pool.push(future::ready(3));
567
568            assert_eq!(pool.len(), 3);
569
570            drop(hook2);
571
572            let mut results = Vec::new();
573            for _ in 0..3 {
574                let result = pool.next_completed().await;
575                results.push(result);
576            }
577
578            let successful: Vec<_> = results.iter().filter_map(|r| r.as_ref().ok()).collect();
579            let aborted: Vec<_> = results.iter().filter(|r| r.is_err()).collect();
580
581            assert_eq!(successful.len(), 2);
582            assert_eq!(aborted.len(), 1);
583            assert!(successful.contains(&&1));
584            assert!(successful.contains(&&3));
585            assert!(pool.is_empty());
586
587            let _ = sender.send(());
588        });
589    }
590
591    #[test]
592    fn test_abortable_pool_borrowing_futures() {
593        block_on(async {
594            let value = 42;
595            let mut pool = AbortablePool::<&i32>::default();
596
597            // A borrowing future is aborted by its aborter, which holds no borrow itself.
598            let (sender, receiver) = oneshot::channel::<()>();
599            let hook = pool.push(async {
600                receiver.await.unwrap();
601                &value
602            });
603            drop(hook);
604            assert!(pool.next_completed().await.is_err());
605
606            let _hook = pool.push(async { &value });
607            assert_eq!(pool.next_completed().await, Ok(&42));
608            assert!(pool.is_empty());
609
610            let _ = sender.send(());
611        });
612    }
613
614    #[test]
615    fn test_rebind_restores_value_and_yields_rest() {
616        block_on(async {
617            let mut slot = Some(1u32);
618            let rest: Result<(&str, bool), &str> = rebind(&mut slot, |value| {
619                future::ready(Ok((value + 1, "rest", true)))
620            })
621            .await;
622            assert_eq!(rest, Ok(("rest", true)));
623            assert_eq!(slot, Some(2));
624
625            let rest: Result<(), &str> =
626                rebind(&mut slot, |value| future::ready(Ok(value + 1))).await;
627            assert_eq!(rest, Ok(()));
628            assert_eq!(slot, Some(3));
629        });
630    }
631
632    #[test]
633    fn test_rebind_error_destroys_value() {
634        block_on(async {
635            let mut slot = Some(1u32);
636            let rest: Result<(), &str> =
637                rebind(&mut slot, |_| future::ready(Err::<u32, _>("failed"))).await;
638            assert_eq!(rest, Err("failed"));
639            assert_eq!(slot, None);
640        });
641    }
642
643    #[test]
644    #[should_panic(expected = "cannot rebind an empty slot")]
645    fn test_rebind_empty_slot_panics() {
646        block_on(async {
647            let mut slot: Option<u32> = None;
648            let _: Result<(), &str> = rebind(&mut slot, |v| future::ready(Ok(v))).await;
649        });
650    }
651
652    #[test]
653    fn test_rebind_entry_restores_value_and_yields_rest() {
654        block_on(async {
655            let mut map = BTreeMap::from([("a", 1u32), ("b", 10)]);
656            let rest: Result<bool, &str> =
657                rebind_entry(&mut map, &"a", |value| future::ready(Ok((value + 1, true)))).await;
658            assert_eq!(rest, Ok(true));
659            assert_eq!(map, BTreeMap::from([("a", 2), ("b", 10)]));
660        });
661    }
662
663    #[test]
664    fn test_rebind_entry_error_destroys_value() {
665        block_on(async {
666            let mut map = BTreeMap::from([("a", 1u32)]);
667            let rest: Result<(), &str> =
668                rebind_entry(&mut map, &"a", |_| future::ready(Err::<u32, _>("failed"))).await;
669            assert_eq!(rest, Err("failed"));
670            assert!(map.is_empty());
671        });
672    }
673
674    #[test]
675    #[should_panic(expected = "cannot rebind a missing entry")]
676    fn test_rebind_entry_missing_entry_panics() {
677        block_on(async {
678            let mut map: BTreeMap<&str, u32> = BTreeMap::new();
679            let _: Result<(), &str> = rebind_entry(&mut map, &"a", |v| future::ready(Ok(v))).await;
680        });
681    }
682
683    #[test]
684    fn test_option_future() {
685        block_on(async {
686            let option_future = OptionFuture::<oneshot::Receiver<()>>::from(None);
687            pin_mut!(option_future);
688
689            let waker = futures::task::noop_waker();
690            let mut cx = std::task::Context::from_waker(&waker);
691            assert!(option_future.poll(&mut cx).is_pending());
692
693            let (tx, rx) = oneshot::channel();
694            let option_future: OptionFuture<_> = Some(rx).into();
695            pin_mut!(option_future);
696
697            tx.send(1usize).unwrap();
698            assert_eq!(option_future.poll(&mut cx), Poll::Ready(Ok(1)));
699        });
700    }
701}