1
- #![ allow( unused_imports) ] // items are used by the macro
1
+ #![ allow( unused_imports, unused_macros ) ] // items are used by the macro
2
2
3
3
use crate :: cell:: UnsafeCell ;
4
4
use crate :: future:: { poll_fn, Future } ;
@@ -45,59 +45,104 @@ use crate::task::{Context, Poll};
45
45
/// # };
46
46
/// ```
47
47
#[ 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).
53
65
(
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
+ ]
65
92
}
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
+ ) ,
77
94
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) } ;
78
110
poll_fn ( move |cx| {
79
111
let mut done = true ;
80
-
112
+ // For each `fut`, pin-project to it, and poll it.
81
113
$(
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 ( ) ;
86
125
) *
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 ;
97
128
}
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
+ )
98
143
} ) . await
99
- }
100
- }
144
+ } }
145
+ ) ,
101
146
}
102
147
103
148
/// Future used by `join!` that stores it's output to
@@ -109,14 +154,14 @@ pub macro join {
109
154
pub enum MaybeDone <F : Future > {
110
155
Future ( F ) ,
111
156
Done ( F :: Output ) ,
112
- Took ,
157
+ Taken ,
113
158
}
114
159
115
160
#[ unstable( feature = "future_join" , issue = "91642" ) ]
116
161
impl < F : Future > MaybeDone < F > {
117
162
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 ) {
120
165
MaybeDone :: Done ( val) => Some ( val) ,
121
166
_ => unreachable ! ( ) ,
122
167
} ,
@@ -132,13 +177,14 @@ impl<F: Future> Future for MaybeDone<F> {
132
177
fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
133
178
// SAFETY: pinning in structural for `f`
134
179
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
+ }
140
186
MaybeDone :: Done ( _) => { }
141
- MaybeDone :: Took => unreachable ! ( ) ,
187
+ MaybeDone :: Taken => unreachable ! ( ) ,
142
188
}
143
189
}
144
190
0 commit comments