@@ -5,8 +5,11 @@ use core::future;
5
5
use core:: pin:: Pin ;
6
6
use core:: task:: { Context , Poll , Waker } ;
7
7
use slab:: Slab ;
8
+ use std:: cell:: LazyCell ;
8
9
use std:: collections:: HashMap ;
9
10
use std:: rc:: Rc ;
11
+ use std:: sync:: Arc ;
12
+ use std:: task:: Wake ;
10
13
use wasi:: io:: poll:: Pollable ;
11
14
12
15
/// A key for a `Pollable`, which is an index into the `Slab<Pollable>` in `Reactor`.
@@ -100,6 +103,7 @@ pub struct Reactor {
100
103
struct InnerReactor {
101
104
pollables : Slab < Pollable > ,
102
105
wakers : HashMap < Waitee , Waker > ,
106
+ immediate : LazyCell < ( Pollable , Waker ) > ,
103
107
}
104
108
105
109
impl Reactor {
@@ -122,6 +126,12 @@ impl Reactor {
122
126
inner : Rc :: new ( RefCell :: new ( InnerReactor {
123
127
pollables : Slab :: new ( ) ,
124
128
wakers : HashMap :: new ( ) ,
129
+ immediate : LazyCell :: new ( || {
130
+ (
131
+ wasi:: clocks:: monotonic_clock:: subscribe_duration ( 0 ) ,
132
+ noop_waker ( ) ,
133
+ )
134
+ } ) ,
125
135
} ) ) ,
126
136
}
127
137
}
@@ -131,30 +141,49 @@ impl Reactor {
131
141
/// # On Wakers and single-threaded runtimes
132
142
///
133
143
/// At first glance it might seem silly that this goes through the motions
134
- /// of calling the wakers. The main waker we create here is a `noop` waker:
135
- /// it does nothing. However, it is common and encouraged to use wakers to
144
+ /// of calling the wakers. However, it is common and encouraged to use wakers to
136
145
/// distinguish between events. Concurrency primitives may construct their
137
146
/// own wakers to keep track of identity and wake more precisely. We do not
138
147
/// control the wakers construted by other libraries, and it is for this
139
148
/// reason that we have to call all the wakers - even if by default they
140
149
/// will do nothing.
141
- pub ( crate ) fn block_until ( & self ) {
150
+ ///
151
+ /// The [awake] argument indicates whether the main task is ready to be re-polled
152
+ /// to make progress. If it is, we will just poll all pollables without blocking
153
+ /// by including an always ready pollable, or choose to skip calling poll at all
154
+ /// if no pollables are registered.
155
+ pub ( crate ) fn block_until ( & self , awake : bool ) {
142
156
let reactor = self . inner . borrow ( ) ;
143
157
158
+ // If no tasks are interested in any pollables currently, and the main task
159
+ // is already awake, run the next poll loop instead
160
+ if reactor. wakers . is_empty ( ) && awake {
161
+ return ;
162
+ }
163
+
144
164
// We're about to wait for a number of pollables. When they wake we get
145
165
// the *indexes* back for the pollables whose events were available - so
146
166
// we need to be able to associate the index with the right waker.
147
167
148
168
// We start by iterating over the pollables, and keeping note of which
149
169
// pollable belongs to which waker
150
- let mut indexed_wakers = Vec :: with_capacity ( reactor. wakers . len ( ) ) ;
151
- let mut targets = Vec :: with_capacity ( reactor. wakers . len ( ) ) ;
170
+ let capacity = reactor. wakers . len ( ) + 1 ;
171
+ let mut indexed_wakers = Vec :: with_capacity ( capacity) ;
172
+ let mut targets = Vec :: with_capacity ( capacity) ;
152
173
for ( waitee, waker) in reactor. wakers . iter ( ) {
153
174
let pollable_index = waitee. pollable . 0 . key ;
154
175
indexed_wakers. push ( waker) ;
155
176
targets. push ( & reactor. pollables [ pollable_index. 0 ] ) ;
156
177
}
157
178
179
+ // If the task is already awake, don't blockingly wait for the pollables,
180
+ // instead ask the host to give us an update immediately
181
+ if awake {
182
+ let ( immediate, waker) = & * reactor. immediate ;
183
+ indexed_wakers. push ( waker) ;
184
+ targets. push ( immediate) ;
185
+ }
186
+
158
187
debug_assert_ne ! (
159
188
targets. len( ) ,
160
189
0 ,
@@ -209,6 +238,18 @@ impl Reactor {
209
238
}
210
239
}
211
240
241
+ /// Construct a new no-op waker
242
+ // NOTE: we can remove this once <https://github.com/rust-lang/rust/issues/98286> lands
243
+ fn noop_waker ( ) -> Waker {
244
+ struct NoopWaker ;
245
+
246
+ impl Wake for NoopWaker {
247
+ fn wake ( self : Arc < Self > ) { }
248
+ }
249
+
250
+ Waker :: from ( Arc :: new ( NoopWaker ) )
251
+ }
252
+
212
253
#[ cfg( test) ]
213
254
mod test {
214
255
use super :: * ;
@@ -281,4 +322,36 @@ mod test {
281
322
. await ;
282
323
} )
283
324
}
325
+
326
+ #[ test]
327
+ fn progresses_wasi_independent_futures ( ) {
328
+ crate :: runtime:: block_on ( async {
329
+ let reactor = Reactor :: current ( ) ;
330
+ let later = wasi:: clocks:: monotonic_clock:: subscribe_duration ( 1_000_000_000 ) ;
331
+ let later = reactor. schedule ( later) ;
332
+ let mut polled_before = false ;
333
+ let wasi_independent_future = futures_lite:: future:: poll_fn ( |cx| {
334
+ if polled_before {
335
+ std:: task:: Poll :: Ready ( true )
336
+ } else {
337
+ polled_before = true ;
338
+ cx. waker ( ) . wake_by_ref ( ) ;
339
+ std:: task:: Poll :: Pending
340
+ }
341
+ } ) ;
342
+ let later = async {
343
+ later. wait_for ( ) . await ;
344
+ false
345
+ } ;
346
+ let wasi_independent_future_won =
347
+ futures_lite:: future:: race ( wasi_independent_future, later) . await ;
348
+ assert ! (
349
+ wasi_independent_future_won,
350
+ "wasi_independent_future should win the race"
351
+ ) ;
352
+ let soon = wasi:: clocks:: monotonic_clock:: subscribe_duration ( 10_000_000 ) ;
353
+ let soon = reactor. schedule ( soon) ;
354
+ soon. wait_for ( ) . await ;
355
+ } )
356
+ }
284
357
}
0 commit comments