1use std::fmt;
6use std::future::Future;
7use std::pin::Pin;
8use std::task::{Context, Poll};
9
10use crate::time::SimDuration;
11use crate::triggers::Timer;
12
13pub enum Either<A, B> {
14 First(A),
15 Second(B),
16}
17
18pub struct First2<'a, A, B> {
25 a: Pin<Box<dyn Future<Output = A> + 'a>>,
26 b: Pin<Box<dyn Future<Output = B> + 'a>>,
27}
28
29pub fn first2<'a, FA, FB>(a: FA, b: FB) -> First2<'a, FA::Output, FB::Output>
30where
31 FA: Future + 'a,
32 FB: Future + 'a,
33{
34 First2 {
35 a: Box::pin(a),
36 b: Box::pin(b),
37 }
38}
39
40impl<A, B> Future for First2<'_, A, B> {
41 type Output = Either<A, B>;
42 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
43 if let Poll::Ready(v) = self.a.as_mut().poll(cx) {
44 return Poll::Ready(Either::First(v));
45 }
46 if let Poll::Ready(v) = self.b.as_mut().poll(cx) {
47 return Poll::Ready(Either::Second(v));
48 }
49 Poll::Pending
50 }
51}
52
53pub struct Join2<'a, A, B> {
55 a: Pin<Box<dyn Future<Output = A> + 'a>>,
56 b: Pin<Box<dyn Future<Output = B> + 'a>>,
57 ra: Option<A>,
58 rb: Option<B>,
59}
60
61pub fn join2<'a, FA, FB>(a: FA, b: FB) -> Join2<'a, FA::Output, FB::Output>
62where
63 FA: Future + 'a,
64 FB: Future + 'a,
65{
66 Join2 {
67 a: Box::pin(a),
68 b: Box::pin(b),
69 ra: None,
70 rb: None,
71 }
72}
73
74impl<A, B> Future for Join2<'_, A, B> {
75 type Output = (A, B);
76 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<(A, B)> {
77 let this = unsafe { self.get_unchecked_mut() };
80 if this.ra.is_none()
81 && let Poll::Ready(v) = this.a.as_mut().poll(cx)
82 {
83 this.ra = Some(v);
84 }
85 if this.rb.is_none()
86 && let Poll::Ready(v) = this.b.as_mut().poll(cx)
87 {
88 this.rb = Some(v);
89 }
90 if this.ra.is_some() && this.rb.is_some() {
91 Poll::Ready((this.ra.take().unwrap(), this.rb.take().unwrap()))
92 } else {
93 Poll::Pending
94 }
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub struct TimeoutError;
100
101impl fmt::Display for TimeoutError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "operation timed out")
104 }
105}
106impl std::error::Error for TimeoutError {}
107
108pub async fn with_timeout<'a, F>(fut: F, d: SimDuration) -> Result<F::Output, TimeoutError>
110where
111 F: Future + 'a,
112{
113 match first2(fut, Timer::new(d)).await {
114 Either::First(v) => Ok(v),
115 Either::Second(()) => Err(TimeoutError),
116 }
117}
118
119pub struct JoinAll<'a, T> {
127 futs: Vec<Option<Pin<Box<dyn Future<Output = T> + 'a>>>>,
128 out: Vec<Option<T>>,
129}
130
131pub fn join_all<'a, T>(futs: Vec<Pin<Box<dyn Future<Output = T> + 'a>>>) -> JoinAll<'a, T> {
132 let n = futs.len();
133 JoinAll {
134 futs: futs.into_iter().map(Some).collect(),
135 out: (0..n).map(|_| None).collect(),
136 }
137}
138
139impl<T> Future for JoinAll<'_, T> {
140 type Output = Vec<T>;
141 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Vec<T>> {
142 let this = unsafe { self.get_unchecked_mut() };
145 let mut all_done = true;
146 for (i, slot) in this.futs.iter_mut().enumerate() {
147 if let Some(f) = slot {
148 match f.as_mut().poll(cx) {
149 Poll::Ready(v) => {
150 this.out[i] = Some(v);
151 *slot = None; }
153 Poll::Pending => all_done = false,
154 }
155 }
156 }
157 if all_done {
158 Poll::Ready(this.out.iter_mut().map(|o| o.take().unwrap()).collect())
159 } else {
160 Poll::Pending
161 }
162 }
163}
164
165#[macro_export]
167macro_rules! first {
168 ($a:expr, $b:expr $(,)?) => {
169 $crate::combinators::first2($a, $b)
170 };
171 ($a:expr, $b:expr, $($rest:expr),+ $(,)?) => {
172 $crate::combinators::first2($a, $crate::first!($b, $($rest),+))
173 };
174}
175
176#[macro_export]
178macro_rules! join {
179 ($a:expr, $b:expr $(,)?) => {
180 $crate::combinators::join2($a, $b)
181 };
182 ($a:expr, $b:expr, $($rest:expr),+ $(,)?) => {
183 $crate::combinators::join2($a, $crate::join!($b, $($rest),+))
184 };
185}
186
187#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::testing::block_on;
195 use std::cell::RefCell;
196 use std::rc::Rc;
197
198 #[test]
199 fn join2_yields_both() {
200 block_on(async {
201 let (a, b) = join2(async { 1u8 }, async { "two" }).await;
202 assert_eq!(a, 1);
203 assert_eq!(b, "two");
204 });
205 }
206
207 #[test]
208 fn join_all_preserves_input_order() {
209 block_on(async {
210 let futs: Vec<Pin<Box<dyn Future<Output = u8>>>> = vec![
211 Box::pin(async { 1 }),
212 Box::pin(async { 2 }),
213 Box::pin(async { 3 }),
214 ];
215 assert_eq!(join_all(futs).await, vec![1, 2, 3]);
216 });
217 }
218
219 #[test]
220 fn first2_returns_the_winner() {
221 block_on(async {
222 let ev = crate::sync::Event::new();
223 let waiter = ev.clone();
224 crate::executor::spawn(async move {
225 ev.set();
226 });
227 match first2(async { 7u8 }, async move { waiter.wait().await }).await {
228 Either::First(v) => assert_eq!(v, 7),
229 Either::Second(()) => panic!("the ready future should have won"),
230 }
231 });
232 }
233
234 #[test]
238 fn first2_drops_the_loser() {
239 struct Tattle(Rc<RefCell<bool>>);
240 impl Drop for Tattle {
241 fn drop(&mut self) {
242 *self.0.borrow_mut() = true;
243 }
244 }
245
246 let dropped = Rc::new(RefCell::new(false));
247 let flag = dropped.clone();
248 block_on(async move {
249 let never = crate::sync::Event::new();
250 let tattle = Tattle(flag);
254 let loser = async move {
255 let _t = tattle;
256 never.wait().await;
257 };
258 let _ = first2(async { 1u8 }, loser).await;
259 });
260 assert!(
261 *dropped.borrow(),
262 "the losing future was dropped, not left running"
263 );
264 }
265
266 #[test]
270 fn joined_futures_may_borrow() {
271 block_on(async {
272 let owned = [1u8, 2, 3];
273 let borrow_a = async { owned.len() };
274 let borrow_b = async { owned[0] as usize };
275 let (a, b) = join2(borrow_a, borrow_b).await;
276 assert_eq!((a, b), (3, 1));
277 });
278 }
279}