diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-04-05 17:20:16 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-04-05 17:20:16 +0000 |
| commit | f5cf465417741ede43e25d30ae3e09d31398023e (patch) | |
| tree | ac38811cdc04d9f340d2b22fe2a00418cf9bc958 | |
| parent | b0de865e0b4bf5cb6782d61d668125850ceb8e3b (diff) | |
| parent | e42295c4c51a35132595a7579de8467a38bc8171 (diff) | |
Merge #693
693: no_std version of `futures::future::select_all` r=Dirbaio a=alexmoon
Here's a no-std compatible version of `select_all`. It's not quite as useful as the original because it requires an array of Unpin futures to be pre-constructed instead of taking an iterator (which could return `Pin<Box<_>>` in `std`). And, of course, you don't get a `Vec` of the unfinished futures returned at completion. Still, I think it's cleaner than a long cons of select calls.
I'll leave it up to you whether this is sufficiently general purpose to include in Embassy or not.
Co-authored-by: alexmoon <[email protected]>
| -rw-r--r-- | embassy/src/util/mod.rs | 2 | ||||
| -rw-r--r-- | embassy/src/util/select_all.rs | 58 |
2 files changed, 60 insertions, 0 deletions
diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs index 7744778bd..f5cc96238 100644 --- a/embassy/src/util/mod.rs +++ b/embassy/src/util/mod.rs | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | //! Misc utilities | 1 | //! Misc utilities |
| 2 | 2 | ||
| 3 | mod forever; | 3 | mod forever; |
| 4 | mod select_all; | ||
| 4 | mod yield_now; | 5 | mod yield_now; |
| 5 | 6 | ||
| 6 | pub use forever::*; | 7 | pub use forever::*; |
| 8 | pub use select_all::*; | ||
| 7 | pub use yield_now::*; | 9 | pub use yield_now::*; |
| 8 | 10 | ||
| 9 | /// Unsafely unborrow an owned singleton out of a `&mut`. | 11 | /// Unsafely unborrow an owned singleton out of a `&mut`. |
diff --git a/embassy/src/util/select_all.rs b/embassy/src/util/select_all.rs new file mode 100644 index 000000000..aef22d894 --- /dev/null +++ b/embassy/src/util/select_all.rs | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | |||
| 5 | /// Future for the [`select_all`] function. | ||
| 6 | #[derive(Debug)] | ||
| 7 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 8 | pub struct SelectAll<Fut, const N: usize> { | ||
| 9 | inner: [Fut; N], | ||
| 10 | } | ||
| 11 | |||
| 12 | impl<Fut: Unpin, const N: usize> Unpin for SelectAll<Fut, N> {} | ||
| 13 | |||
| 14 | /// Creates a new future which will select over a list of futures. | ||
| 15 | /// | ||
| 16 | /// The returned future will wait for any future within `iter` to be ready. Upon | ||
| 17 | /// completion the item resolved will be returned, along with the index of the | ||
| 18 | /// future that was ready. | ||
| 19 | /// | ||
| 20 | /// # Panics | ||
| 21 | /// | ||
| 22 | /// This function will panic if the array specified contains no items. | ||
| 23 | pub fn select_all<Fut: Future, const N: usize>(arr: [Fut; N]) -> SelectAll<Fut, N> { | ||
| 24 | assert!(N > 0); | ||
| 25 | SelectAll { inner: arr } | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<Fut, const N: usize> SelectAll<Fut, N> { | ||
| 29 | /// Consumes this combinator, returning the underlying futures. | ||
| 30 | pub fn into_inner(self) -> [Fut; N] { | ||
| 31 | self.inner | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | impl<Fut: Future, const N: usize> Future for SelectAll<Fut, N> { | ||
| 36 | type Output = (Fut::Output, usize); | ||
| 37 | |||
| 38 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 39 | // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move, | ||
| 40 | // its elements also cannot move. Therefore it is safe to access `inner` and pin | ||
| 41 | // references to the contained futures. | ||
| 42 | let item = unsafe { | ||
| 43 | self.get_unchecked_mut() | ||
| 44 | .inner | ||
| 45 | .iter_mut() | ||
| 46 | .enumerate() | ||
| 47 | .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) { | ||
| 48 | Poll::Pending => None, | ||
| 49 | Poll::Ready(e) => Some((i, e)), | ||
| 50 | }) | ||
| 51 | }; | ||
| 52 | |||
| 53 | match item { | ||
| 54 | Some((idx, res)) => Poll::Ready((res, idx)), | ||
| 55 | None => Poll::Pending, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
