1
- use crate :: park:: { Park , Unpark } ;
1
+ use crate :: loom;
2
+ use crate :: loom:: sync:: { Condvar , Mutex } ;
3
+ use crate :: park:: { Park , ParkThread , Unpark } ;
2
4
use crate :: runtime;
3
5
use crate :: runtime:: task:: { self , JoinHandle , Schedule , Task } ;
4
6
use crate :: util:: linked_list:: LinkedList ;
5
- use crate :: util:: { waker_ref, Wake } ;
7
+ use crate :: util:: { waker_ref, Wake , WakerRef } ;
6
8
7
9
use std:: cell:: RefCell ;
8
10
use std:: collections:: VecDeque ;
9
11
use std:: fmt;
10
12
use std:: future:: Future ;
11
- use std:: sync:: { Arc , Mutex } ;
13
+ use std:: sync:: Arc ;
12
14
use std:: task:: Poll :: Ready ;
13
15
use std:: time:: Duration ;
14
16
15
17
/// Executes tasks on the current thread
16
- pub ( crate ) struct BasicScheduler < P >
17
- where
18
- P : Park ,
19
- {
18
+ pub ( crate ) struct BasicScheduler < P : Park > {
19
+ /// Inner with the dedicated parker P
20
+ inner : Mutex < Option < Inner < P > > > ,
21
+
22
+ /// Sync items used to notify other threads that called
23
+ /// block_on concurrently that the dedicated driver is available
24
+ /// to steal
25
+ condvar : loom:: sync:: Arc < Condvar > ,
26
+ mutex : loom:: sync:: Arc < Mutex < ( ) > > ,
27
+
28
+ /// Sendable task spawner
29
+ spawner : Spawner ,
30
+ }
31
+
32
+ struct Inner < P : Park > {
20
33
/// Scheduler run queue
21
34
///
22
35
/// When the scheduler is executed, the queue is removed from `self` and
@@ -59,7 +72,7 @@ struct Shared {
59
72
unpark : Box < dyn Unpark > ,
60
73
}
61
74
62
- /// Thread-local context
75
+ /// Thread-local context.
63
76
struct Context {
64
77
/// Shared scheduler state
65
78
shared : Arc < Shared > ,
@@ -68,16 +81,16 @@ struct Context {
68
81
tasks : RefCell < Tasks > ,
69
82
}
70
83
71
- /// Initial queue capacity
84
+ /// Initial queue capacity.
72
85
const INITIAL_CAPACITY : usize = 64 ;
73
86
74
87
/// Max number of tasks to poll per tick.
75
88
const MAX_TASKS_PER_TICK : usize = 61 ;
76
89
77
- /// How often ot check the remote queue first
90
+ /// How often to check the remote queue first.
78
91
const REMOTE_FIRST_INTERVAL : u8 = 31 ;
79
92
80
- // Tracks the current BasicScheduler
93
+ // Tracks the current BasicScheduler.
81
94
scoped_thread_local ! ( static CURRENT : Context ) ;
82
95
83
96
impl < P > BasicScheduler < P >
@@ -87,19 +100,28 @@ where
87
100
pub ( crate ) fn new ( park : P ) -> BasicScheduler < P > {
88
101
let unpark = Box :: new ( park. unpark ( ) ) ;
89
102
90
- BasicScheduler {
103
+ let spawner = Spawner {
104
+ shared : Arc :: new ( Shared {
105
+ queue : Mutex :: new ( VecDeque :: with_capacity ( INITIAL_CAPACITY ) ) ,
106
+ unpark : unpark as Box < dyn Unpark > ,
107
+ } ) ,
108
+ } ;
109
+
110
+ let inner = Mutex :: new ( Some ( Inner {
91
111
tasks : Some ( Tasks {
92
112
owned : LinkedList :: new ( ) ,
93
113
queue : VecDeque :: with_capacity ( INITIAL_CAPACITY ) ,
94
114
} ) ,
95
- spawner : Spawner {
96
- shared : Arc :: new ( Shared {
97
- queue : Mutex :: new ( VecDeque :: with_capacity ( INITIAL_CAPACITY ) ) ,
98
- unpark : unpark as Box < dyn Unpark > ,
99
- } ) ,
100
- } ,
115
+ spawner : spawner. clone ( ) ,
101
116
tick : 0 ,
102
117
park,
118
+ } ) ) ;
119
+
120
+ BasicScheduler {
121
+ inner,
122
+ spawner,
123
+ condvar : loom:: sync:: Arc :: new ( Condvar :: new ( ) ) ,
124
+ mutex : loom:: sync:: Arc :: new ( Mutex :: new ( ( ) ) ) ,
103
125
}
104
126
}
105
127
@@ -108,7 +130,6 @@ where
108
130
}
109
131
110
132
/// Spawns a future onto the thread pool
111
- #[ allow( dead_code) ]
112
133
pub ( crate ) fn spawn < F > ( & self , future : F ) -> JoinHandle < F :: Output >
113
134
where
114
135
F : Future + Send + ' static ,
@@ -117,13 +138,55 @@ where
117
138
self . spawner . spawn ( future)
118
139
}
119
140
120
- pub ( crate ) fn block_on < F > ( & mut self , future : F ) -> F :: Output
121
- where
122
- F : Future ,
123
- {
141
+ pub ( crate ) fn block_on < F : Future > ( & self , future : F ) -> F :: Output {
142
+ // If we can steal the dedicated parker than lets block_on that
143
+ // otherwise, lets block_on and attempt to steal it back if we can.
144
+ if let Some ( mut inner) = self . take_inner ( ) {
145
+ inner. block_on ( future)
146
+ } else {
147
+ // TODO: should this be false or true? In the origina block_on for
148
+ // basic_scheduler we have false?
149
+ let enter = crate :: runtime:: enter ( false ) ;
150
+
151
+ let mut park = ParkThread :: with_condvar ( self . condvar . clone ( ) , self . mutex . clone ( ) ) ;
152
+ let waker = park. unpark ( ) . into_waker ( ) ;
153
+ let mut cx = std:: task:: Context :: from_waker ( & waker) ;
154
+
155
+ pin ! ( future) ;
156
+
157
+ loop {
158
+ if let Ready ( v) = crate :: coop:: budget ( || future. as_mut ( ) . poll ( & mut cx) ) {
159
+ return v;
160
+ }
161
+
162
+ if let Some ( mut inner) = self . take_inner ( ) {
163
+ // We will enter again on in the inner implementation below
164
+ drop ( enter) ;
165
+ return inner. block_on ( future) ;
166
+ }
167
+
168
+ park. park ( ) . expect ( "failed to park" ) ;
169
+ }
170
+ }
171
+ }
172
+
173
+ fn take_inner ( & self ) -> Option < InnerGuard < ' _ , P > > {
174
+ let mut lock = self . inner . lock ( ) . unwrap ( ) ;
175
+ let inner = lock. take ( ) ?;
176
+
177
+ Some ( InnerGuard {
178
+ inner : Some ( inner) ,
179
+ scheduler : & self ,
180
+ } )
181
+ }
182
+ }
183
+
184
+ impl < P : Park > Inner < P > {
185
+ /// Block on the future provided and drive the runtime's driver.
186
+ fn block_on < F : Future > ( & mut self , future : F ) -> F :: Output {
124
187
enter ( self , |scheduler, context| {
125
188
let _enter = runtime:: enter ( false ) ;
126
- let waker = waker_ref ( & scheduler. spawner . shared ) ;
189
+ let waker = scheduler. spawner . waker_ref ( ) ;
127
190
let mut cx = std:: task:: Context :: from_waker ( & waker) ;
128
191
129
192
pin ! ( future) ;
@@ -178,16 +241,16 @@ where
178
241
179
242
/// Enter the scheduler context. This sets the queue and other necessary
180
243
/// scheduler state in the thread-local
181
- fn enter < F , R , P > ( scheduler : & mut BasicScheduler < P > , f : F ) -> R
244
+ fn enter < F , R , P > ( scheduler : & mut Inner < P > , f : F ) -> R
182
245
where
183
- F : FnOnce ( & mut BasicScheduler < P > , & Context ) -> R ,
246
+ F : FnOnce ( & mut Inner < P > , & Context ) -> R ,
184
247
P : Park ,
185
248
{
186
249
// Ensures the run queue is placed back in the `BasicScheduler` instance
187
250
// once `block_on` returns.`
188
251
struct Guard < ' a , P : Park > {
189
252
context : Option < Context > ,
190
- scheduler : & ' a mut BasicScheduler < P > ,
253
+ scheduler : & ' a mut Inner < P > ,
191
254
}
192
255
193
256
impl < P : Park > Drop for Guard < ' _ , P > {
@@ -214,12 +277,15 @@ where
214
277
CURRENT . set ( context, || f ( scheduler, context) )
215
278
}
216
279
217
- impl < P > Drop for BasicScheduler < P >
218
- where
219
- P : Park ,
220
- {
280
+ impl < P : Park > Drop for BasicScheduler < P > {
221
281
fn drop ( & mut self ) {
222
- enter ( self , |scheduler, context| {
282
+ let mut inner = {
283
+ let mut lock = self . inner . lock ( ) . expect ( "BasicScheduler Inner lock" ) ;
284
+ lock. take ( )
285
+ . expect ( "Oh no! We never placed the Inner state back!" )
286
+ } ;
287
+
288
+ enter ( & mut inner, |scheduler, context| {
223
289
// Loop required here to ensure borrow is dropped between iterations
224
290
#[ allow( clippy:: while_let_loop) ]
225
291
loop {
@@ -269,6 +335,10 @@ impl Spawner {
269
335
fn pop ( & self ) -> Option < task:: Notified < Arc < Shared > > > {
270
336
self . shared . queue . lock ( ) . unwrap ( ) . pop_front ( )
271
337
}
338
+
339
+ fn waker_ref ( & self ) -> WakerRef < ' _ > {
340
+ waker_ref ( & self . shared )
341
+ }
272
342
}
273
343
274
344
impl fmt:: Debug for Spawner {
@@ -325,3 +395,36 @@ impl Wake for Shared {
325
395
arc_self. unpark . unpark ( ) ;
326
396
}
327
397
}
398
+
399
+ // ===== InnerGuard =====
400
+
401
+ /// Used to ensure we always place the Inner value
402
+ /// back into its slot in `BasicScheduler` even if the
403
+ /// future panics.
404
+ struct InnerGuard < ' a , P : Park > {
405
+ inner : Option < Inner < P > > ,
406
+ scheduler : & ' a BasicScheduler < P > ,
407
+ }
408
+
409
+ impl < P : Park > InnerGuard < ' _ , P > {
410
+ fn block_on < F : Future > ( & mut self , future : F ) -> F :: Output {
411
+ // The only time inner gets set to `None` is if we have dropped
412
+ // already so this unwrap is safe.
413
+ self . inner . as_mut ( ) . unwrap ( ) . block_on ( future)
414
+ }
415
+ }
416
+
417
+ impl < P : Park > Drop for InnerGuard < ' _ , P > {
418
+ fn drop ( & mut self ) {
419
+ if let Some ( inner) = self . inner . take ( ) {
420
+ let mut lock = self . scheduler . inner . lock ( ) . unwrap ( ) ;
421
+ lock. replace ( inner) ;
422
+
423
+ // Wake up possible other threads
424
+ // notifying them that they might need
425
+ // to steal the driver.
426
+ drop ( self . scheduler . mutex . lock ( ) . unwrap ( ) ) ;
427
+ self . scheduler . condvar . notify_one ( ) ;
428
+ }
429
+ }
430
+ }
0 commit comments