From f73ee7778d9e4252403f5c873f295cdbc2906f22 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Wed, 16 Aug 2023 22:25:04 -0600 Subject: [PATCH] Use types to represent a select strategy instead of a boolean --- futures-util/src/future/mod.rs | 3 +++ futures-util/src/future/select.rs | 26 ++++++++++++---------- futures-util/src/future/select_ok.rs | 22 +++++++++--------- futures-util/src/future/select_strategy.rs | 25 +++++++++++++++++++++ futures-util/src/future/try_select.rs | 23 ++++++++++--------- 5 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 futures-util/src/future/select_strategy.rs diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 97a8c8ff7..419d01c3b 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -82,6 +82,9 @@ mod join_all; #[cfg(feature = "alloc")] pub use self::join_all::{join_all, JoinAll}; +mod select_strategy; +pub use select_strategy::{Biased, Fair, IsBiased}; + mod select; pub use self::select::{select, select_biased, Select}; diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs index 7d5eba436..cbce54c3a 100644 --- a/futures-util/src/future/select.rs +++ b/futures-util/src/future/select.rs @@ -1,5 +1,6 @@ -use super::assert_future; +use super::{assert_future, Biased, Fair, IsBiased}; use crate::future::{Either, FutureExt}; +use core::marker::PhantomData; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; @@ -7,12 +8,12 @@ use futures_core::task::{Context, Poll}; /// Future for the [`select()`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] -pub struct Select { +pub struct Select { inner: Option<(A, B)>, - _biased: bool, + _phantom: PhantomData, } -impl Unpin for Select {} +impl Unpin for Select {} /// Waits for either one of two differently-typed futures to complete. /// @@ -86,14 +87,14 @@ impl Unpin for Select {} /// }) /// } /// ``` -pub fn select(future1: A, future2: B) -> Select +pub fn select(future1: A, future2: B) -> Select where A: Future + Unpin, B: Future + Unpin, { assert_future::, _>(Select { inner: Some((future1, future2)), - _biased: false, + _phantom: PhantomData, }) } @@ -167,21 +168,22 @@ where /// }) /// } /// ``` -pub fn select_biased(future1: A, future2: B) -> Select +pub fn select_biased(future1: A, future2: B) -> Select where A: Future + Unpin, B: Future + Unpin, { assert_future::, _>(Select { inner: Some((future1, future2)), - _biased: true, + _phantom: PhantomData, }) } -impl Future for Select +impl Future for Select where A: Future + Unpin, B: Future + Unpin, + BIASED: IsBiased, { type Output = Either<(A::Output, B), (B::Output, A)>; @@ -195,7 +197,6 @@ where Some(value) => value, } } - let _biased = self._biased; let (a, b) = self.inner.as_mut().expect("cannot poll Select twice"); @@ -208,7 +209,7 @@ where } #[cfg(feature = "std")] - if _biased || crate::gen_index(2) == 0 { + if BIASED::IS_BIASED || crate::gen_index(2) == 0 { poll_wrap!(a, unwrap_option(self.inner.take()).1, Either::Left); poll_wrap!(b, unwrap_option(self.inner.take()).0, Either::Right); } else { @@ -225,10 +226,11 @@ where } } -impl FusedFuture for Select +impl FusedFuture for Select where A: Future + Unpin, B: Future + Unpin, + BIASED: IsBiased, { fn is_terminated(&self) -> bool { self.inner.is_none() diff --git a/futures-util/src/future/select_ok.rs b/futures-util/src/future/select_ok.rs index 1aadd9c5e..848cd52b8 100644 --- a/futures-util/src/future/select_ok.rs +++ b/futures-util/src/future/select_ok.rs @@ -1,7 +1,9 @@ use super::assert_future; +use super::{Biased, Fair, IsBiased}; use crate::future::TryFutureExt; use alloc::vec::Vec; use core::iter::FromIterator; +use core::marker::PhantomData; use core::mem; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; @@ -10,12 +12,12 @@ use futures_core::task::{Context, Poll}; /// Future for the [`select_ok`] function. #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct SelectOk { +pub struct SelectOk { inner: Vec, - _biased: bool, + _phantom: PhantomData, } -impl Unpin for SelectOk {} +impl Unpin for SelectOk {} /// Creates a new future which will select the first successful future over a list of futures. /// @@ -41,12 +43,12 @@ impl Unpin for SelectOk {} /// # Panics /// /// This function will panic if the iterator specified contains no items. -pub fn select_ok(iter: I) -> SelectOk +pub fn select_ok(iter: I) -> SelectOk where I: IntoIterator, I::Item: TryFuture + Unpin, { - let ret = SelectOk { inner: iter.into_iter().collect(), _biased: false }; + let ret = SelectOk { inner: iter.into_iter().collect(), _phantom: PhantomData }; assert!(!ret.inner.is_empty(), "iterator provided to select_ok was empty"); assert_future::< Result<(::Ok, Vec), ::Error>, @@ -70,12 +72,12 @@ where /// # Panics /// /// This function will panic if the iterator specified contains no items. -pub fn select_ok_biased(iter: I) -> SelectOk +pub fn select_ok_biased(iter: I) -> SelectOk where I: IntoIterator, I::Item: TryFuture + Unpin, { - let ret = SelectOk { inner: iter.into_iter().collect(), _biased: true }; + let ret = SelectOk { inner: iter.into_iter().collect(), _phantom: PhantomData }; assert!(!ret.inner.is_empty(), "iterator provided to select_ok was empty"); assert_future::< Result<(::Ok, Vec), ::Error>, @@ -83,14 +85,14 @@ where >(ret) } -impl Future for SelectOk { +impl Future for SelectOk { type Output = Result<(Fut::Ok, Vec), Fut::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { inner, _biased } = &mut *self; + let Self { inner, _phantom } = &mut *self; #[cfg(feature = "std")] { - if !*_biased { + if !BIASED::IS_BIASED { crate::shuffle(inner); } } diff --git a/futures-util/src/future/select_strategy.rs b/futures-util/src/future/select_strategy.rs new file mode 100644 index 000000000..4d82e1328 --- /dev/null +++ b/futures-util/src/future/select_strategy.rs @@ -0,0 +1,25 @@ +/// When used with a select future, this will make the future biased. +/// When multiple futures are ready, the winner will be the first one +/// specified. +#[derive(Debug)] +pub struct Biased; + +/// When used with a select future, this will make the future fair. +/// When multiple futures are ready, the winner will be pseudo-randomly +/// selected. This is the default behavior. +#[derive(Debug)] +pub struct Fair; + +/// Reports whether the type is an instance of [`Biased`] or not. +pub trait IsBiased { + /// Contains the answer to our question: is this biased? + const IS_BIASED: bool; +} + +impl IsBiased for Biased { + const IS_BIASED: bool = true; +} + +impl IsBiased for Fair { + const IS_BIASED: bool = false; +} diff --git a/futures-util/src/future/try_select.rs b/futures-util/src/future/try_select.rs index 8927865d2..01d5d8572 100644 --- a/futures-util/src/future/try_select.rs +++ b/futures-util/src/future/try_select.rs @@ -1,4 +1,6 @@ +use super::{Biased, Fair, IsBiased}; use crate::future::{Either, TryFutureExt}; +use core::marker::PhantomData; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; @@ -6,17 +8,17 @@ use futures_core::task::{Context, Poll}; /// Future for the [`try_select()`] function. #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] -pub struct TrySelect { +pub struct TrySelect { inner: Option<(A, B)>, - _biased: bool, + _phantom: PhantomData, } -impl Unpin for TrySelect {} +impl Unpin for TrySelect {} type EitherOk = Either<(::Ok, B), (::Ok, A)>; type EitherErr = Either<(::Error, B), (::Error, A)>; -/// Waits for either one of two differently-typed futures to complete. +/// Waits for either onedsfasdf of two differently-typed futures to complete. /// /// This function will return a new future which awaits for either one of both /// futures to complete. The returned future will finish with both the value @@ -55,14 +57,14 @@ type EitherErr = Either<(::Error, B), (::E /// }) /// } /// ``` -pub fn try_select(future1: A, future2: B) -> TrySelect +pub fn try_select(future1: A, future2: B) -> TrySelect where A: TryFuture + Unpin, B: TryFuture + Unpin, { super::assert_future::, EitherErr>, _>(TrySelect { inner: Some((future1, future2)), - _biased: false, + _phantom: PhantomData, }) } @@ -103,21 +105,22 @@ where /// }) /// } /// ``` -pub fn try_select_biased(future1: A, future2: B) -> TrySelect +pub fn try_select_biased(future1: A, future2: B) -> TrySelect where A: TryFuture + Unpin, B: TryFuture + Unpin, { super::assert_future::, EitherErr>, _>(TrySelect { inner: Some((future1, future2)), - _biased: true, + _phantom: PhantomData, }) } -impl Future for TrySelect +impl Future for TrySelect where A: TryFuture, B: TryFuture, + BIASED: IsBiased, { type Output = Result, EitherErr>; @@ -142,7 +145,7 @@ where #[cfg(feature = "std")] { - if self._biased || crate::gen_index(2) == 0 { + if BIASED::IS_BIASED || crate::gen_index(2) == 0 { poll_wrap!(a, b, Either::Left, Either::Right) } else { poll_wrap!(b, a, Either::Right, Either::Left)