11use crate :: enter;
2+ use crate :: park:: { Park , ParkDuration } ;
3+ use crate :: park_thread:: ParkThread ;
24use futures_core:: future:: Future ;
35use futures_core:: stream:: Stream ;
46use futures_core:: task:: { Context , Poll } ;
5- use futures_task:: { waker_ref, ArcWake } ;
67use futures_task:: { FutureObj , LocalFutureObj , LocalSpawn , Spawn , SpawnError } ;
78use futures_util:: stream:: FuturesUnordered ;
89use futures_util:: stream:: StreamExt ;
910use std:: cell:: RefCell ;
1011use std:: ops:: { Deref , DerefMut } ;
1112use std:: pin:: pin;
1213use std:: rc:: { Rc , Weak } ;
13- use std:: sync:: {
14- atomic:: { AtomicBool , Ordering } ,
15- Arc ,
16- } ;
17- use std:: thread:: { self , Thread } ;
1814use std:: vec:: Vec ;
1915
16+ // state outside park
17+ #[ derive( Debug ) ]
18+ struct State {
19+ pool : FuturesUnordered < LocalFutureObj < ' static , ( ) > > ,
20+ incoming : Rc < Incoming > ,
21+ }
22+
23+ impl State {
24+ /// Poll `self.pool`, re-filling it with any newly-spawned tasks.
25+ /// Repeat until either the pool is empty, or it returns `Pending`.
26+ ///
27+ /// Returns `Ready` if the pool was empty, and `Pending` otherwise.
28+ ///
29+ /// NOTE: the pool may call `wake`, so `Pending` doesn't necessarily
30+ /// mean that the pool can't make progress.
31+ fn poll_pool ( & mut self , cx : & mut Context < ' _ > ) -> Poll < ( ) > {
32+ loop {
33+ self . drain_incoming ( ) ;
34+
35+ let pool_ret = self . pool . poll_next_unpin ( cx) ;
36+
37+ // We queued up some new tasks; add them and poll again.
38+ if !self . incoming . borrow ( ) . is_empty ( ) {
39+ continue ;
40+ }
41+
42+ match pool_ret {
43+ Poll :: Ready ( Some ( ( ) ) ) => continue ,
44+ Poll :: Ready ( None ) => return Poll :: Ready ( ( ) ) ,
45+ Poll :: Pending => return Poll :: Pending ,
46+ }
47+ }
48+ }
49+
50+ /// Empty the incoming queue of newly-spawned tasks.
51+ fn drain_incoming ( & mut self ) {
52+ let mut incoming = self . incoming . borrow_mut ( ) ;
53+ for task in incoming. drain ( ..) {
54+ self . pool . push ( task)
55+ }
56+ }
57+
58+ /// Runs all tasks and returns after completing one future or until no more progress
59+ /// can be made. Returns `Ready` if one future was completed, `Pending` otherwise.
60+ fn poll_pool_one ( & mut self , cx : & mut Context < ' _ > ) -> Poll < ( ) > {
61+ loop {
62+ self . drain_incoming ( ) ;
63+
64+ match self . pool . poll_next_unpin ( cx) {
65+ // Success!
66+ Poll :: Ready ( Some ( ( ) ) ) => return Poll :: Ready ( ( ) ) ,
67+ // The pool was empty.
68+ Poll :: Ready ( None ) => return Poll :: Pending ,
69+ Poll :: Pending => ( ) ,
70+ }
71+
72+ if !self . incoming . borrow ( ) . is_empty ( ) {
73+ // New tasks were spawned; try again.
74+ continue ;
75+ } else {
76+ // No more progress for now (but caller should check cx)
77+ return Poll :: Pending ;
78+ }
79+ }
80+ }
81+ }
82+
2083/// A single-threaded task pool for polling futures to completion.
2184///
2285/// This executor allows you to multiplex any number of tasks onto a single
@@ -29,9 +92,9 @@ use std::vec::Vec;
2992/// single-threaded, it supports a special form of task spawning for non-`Send`
3093/// futures, via [`spawn_local_obj`](futures_task::LocalSpawn::spawn_local_obj).
3194#[ derive( Debug ) ]
32- pub struct LocalPool {
33- pool : FuturesUnordered < LocalFutureObj < ' static , ( ) > > ,
34- incoming : Rc < Incoming > ,
95+ pub struct LocalPool < P = ParkThread > {
96+ park : P ,
97+ state : State ,
3598}
3699
37100/// A handle to a [`LocalPool`] that implements [`Spawn`](futures_task::Spawn).
@@ -42,80 +105,56 @@ pub struct LocalSpawner {
42105
43106type Incoming = RefCell < Vec < LocalFutureObj < ' static , ( ) > > > ;
44107
45- pub ( crate ) struct ThreadNotify {
46- /// The (single) executor thread.
47- thread : Thread ,
48- /// A flag to ensure a wakeup (i.e. `unpark()`) is not "forgotten"
49- /// before the next `park()`, which may otherwise happen if the code
50- /// being executed as part of the future(s) being polled makes use of
51- /// park / unpark calls of its own, i.e. we cannot assume that no other
52- /// code uses park / unpark on the executing `thread`.
53- unparked : AtomicBool ,
54- }
55-
56- std:: thread_local! {
57- static CURRENT_THREAD_NOTIFY : Arc <ThreadNotify > = Arc :: new( ThreadNotify {
58- thread: thread:: current( ) ,
59- unparked: AtomicBool :: new( false ) ,
60- } ) ;
61- }
62-
63- impl ArcWake for ThreadNotify {
64- fn wake_by_ref ( arc_self : & Arc < Self > ) {
65- // Make sure the wakeup is remembered until the next `park()`.
66- let unparked = arc_self. unparked . swap ( true , Ordering :: Release ) ;
67- if !unparked {
68- // If the thread has not been unparked yet, it must be done
69- // now. If it was actually parked, it will run again,
70- // otherwise the token made available by `unpark`
71- // may be consumed before reaching `park()`, but `unparked`
72- // ensures it is not forgotten.
73- arc_self. thread . unpark ( ) ;
74- }
75- }
76- }
77-
78- // Set up and run a basic single-threaded spawner loop, invoking `f` on each
79- // turn.
80- fn run_executor < T , F : FnMut ( & mut Context < ' _ > ) -> Poll < T > > ( mut f : F ) -> T {
81- let _enter = enter ( ) . expect (
108+ fn run_executor < P : Park , T , F : FnMut ( & mut Context < ' _ > ) -> Poll < T > > ( park : & mut P , mut f : F ) -> T {
109+ let mut enter = enter ( ) . expect (
82110 "cannot execute `LocalPool` executor from within \
83- another executor",
111+ another executor",
84112 ) ;
85113
86- CURRENT_THREAD_NOTIFY . with ( |thread_notify| {
87- let waker = waker_ref ( thread_notify ) ;
114+ loop {
115+ let waker = park . waker ( ) ;
88116 let mut cx = Context :: from_waker ( & waker) ;
89- loop {
90- if let Poll :: Ready ( t) = f ( & mut cx) {
91- return t;
92- }
93117
94- // Wait for a wakeup.
95- while !thread_notify. unparked . swap ( false , Ordering :: Acquire ) {
96- // No wakeup occurred. It may occur now, right before parking,
97- // but in that case the token made available by `unpark()`
98- // is guaranteed to still be available and `park()` is a no-op.
99- thread:: park ( ) ;
100- }
118+ if let Poll :: Ready ( t) = f ( & mut cx) {
119+ return t;
101120 }
102- } )
103- }
104-
105- /// Check for a wakeup, but don't consume it.
106- fn woken ( ) -> bool {
107- CURRENT_THREAD_NOTIFY . with ( |thread_notify| thread_notify. unparked . load ( Ordering :: Acquire ) )
121+ park. park ( & mut enter, ParkDuration :: Block ) ;
122+ }
108123}
109124
110125impl LocalPool {
111126 /// Create a new, empty pool of tasks.
127+ // Not generic over `P: Default` - would break API (default type not working)
112128 pub fn new ( ) -> Self {
113- Self { pool : FuturesUnordered :: new ( ) , incoming : Default :: default ( ) }
129+ Self :: new_with_park ( ParkThread :: default ( ) )
130+ }
131+ }
132+
133+ impl < P > LocalPool < P >
134+ where
135+ P : Park ,
136+ {
137+ /// Create a new, empty pool of tasks.
138+ pub fn new_with_park ( park : P ) -> Self {
139+ LocalPool {
140+ park,
141+ state : State { pool : FuturesUnordered :: new ( ) , incoming : Default :: default ( ) } ,
142+ }
143+ }
144+
145+ /// Returns a reference to the underlying Park instance.
146+ pub fn get_park ( & self ) -> & P {
147+ & self . park
148+ }
149+
150+ /// Returns a mutable reference to the underlying Park instance.
151+ pub fn get_park_mut ( & mut self ) -> & mut P {
152+ & mut self . park
114153 }
115154
116155 /// Get a clonable handle to the pool as a [`Spawn`].
117156 pub fn spawner ( & self ) -> LocalSpawner {
118- LocalSpawner { incoming : Rc :: downgrade ( & self . incoming ) }
157+ LocalSpawner { incoming : Rc :: downgrade ( & self . state . incoming ) }
119158 }
120159
121160 /// Run all tasks in the pool to completion.
@@ -134,7 +173,7 @@ impl LocalPool {
134173 /// The function will block the calling thread until *all* tasks in the pool
135174 /// are complete, including any spawned while running existing tasks.
136175 pub fn run ( & mut self ) {
137- run_executor ( |cx| self . poll_pool ( cx) )
176+ self . run_executor ( |state , cx| state . poll_pool ( cx) )
138177 }
139178
140179 /// Runs all the tasks in the pool until the given future completes.
@@ -157,7 +196,7 @@ impl LocalPool {
157196 pub fn run_until < F : Future > ( & mut self , future : F ) -> F :: Output {
158197 let mut future = pin ! ( future) ;
159198
160- run_executor ( |cx| {
199+ self . run_executor ( |state , cx| {
161200 {
162201 // if our main task is done, so are we
163202 let result = future. as_mut ( ) . poll ( cx) ;
@@ -166,7 +205,7 @@ impl LocalPool {
166205 }
167206 }
168207
169- let _ = self . poll_pool ( cx) ;
208+ let _ = state . poll_pool ( cx) ;
170209 Poll :: Pending
171210 } )
172211 }
@@ -200,29 +239,7 @@ impl LocalPool {
200239 /// further use of one of the pool's run or poll methods.
201240 /// Though only one task will be completed, progress may be made on multiple tasks.
202241 pub fn try_run_one ( & mut self ) -> bool {
203- run_executor ( |cx| {
204- loop {
205- self . drain_incoming ( ) ;
206-
207- match self . pool . poll_next_unpin ( cx) {
208- // Success!
209- Poll :: Ready ( Some ( ( ) ) ) => return Poll :: Ready ( true ) ,
210- // The pool was empty.
211- Poll :: Ready ( None ) => return Poll :: Ready ( false ) ,
212- Poll :: Pending => ( ) ,
213- }
214-
215- if !self . incoming . borrow ( ) . is_empty ( ) {
216- // New tasks were spawned; try again.
217- continue ;
218- } else if woken ( ) {
219- // The pool yielded to us, but there's more progress to be made.
220- return Poll :: Pending ;
221- } else {
222- return Poll :: Ready ( false ) ;
223- }
224- }
225- } )
242+ self . poll_executor ( |state, cx| state. poll_pool_one ( cx) ) . is_ready ( )
226243 }
227244
228245 /// Runs all tasks in the pool and returns if no more progress can be made
@@ -251,58 +268,51 @@ impl LocalPool {
251268 /// of the pool's run or poll methods. While the function is running, all tasks
252269 /// in the pool will try to make progress.
253270 pub fn run_until_stalled ( & mut self ) {
254- run_executor ( |cx| match self . poll_pool ( cx) {
255- // The pool is empty.
256- Poll :: Ready ( ( ) ) => Poll :: Ready ( ( ) ) ,
257- Poll :: Pending => {
258- if woken ( ) {
259- Poll :: Pending
260- } else {
261- // We're stalled for now.
262- Poll :: Ready ( ( ) )
263- }
264- }
265- } ) ;
271+ // Ready: all tasks finished, Pending: would block
272+ let _ = self . poll_executor ( |state, cx| state. poll_pool ( cx) ) ;
266273 }
267274
268- /// Poll `self.pool`, re-filling it with any newly-spawned tasks.
269- /// Repeat until either the pool is empty, or it returns `Pending` .
270- ///
271- /// Returns `Ready` if the pool was empty, and `Pending` otherwise.
272- ///
273- /// NOTE: the pool may call `wake`, so `Pending` doesn't necessarily
274- /// mean that the pool can't make progress.
275- fn poll_pool ( & mut self , cx : & mut Context < ' _ > ) -> Poll < ( ) > {
276- loop {
277- self . drain_incoming ( ) ;
275+ // Set up and run a basic single-threaded spawner loop, polling `f` on each
276+ // turn until it completes or [`P::park`] fails .
277+ fn run_executor < T , F : FnMut ( & mut State , & mut Context < ' _ > ) -> Poll < T > > (
278+ & mut self ,
279+ mut f : F ,
280+ ) -> T {
281+ let park = & mut self . park ;
282+ let state = & mut self . state ;
283+ run_executor ( park , |cx| f ( state , cx ) )
284+ }
278285
279- let pool_ret = self . pool . poll_next_unpin ( cx) ;
286+ // Set up and run a basic single-threaded spawner loop, polling `f` on each
287+ // turn until it completes or no progress can't be made anymore.
288+ fn poll_executor < T , F : FnMut ( & mut State , & mut Context < ' _ > ) -> Poll < T > > (
289+ & mut self ,
290+ mut f : F ,
291+ ) -> Poll < T > {
292+ let _enter = enter ( ) . expect (
293+ "cannot execute `LocalPool` executor from within \
294+ another executor",
295+ ) ;
280296
281- // We queued up some new tasks; add them and poll again.
282- if !self . incoming . borrow ( ) . is_empty ( ) {
283- continue ;
284- }
297+ loop {
298+ let waker = self . park . waker ( ) ;
299+ let mut cx = Context :: from_waker ( & waker) ;
285300
286- match pool_ret {
287- Poll :: Ready ( Some ( ( ) ) ) => continue ,
288- Poll :: Ready ( None ) => return Poll :: Ready ( ( ) ) ,
289- Poll :: Pending => return Poll :: Pending ,
301+ if let Poll :: Ready ( t) = f ( & mut self . state , & mut cx) {
302+ return Poll :: Ready ( t) ;
303+ }
304+ if !self . park . poll ( ) {
305+ // can't make more progress
306+ return Poll :: Pending ;
290307 }
291- }
292- }
293-
294- /// Empty the incoming queue of newly-spawned tasks.
295- fn drain_incoming ( & mut self ) {
296- let mut incoming = self . incoming . borrow_mut ( ) ;
297- for task in incoming. drain ( ..) {
298- self . pool . push ( task)
299308 }
300309 }
301310}
302311
312+ // Not generic over `P: Default` - would break API (default type not working)
303313impl Default for LocalPool {
304314 fn default ( ) -> Self {
305- Self :: new ( )
315+ Self :: new_with_park ( ParkThread :: default ( ) )
306316 }
307317}
308318
@@ -313,7 +323,7 @@ impl Default for LocalPool {
313323/// Use a [`LocalPool`] if you need finer-grained control over spawned tasks.
314324pub fn block_on < F : Future > ( f : F ) -> F :: Output {
315325 let mut f = pin ! ( f) ;
316- run_executor ( |cx| f. as_mut ( ) . poll ( cx) )
326+ run_executor ( & mut ParkThread :: new ( ) , |cx| f. as_mut ( ) . poll ( cx) )
317327}
318328
319329/// Turn a stream into a blocking iterator.
0 commit comments