@@ -16,6 +16,7 @@ function setupNextTick() {
16
16
} = require ( 'internal/async_hooks' ) ;
17
17
const promises = require ( 'internal/process/promises' ) ;
18
18
const { ERR_INVALID_CALLBACK } = require ( 'internal/errors' ) . codes ;
19
+ const FixedQueue = require ( 'internal/fixed_queue' ) ;
19
20
const { emitPromiseRejectionWarnings } = promises ;
20
21
21
22
// tickInfo is used so that the C++ code in src/node.cc can
@@ -31,119 +32,7 @@ function setupNextTick() {
31
32
const kHasScheduled = 0 ;
32
33
const kHasPromiseRejections = 1 ;
33
34
34
- // Queue size for each tick array. Must be a power of two.
35
- const kQueueSize = 2048 ;
36
- const kQueueMask = kQueueSize - 1 ;
37
-
38
- // The next tick queue is implemented as a singly-linked list of fixed-size
39
- // circular buffers. It looks something like this:
40
- //
41
- // head tail
42
- // | |
43
- // v v
44
- // +-----------+ <-----\ +-----------+ <------\ +-----------+
45
- // | [null] | \----- | next | \------- | next |
46
- // +-----------+ +-----------+ +-----------+
47
- // | tick | <-- bottom | tick | <-- bottom | [empty] |
48
- // | tick | | tick | | [empty] |
49
- // | tick | | tick | | [empty] |
50
- // | tick | | tick | | [empty] |
51
- // | tick | | tick | bottom --> | tick |
52
- // | tick | | tick | | tick |
53
- // | ... | | ... | | ... |
54
- // | tick | | tick | | tick |
55
- // | tick | | tick | | tick |
56
- // | [empty] | <-- top | tick | | tick |
57
- // | [empty] | | tick | | tick |
58
- // | [empty] | | tick | | tick |
59
- // +-----------+ +-----------+ <-- top top --> +-----------+
60
- //
61
- // Or, if there is only one fixed-size queue, it looks something
62
- // like either of these:
63
- //
64
- // head tail head tail
65
- // | | | |
66
- // v v v v
67
- // +-----------+ +-----------+
68
- // | [null] | | [null] |
69
- // +-----------+ +-----------+
70
- // | [empty] | | tick |
71
- // | [empty] | | tick |
72
- // | tick | <-- bottom top --> | [empty] |
73
- // | tick | | [empty] |
74
- // | [empty] | <-- top bottom --> | tick |
75
- // | [empty] | | tick |
76
- // +-----------+ +-----------+
77
- //
78
- // Adding a value means moving `top` forward by one, removing means
79
- // moving `bottom` forward by one.
80
- //
81
- // We let `bottom` and `top` wrap around, so when `top` is conceptually
82
- // pointing to the end of the list, that means that the actual value is `0`.
83
- //
84
- // In particular, when `top === bottom`, this can mean *either* that the
85
- // current queue is empty or that it is full. We can differentiate by
86
- // checking whether an entry in the queue is empty (a.k.a. `=== undefined`).
87
-
88
- class FixedQueue {
89
- constructor ( ) {
90
- this . bottom = 0 ;
91
- this . top = 0 ;
92
- this . list = new Array ( kQueueSize ) ;
93
- this . next = null ;
94
- }
95
-
96
- push ( data ) {
97
- this . list [ this . top ] = data ;
98
- this . top = ( this . top + 1 ) & kQueueMask ;
99
- }
100
-
101
- shift ( ) {
102
- const nextItem = this . list [ this . bottom ] ;
103
- if ( nextItem === undefined )
104
- return null ;
105
- this . list [ this . bottom ] = undefined ;
106
- this . bottom = ( this . bottom + 1 ) & kQueueMask ;
107
- return nextItem ;
108
- }
109
- }
110
-
111
- var head = new FixedQueue ( ) ;
112
- var tail = head ;
113
-
114
- function push ( data ) {
115
- if ( head . bottom === head . top ) {
116
- // Either empty or full:
117
- if ( head . list [ head . top ] !== undefined ) {
118
- // It's full: Creates a new queue, sets the old queue's `.next` to it,
119
- // and sets it as the new main queue.
120
- head = head . next = new FixedQueue ( ) ;
121
- } else {
122
- // If the head is empty, that means that it was the only fixed-sized
123
- // queue in existence.
124
- DCHECK_EQ ( head . next , null ) ;
125
- // This is the first tick object in existence, so we need to inform
126
- // the C++ side that we do want to run `_tickCallback()`.
127
- tickInfo [ kHasScheduled ] = 1 ;
128
- }
129
- }
130
- head . push ( data ) ;
131
- }
132
-
133
- function shift ( ) {
134
- const next = tail . shift ( ) ;
135
- if ( tail . top === tail . bottom ) { // -> .shift() emptied the current queue.
136
- if ( tail . next !== null ) {
137
- // If there is another queue, it forms the new tail.
138
- tail = tail . next ;
139
- } else {
140
- // We've just run out of items. Let the native side know that it
141
- // doesn't need to bother calling into JS to run the queue.
142
- tickInfo [ kHasScheduled ] = 0 ;
143
- }
144
- }
145
- return next ;
146
- }
35
+ const queue = new FixedQueue ( ) ;
147
36
148
37
process . nextTick = nextTick ;
149
38
// Needs to be accessible from beyond this scope.
@@ -152,7 +41,7 @@ function setupNextTick() {
152
41
function _tickCallback ( ) {
153
42
let tock ;
154
43
do {
155
- while ( tock = shift ( ) ) {
44
+ while ( tock = queue . shift ( ) ) {
156
45
const asyncId = tock [ async_id_symbol ] ;
157
46
emitBefore ( asyncId , tock [ trigger_async_id_symbol ] ) ;
158
47
// emitDestroy() places the async_id_symbol into an asynchronous queue
@@ -175,8 +64,9 @@ function setupNextTick() {
175
64
176
65
emitAfter ( asyncId ) ;
177
66
}
67
+ tickInfo [ kHasScheduled ] = 0 ;
178
68
runMicrotasks ( ) ;
179
- } while ( head . top !== head . bottom || emitPromiseRejectionWarnings ( ) ) ;
69
+ } while ( ! queue . isEmpty ( ) || emitPromiseRejectionWarnings ( ) ) ;
180
70
tickInfo [ kHasPromiseRejections ] = 0 ;
181
71
}
182
72
@@ -222,6 +112,8 @@ function setupNextTick() {
222
112
args [ i - 1 ] = arguments [ i ] ;
223
113
}
224
114
225
- push ( new TickObject ( callback , args , getDefaultTriggerAsyncId ( ) ) ) ;
115
+ if ( queue . isEmpty ( ) )
116
+ tickInfo [ kHasScheduled ] = 1 ;
117
+ queue . push ( new TickObject ( callback , args , getDefaultTriggerAsyncId ( ) ) ) ;
226
118
}
227
119
}
0 commit comments