Skip to content

Commit d761e84

Browse files
committed
implement core::future::join
1 parent 477fd70 commit d761e84

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

library/core/src/future/join.rs

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#![allow(unused_imports)] // items are used by the macro
2+
3+
use crate::cell::UnsafeCell;
4+
use crate::future::{poll_fn, Future};
5+
use crate::pin::Pin;
6+
use crate::task::Poll;
7+
/// Polls multiple futures simultaneously, returning a tuple
8+
/// of all results once complete.
9+
///
10+
/// While `join!(a, b)` is similar to `(a.await, b.await)`,
11+
/// `join!` polls both futures concurrently and is therefore more efficient.
12+
///
13+
/// # Examples
14+
///
15+
/// ```
16+
/// #![feature(future_join, future_poll_fn)]
17+
///
18+
/// use std::future::join;
19+
///
20+
/// async fn one() -> usize { 1 }
21+
/// async fn two() -> usize { 2 }
22+
///
23+
/// # let _ = async {
24+
/// let x = join!(one(), two());
25+
/// assert_eq!(x, (1, 2));
26+
/// # };
27+
/// ```
28+
///
29+
/// `join!` is variadic, so you can pass any number of futures:
30+
///
31+
/// ```
32+
/// #![feature(future_join, future_poll_fn)]
33+
///
34+
/// use std::future::join;
35+
///
36+
/// async fn one() -> usize { 1 }
37+
/// async fn two() -> usize { 2 }
38+
/// async fn three() -> usize { 3 }
39+
///
40+
/// # let _ = async {
41+
/// let x = join!(one(), two(), three());
42+
/// assert_eq!(x, (1, 2, 3));
43+
/// # };
44+
/// ```
45+
#[unstable(feature = "future_join", issue = "91642")]
46+
pub macro join {
47+
( $($fut:expr),* $(,)?) => {
48+
join! { @count: (), @futures: {}, @rest: ($($fut,)*) }
49+
},
50+
// Recurse until we have the position of each future in the tuple
51+
(
52+
// A token for each future that has been expanded: "_ _ _"
53+
@count: ($($count:tt)*),
54+
// Futures and their positions in the tuple: "{ a => (_), b => (_ _)) }"
55+
@futures: { $($fut:tt)* },
56+
// The future currently being expanded, and the rest
57+
@rest: ($current:expr, $($rest:tt)*)
58+
) => {
59+
join! {
60+
@count: ($($count)* _), // Add to the count
61+
@futures: { $($fut)* $current => ($($count)*), }, // Add the future from @rest with it's position
62+
@rest: ($($rest)*) // And leave the rest
63+
}
64+
},
65+
// Now generate the output future
66+
(
67+
@count: ($($count:tt)*),
68+
@futures: {
69+
$( $fut:expr => ( $($pos:tt)* ), )*
70+
},
71+
@rest: ()
72+
) => {{
73+
let mut futures = ( $( MaybeDone::Future($fut), )* );
74+
75+
poll_fn(move |cx| {
76+
let mut done = true;
77+
78+
$(
79+
// Extract the future from the tuple
80+
let ( $($pos,)* fut, .. ) = &mut futures;
81+
82+
// SAFETY: the futures are never moved
83+
done &= unsafe { Pin::new_unchecked(fut).poll(cx).is_ready() };
84+
)*
85+
86+
if done {
87+
Poll::Ready(($({
88+
let ( $($pos,)* fut, .. ) = &mut futures;
89+
90+
// SAFETY: the futures are never moved
91+
unsafe { Pin::new_unchecked(fut).take_output().unwrap() }
92+
}),*))
93+
} else {
94+
Poll::Pending
95+
}
96+
}).await
97+
}}
98+
}
99+
100+
/// Future used by `join!` that stores it's output to
101+
/// be later taken and doesn't panic when polled after ready.
102+
#[allow(dead_code)]
103+
#[unstable(feature = "future_join", issue = "none")]
104+
enum MaybeDone<F: Future> {
105+
Future(F),
106+
Done(F::Output),
107+
Took,
108+
}
109+
110+
#[unstable(feature = "future_join", issue = "none")]
111+
impl<F: Future + Unpin> Unpin for MaybeDone<F> {}
112+
113+
#[unstable(feature = "future_join", issue = "none")]
114+
impl<F: Future> MaybeDone<F> {
115+
#[allow(dead_code)]
116+
fn take_output(self: Pin<&mut Self>) -> Option<F::Output> {
117+
unsafe {
118+
match &*self {
119+
MaybeDone::Done(_) => match mem::replace(self.get_unchecked_mut(), Self::Took) {
120+
MaybeDone::Done(val) => Some(val),
121+
_ => unreachable!(),
122+
},
123+
_ => None,
124+
}
125+
}
126+
}
127+
}
128+
129+
#[unstable(feature = "future_join", issue = "none")]
130+
impl<F: Future> Future for MaybeDone<F> {
131+
type Output = ();
132+
133+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134+
unsafe {
135+
match self.as_mut().get_unchecked_mut() {
136+
MaybeDone::Future(f) => match Pin::new_unchecked(f).poll(cx) {
137+
Poll::Ready(val) => self.set(Self::Done(val)),
138+
Poll::Pending => return Poll::Pending,
139+
},
140+
MaybeDone::Done(_) => {}
141+
MaybeDone::Took => unreachable!(),
142+
}
143+
}
144+
145+
Poll::Ready(())
146+
}
147+
}

library/core/src/future/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ use crate::{
1111

1212
mod future;
1313
mod into_future;
14+
mod join;
1415
mod pending;
1516
mod poll_fn;
1617
mod ready;
1718

1819
#[stable(feature = "futures_api", since = "1.36.0")]
1920
pub use self::future::Future;
2021

22+
#[unstable(feature = "future_join", issue = "91642")]
23+
pub use self::join::join;
24+
2125
#[unstable(feature = "into_future", issue = "67644")]
2226
pub use into_future::IntoFuture;
2327

0 commit comments

Comments
 (0)