1- #![ allow( unused_imports) ] // items are used by the macro
1+ #![ allow( unused_imports, unused_macros ) ] // items are used by the macro
22
33use crate :: cell:: UnsafeCell ;
44use crate :: future:: { poll_fn, Future } ;
@@ -45,59 +45,104 @@ use crate::task::{Context, Poll};
4545/// # };
4646/// ```
4747#[ unstable( feature = "future_join" , issue = "91642" ) ]
48- pub macro join {
49- ( $( $fut: expr) , * $( , ) ?) => {
50- join ! { @count: ( ) , @futures: { } , @rest: ( $( $fut, ) * ) }
51- } ,
52- // Recurse until we have the position of each future in the tuple
48+ pub macro join ( $( $fut: expr) , + $( , ) ? ) {
49+ // Funnel through an internal macro not to leak implementation details.
50+ join_internal ! {
51+ current_position: [ ]
52+ futures_and_positions: [ ]
53+ munching: [ $( $fut) + ]
54+ }
55+ }
56+
57+ // FIXME(danielhenrymantilla): a private macro should need no stability guarantee.
58+ #[ unstable( feature = "future_join" , issue = "91642" ) ]
59+ /// To be able to *name* the i-th future in the tuple (say we want the .4-th),
60+ /// the following trick will be used: `let (_, _, _, _, it, ..) = tuple;`
61+ /// In order to do that, we need to generate a `i`-long repetition of `_`,
62+ /// for each i-th fut. Hence the recursive muncher approach.
63+ macro join_internal {
64+ // Recursion step: map each future with its "position" (underscore count).
5365 (
54- // A token for each future that has been expanded: "_ _ _"
55- @count : ( $( $count: tt) * ) ,
56- // Futures and their positions in the tuple: "{ a => (_), b => (_ _)) }"
57- @futures : { $( $fut: tt) * } ,
58- // Take a future from @rest to expand
59- @rest : ( $current: expr, $( $rest: tt) * )
60- ) => {
61- join ! {
62- @count: ( $( $count) * _) ,
63- @futures: { $( $fut) * $current => ( $( $count) * ) , } ,
64- @rest: ( $( $rest) * )
66+ // Accumulate a token for each future that has been expanded: "_ _ _".
67+ current_position: [
68+ $( $underscores: tt) *
69+ ]
70+ // Accumulate Futures and their positions in the tuple: `_0th () _1st ( _ ) …`.
71+ futures_and_positions: [
72+ $( $acc: tt) *
73+ ]
74+ // Munch one future.
75+ munching: [
76+ $current: tt
77+ $( $rest: tt) *
78+ ]
79+ ) => (
80+ join_internal ! {
81+ current_position: [
82+ $( $underscores) *
83+ _
84+ ]
85+ futures_and_positions: [
86+ $( $acc) *
87+ $current ( $( $underscores) * )
88+ ]
89+ munching: [
90+ $( $rest) *
91+ ]
6592 }
66- } ,
67- // Now generate the output future
68- (
69- @count: ( $( $count: tt) * ) ,
70- @futures : {
71- $( $( @$f: tt) ? $fut: expr => ( $( $pos: tt) * ) , ) *
72- } ,
73- @rest : ( )
74- ) => {
75- async move {
76- let mut futures = ( $( MaybeDone :: Future ( $fut) , ) * ) ;
93+ ) ,
7794
95+ // End of recursion: generate the output future.
96+ (
97+ current_position: $_: tt
98+ futures_and_positions: [
99+ $(
100+ $fut_expr: tt ( $( $pos: tt) * )
101+ ) *
102+ ]
103+ // Nothing left to munch.
104+ munching: [ ]
105+ ) => (
106+ match ( $( MaybeDone :: Future ( $fut_expr) , ) * ) { futures => async {
107+ let mut futures = futures;
108+ // SAFETY: this is `pin_mut!`.
109+ let mut futures = unsafe { Pin :: new_unchecked ( & mut futures) } ;
78110 poll_fn ( move |cx| {
79111 let mut done = true ;
80-
112+ // For each `fut`, pin-project to it, and poll it.
81113 $(
82- let ( $( $pos, ) * fut, .. ) = & mut futures;
83-
84- // SAFETY: The futures are never moved
85- done &= unsafe { Pin :: new_unchecked ( fut) . poll ( cx) . is_ready ( ) } ;
114+ // SAFETY: pinning projection
115+ let fut = unsafe {
116+ futures. as_mut ( ) . map_unchecked_mut ( |it| {
117+ let ( $( $pos, ) * fut, .. ) = it;
118+ fut
119+ } )
120+ } ;
121+ // Despite how tempting it may be to `let () = fut.poll(cx).ready()?;`
122+ // doing so would defeat the point of `join!`: to start polling eagerly all
123+ // of the futures, to allow parallelizing the waits.
124+ done &= fut. poll ( cx) . is_ready ( ) ;
86125 ) *
87-
88- if done {
89- // Extract all the outputs
90- Poll :: Ready ( ( $( {
91- let ( $( $pos, ) * fut, .. ) = & mut futures;
92-
93- fut. take_output ( ) . unwrap ( )
94- } ) , * ) )
95- } else {
96- Poll :: Pending
126+ if !done {
127+ return Poll :: Pending ;
97128 }
129+ // All ready; time to extract all the outputs.
130+
131+ // SAFETY: `.take_output()` does not break the `Pin` invariants for that `fut`.
132+ let futures = unsafe {
133+ futures. as_mut ( ) . get_unchecked_mut ( )
134+ } ;
135+ Poll :: Ready (
136+ ( $(
137+ {
138+ let ( $( $pos, ) * fut, .. ) = & mut * futures;
139+ fut. take_output ( ) . unwrap ( )
140+ }
141+ ) , * ) // <- no trailing comma since we don't want 1-tuples.
142+ )
98143 } ) . await
99- }
100- }
144+ } }
145+ ) ,
101146}
102147
103148/// Future used by `join!` that stores it's output to
@@ -109,14 +154,14 @@ pub macro join {
109154pub enum MaybeDone <F : Future > {
110155 Future ( F ) ,
111156 Done ( F :: Output ) ,
112- Took ,
157+ Taken ,
113158}
114159
115160#[ unstable( feature = "future_join" , issue = "91642" ) ]
116161impl < F : Future > MaybeDone < F > {
117162 pub fn take_output ( & mut self ) -> Option < F :: Output > {
118- match & * self {
119- MaybeDone :: Done ( _) => match mem:: replace ( self , Self :: Took ) {
163+ match * self {
164+ MaybeDone :: Done ( _) => match mem:: replace ( self , Self :: Taken ) {
120165 MaybeDone :: Done ( val) => Some ( val) ,
121166 _ => unreachable ! ( ) ,
122167 } ,
@@ -132,13 +177,14 @@ impl<F: Future> Future for MaybeDone<F> {
132177 fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
133178 // SAFETY: pinning in structural for `f`
134179 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- } ,
180+ // Do not mix match ergonomics with unsafe.
181+ match * self . as_mut ( ) . get_unchecked_mut ( ) {
182+ MaybeDone :: Future ( ref mut f) => {
183+ let val = Pin :: new_unchecked ( f) . poll ( cx) . ready ( ) ?;
184+ self . set ( Self :: Done ( val) ) ;
185+ }
140186 MaybeDone :: Done ( _) => { }
141- MaybeDone :: Took => unreachable ! ( ) ,
187+ MaybeDone :: Taken => unreachable ! ( ) ,
142188 }
143189 }
144190
0 commit comments