@@ -5,26 +5,27 @@ use std::rc::Rc;
5
5
use wasm_bindgen:: prelude:: * ;
6
6
7
7
struct QueueState {
8
- // The queue of Tasks which will be run in order. In practice this is all the
8
+ // The queue of Tasks which are to be run in order. In practice this is all the
9
9
// synchronous work of futures, and each `Task` represents calling `poll` on
10
- // a future "at the right time"
10
+ // a future "at the right time".
11
11
tasks : RefCell < VecDeque < Rc < crate :: task:: Task > > > ,
12
12
13
- // This flag indicates whether we're currently executing inside of
14
- // `run_all` or have scheduled `run_all` to run in the future. This is
15
- // used to ensure that it's only scheduled once.
16
- is_spinning : Cell < bool > ,
13
+ // This flag indicates whether we've scheduled `run_all` to run in the future.
14
+ // This is used to ensure that it's only scheduled once.
15
+ is_scheduled : Cell < bool > ,
17
16
}
18
17
19
18
impl QueueState {
20
19
fn run_all ( & self ) {
21
- debug_assert ! ( self . is_spinning. get( ) ) ;
20
+ // "consume" the schedule
21
+ let _was_scheduled = self . is_scheduled . replace ( false ) ;
22
+ debug_assert ! ( _was_scheduled) ;
22
23
23
- // Runs all Tasks until empty. This blocks the event loop if a Future is
24
- // stuck in an infinite loop, so we may want to yield back to the main
25
- // event loop occasionally. For now though greedy execution should get
26
- // the job done.
27
- loop {
24
+ // Stop when all tasks that have been scheduled before this tick have been run.
25
+ // Tasks that are scheduled while running tasks will run on the next tick.
26
+ let mut task_count_left = self . tasks . borrow ( ) . len ( ) ;
27
+ while task_count_left > 0 {
28
+ task_count_left -= 1 ;
28
29
let task = match self . tasks . borrow_mut ( ) . pop_front ( ) {
29
30
Some ( task) => task,
30
31
None => break ,
@@ -34,7 +35,6 @@ impl QueueState {
34
35
35
36
// All of the Tasks have been run, so it's now possible to schedule the
36
37
// next tick again
37
- self . is_spinning . set ( false ) ;
38
38
}
39
39
}
40
40
@@ -45,26 +45,28 @@ pub(crate) struct Queue {
45
45
}
46
46
47
47
impl Queue {
48
- pub ( crate ) fn push_task ( & self , task : Rc < crate :: task:: Task > ) {
48
+ // Schedule a task to run on the next tick
49
+ pub ( crate ) fn schedule_task ( & self , task : Rc < crate :: task:: Task > ) {
49
50
self . state . tasks . borrow_mut ( ) . push_back ( task) ;
50
-
51
- // If we're already inside the `run_all` loop then that'll pick up the
52
- // task we just enqueued. If we're not in `run_all`, though, then we need
53
- // to schedule a microtask.
54
- //
55
51
// Note that we currently use a promise and a closure to do this, but
56
52
// eventually we should probably use something like `queueMicrotask`:
57
53
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/queueMicrotask
58
- if !self . state . is_spinning . replace ( true ) {
54
+ if !self . state . is_scheduled . replace ( true ) {
59
55
let _ = self . promise . then ( & self . closure ) ;
60
56
}
61
57
}
58
+ // Append a task to the currently running queue, or schedule it
59
+ pub ( crate ) fn push_task ( & self , task : Rc < crate :: task:: Task > ) {
60
+ // It would make sense to run this task on the same tick. For now, we
61
+ // make the simplifying choice of always scheduling tasks for a future tick.
62
+ self . schedule_task ( task)
63
+ }
62
64
}
63
65
64
66
impl Queue {
65
67
fn new ( ) -> Self {
66
68
let state = Rc :: new ( QueueState {
67
- is_spinning : Cell :: new ( false ) ,
69
+ is_scheduled : Cell :: new ( false ) ,
68
70
tasks : RefCell :: new ( VecDeque :: new ( ) ) ,
69
71
} ) ;
70
72
0 commit comments