From 787e4dcbede14d59d9ac95d6ea2fba59d8b960c8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts <2467194+yoshuawuyts@users.noreply.github.com> Date: Mon, 14 Nov 2022 16:13:34 +0100 Subject: [PATCH 1/2] make {array,vec}::race fair --- src/future/race/array.rs | 34 +++++++++++++++++++++------------- src/future/race/vec.rs | 35 ++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/future/race/array.rs b/src/future/race/array.rs index 5360d7c..cb48333 100644 --- a/src/future/race/array.rs +++ b/src/future/race/array.rs @@ -1,3 +1,5 @@ +use crate::utils::{self, RandomGenerator}; + use super::Race as RaceTrait; use core::fmt; @@ -20,7 +22,9 @@ pub struct Race where Fut: Future, { - futs: [Fut; N], + #[pin] + futures: [Fut; N], + rng: RandomGenerator, done: bool, } @@ -30,7 +34,7 @@ where Fut::Output: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.futs.iter()).finish() + f.debug_list().entries(self.futures.iter()).finish() } } @@ -41,16 +45,19 @@ where type Output = Fut::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - assert!( - !*this.done, - "Futures must not be polled after being completed" - ); - for fut in this.futs { - let fut = unsafe { Pin::new_unchecked(fut) }; - if let Poll::Ready(output) = Future::poll(fut, cx) { - *this.done = true; - return Poll::Ready(output); + let mut this = self.project(); + assert!(!*this.done, "Futures must not be polled after completing"); + + let index = this.rng.generate(N as u32) as usize; + + for index in (0..N).map(|pos| (index + pos).wrapping_rem(N)) { + let fut = utils::get_pin_mut(this.futures.as_mut(), index).unwrap(); + match fut.poll(cx) { + Poll::Ready(item) => { + *this.done = true; + return Poll::Ready(item); + } + Poll::Pending => continue, } } Poll::Pending @@ -66,7 +73,8 @@ where fn race(self) -> Self::Future { Race { - futs: self.map(|fut| fut.into_future()), + futures: self.map(|fut| fut.into_future()), + rng: RandomGenerator::new(), done: false, } } diff --git a/src/future/race/vec.rs b/src/future/race/vec.rs index 7c1996e..5478558 100644 --- a/src/future/race/vec.rs +++ b/src/future/race/vec.rs @@ -1,3 +1,5 @@ +use crate::utils::{self, RandomGenerator}; + use super::Race as RaceTrait; use core::fmt; @@ -20,7 +22,9 @@ pub struct Race where Fut: Future, { - futs: Vec, + #[pin] + futures: Vec, + rng: RandomGenerator, done: bool, } @@ -30,7 +34,7 @@ where Fut::Output: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.futs.iter()).finish() + f.debug_list().entries(self.futures.iter()).finish() } } @@ -41,16 +45,20 @@ where type Output = Fut::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - assert!( - !*this.done, - "Futures must not be polled after being completed" - ); - for fut in this.futs { - let fut = unsafe { Pin::new_unchecked(fut) }; - if let Poll::Ready(output) = Future::poll(fut, cx) { - *this.done = true; - return Poll::Ready(output); + let mut this = self.project(); + assert!(!*this.done, "Futures must not be polled after completing"); + + let len = this.futures.len(); + let index = this.rng.generate(len as u32) as usize; + + for index in (0..len).map(|pos| (index + pos).wrapping_rem(len)) { + let fut = utils::get_pin_mut_from_vec(this.futures.as_mut(), index).unwrap(); + match fut.poll(cx) { + Poll::Ready(item) => { + *this.done = true; + return Poll::Ready(item); + } + Poll::Pending => continue, } } Poll::Pending @@ -66,7 +74,8 @@ where fn race(self) -> Self::Future { Race { - futs: self.into_iter().map(|fut| fut.into_future()).collect(), + futures: self.into_iter().map(|fut| fut.into_future()).collect(), + rng: RandomGenerator::new(), done: false, } } From 7d05b37aa37f573ad37fd64fef79b67c978d4677 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts <2467194+yoshuawuyts@users.noreply.github.com> Date: Mon, 14 Nov 2022 16:21:23 +0100 Subject: [PATCH 2/2] fix tests --- src/future/race/array.rs | 2 +- src/future/race/vec.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/future/race/array.rs b/src/future/race/array.rs index cb48333..9ce2fa3 100644 --- a/src/future/race/array.rs +++ b/src/future/race/array.rs @@ -92,7 +92,7 @@ mod test { let res = [future::ready("hello"), future::ready("world")] .race() .await; - assert_eq!(res, "hello"); + assert!(matches!(res, "hello" | "world")); }); } } diff --git a/src/future/race/vec.rs b/src/future/race/vec.rs index 5478558..5a66c2a 100644 --- a/src/future/race/vec.rs +++ b/src/future/race/vec.rs @@ -93,7 +93,7 @@ mod test { let res = vec![future::ready("hello"), future::ready("world")] .race() .await; - assert_eq!(res, "hello"); + assert!(matches!(res, "hello" | "world")); }); } }