@@ -32,10 +32,60 @@ function setupNextTick() {
32
32
const kHasScheduled = 0 ;
33
33
const kHasPromiseRejections = 1 ;
34
34
35
- // Queue size for each tick array. Must be a factor of two.
35
+ // Queue size for each tick array. Must be a power of two.
36
36
const kQueueSize = 2048 ;
37
37
const kQueueMask = kQueueSize - 1 ;
38
38
39
+ // The next tick queue is implemented as a singly-linked list of fixed-size
40
+ // circular buffers. It looks something like this:
41
+ //
42
+ // head tail
43
+ // | |
44
+ // v v
45
+ // +-----------+ <-----\ +-----------+ <------\ +-----------+
46
+ // | [null] | \----- | next | \------- | next |
47
+ // +-----------+ +-----------+ +-----------+
48
+ // | tick | <-- bottom | tick | <-- bottom | [empty] |
49
+ // | tick | | tick | | [empty] |
50
+ // | tick | | tick | | [empty] |
51
+ // | tick | | tick | | [empty] |
52
+ // | tick | | tick | bottom --> | tick |
53
+ // | tick | | tick | | tick |
54
+ // | ... | | ... | | ... |
55
+ // | tick | | tick | | tick |
56
+ // | tick | | tick | | tick |
57
+ // | [empty] | <-- top | tick | | tick |
58
+ // | [empty] | | tick | | tick |
59
+ // | [empty] | | tick | | tick |
60
+ // +-----------+ +-----------+ <-- top top --> +-----------+
61
+ //
62
+ // Or, if there is only one fixed-size queue, it looks something
63
+ // like either of these:
64
+ //
65
+ // head tail head tail
66
+ // | | | |
67
+ // v v v v
68
+ // +-----------+ +-----------+
69
+ // | [null] | | [null] |
70
+ // +-----------+ +-----------+
71
+ // | [empty] | | tick |
72
+ // | [empty] | | tick |
73
+ // | tick | <-- bottom top --> | [empty] |
74
+ // | tick | | [empty] |
75
+ // | [empty] | <-- top bottom --> | tick |
76
+ // | [empty] | | tick |
77
+ // +-----------+ +-----------+
78
+ //
79
+ // Adding a value means moving `top` forward by one, removing means
80
+ // moving `bottom` forward by one.
81
+ //
82
+ // We let `bottom` and `top` wrap around, so when `top` is conceptually
83
+ // pointing to the end of the list, that means that the actual value is `0`.
84
+ //
85
+ // In particular, when `top === bottom`, this can mean *either* that the
86
+ // current queue is empty or that it is full. We can differentiate by
87
+ // checking whether an entry in the queue is empty (a.k.a. `=== undefined`).
88
+
39
89
class FixedQueue {
40
90
constructor ( ) {
41
91
this . bottom = 0 ;
@@ -50,11 +100,12 @@ function setupNextTick() {
50
100
}
51
101
52
102
shift ( ) {
53
- const next = this . list [ this . bottom ] ;
54
- if ( next === undefined ) return null ;
103
+ const nextItem = this . list [ this . bottom ] ;
104
+ if ( nextItem === undefined )
105
+ return null ;
55
106
this . list [ this . bottom ] = undefined ;
56
107
this . bottom = ( this . bottom + 1 ) & kQueueMask ;
57
- return next ;
108
+ return nextItem ;
58
109
}
59
110
}
60
111
@@ -63,21 +114,34 @@ function setupNextTick() {
63
114
64
115
function push ( data ) {
65
116
if ( head . bottom === head . top ) {
66
- if ( head . list [ head . top ] !== undefined )
117
+ // Either empty or full:
118
+ if ( head . list [ head . top ] !== undefined ) {
119
+ // It's full: Creates a new queue, sets the old queue's `.next` to it,
120
+ // and sets it as the new main queue.
67
121
head = head . next = new FixedQueue ( ) ;
68
- else
122
+ } else {
123
+ // If the head is empty, that means that it was the only fixed-sized
124
+ // queue in existence.
125
+ DCHECK_EQ ( head . next , null ) ;
126
+ // This is the first tick object in existence, so we need to inform
127
+ // the C++ side that we do want to run `_tickCallback()`.
69
128
tickInfo [ kHasScheduled ] = 1 ;
129
+ }
70
130
}
71
131
head . push ( data ) ;
72
132
}
73
133
74
134
function shift ( ) {
75
135
const next = tail . shift ( ) ;
76
- if ( tail . top === tail . bottom ) {
77
- if ( tail . next )
136
+ if ( tail . top === tail . bottom ) { // -> .shift() emptied the current queue.
137
+ if ( tail . next !== null ) {
138
+ // If there is another queue, it forms the new tail.
78
139
tail = tail . next ;
79
- else
140
+ } else {
141
+ // We've just run out of items. Let the native side know that it
142
+ // doesn't need to bother calling into JS to run the queue.
80
143
tickInfo [ kHasScheduled ] = 0 ;
144
+ }
81
145
}
82
146
return next ;
83
147
}
0 commit comments