1
1
'use strict' ;
2
2
3
- // This value is used to prevent the nextTickQueue from becoming too
4
- // large and cause the process to run out of memory. When this value
5
- // is reached the nextTimeQueue array will be shortened (see tickDone
6
- // for details).
7
- const kMaxCallbacksPerLoop = 1e4 ;
8
-
9
3
exports . setup = setupNextTick ;
10
4
// Will be overwritten when setupNextTick() is called.
11
5
exports . nextTick = null ;
12
6
13
- class NextTickQueue {
14
- constructor ( ) {
15
- this . head = null ;
16
- this . tail = null ;
17
- }
18
-
19
- push ( v ) {
20
- const entry = { data : v , next : null } ;
21
- if ( this . tail !== null )
22
- this . tail . next = entry ;
23
- else
24
- this . head = entry ;
25
- this . tail = entry ;
26
- }
27
-
28
- shift ( ) {
29
- if ( this . head === null )
30
- return ;
31
- const ret = this . head . data ;
32
- if ( this . head === this . tail )
33
- this . head = this . tail = null ;
34
- else
35
- this . head = this . head . next ;
36
- return ret ;
37
- }
38
-
39
- clear ( ) {
40
- this . head = null ;
41
- this . tail = null ;
42
- }
43
- }
44
-
45
7
function setupNextTick ( ) {
46
8
const async_wrap = process . binding ( 'async_wrap' ) ;
47
9
const async_hooks = require ( 'internal/async_hooks' ) ;
@@ -56,15 +18,47 @@ function setupNextTick() {
56
18
// Grab the constants necessary for working with internal arrays.
57
19
const { kInit, kDestroy, kAsyncIdCounter } = async_wrap . constants ;
58
20
const { async_id_symbol, trigger_async_id_symbol } = async_wrap ;
59
- const nextTickQueue = new NextTickQueue ( ) ;
60
- var microtasksScheduled = false ;
61
21
62
- // Used to run V8's micro task queue.
63
- var _runMicrotasks = { } ;
22
+ // tickInfo is used so that the C++ code in src/node.cc can
23
+ // have easy access to our nextTick state, and avoid unnecessary
24
+ // calls into JS land.
25
+ // runMicrotasks is used to run V8's micro task queue.
26
+ const [
27
+ tickInfo ,
28
+ runMicrotasks
29
+ ] = process . _setupNextTick ( _tickCallback ) ;
64
30
65
31
// *Must* match Environment::TickInfo::Fields in src/env.h.
66
- var kIndex = 0 ;
67
- var kLength = 1 ;
32
+ const kScheduled = 0 ;
33
+
34
+ const nextTickQueue = {
35
+ head : null ,
36
+ tail : null ,
37
+ push ( data ) {
38
+ const entry = { data, next : null } ;
39
+ if ( this . tail !== null ) {
40
+ this . tail . next = entry ;
41
+ } else {
42
+ this . head = entry ;
43
+ tickInfo [ kScheduled ] = 1 ;
44
+ }
45
+ this . tail = entry ;
46
+ } ,
47
+ shift ( ) {
48
+ if ( this . head === null )
49
+ return ;
50
+ const ret = this . head . data ;
51
+ if ( this . head === this . tail ) {
52
+ this . head = this . tail = null ;
53
+ tickInfo [ kScheduled ] = 0 ;
54
+ } else {
55
+ this . head = this . head . next ;
56
+ }
57
+ return ret ;
58
+ }
59
+ } ;
60
+
61
+ var microtasksScheduled = false ;
68
62
69
63
process . nextTick = nextTick ;
70
64
// Needs to be accessible from beyond this scope.
@@ -73,25 +67,6 @@ function setupNextTick() {
73
67
// Set the nextTick() function for internal usage.
74
68
exports . nextTick = internalNextTick ;
75
69
76
- // This tickInfo thing is used so that the C++ code in src/node.cc
77
- // can have easy access to our nextTick state, and avoid unnecessary
78
- // calls into JS land.
79
- const tickInfo = process . _setupNextTick ( _tickCallback , _runMicrotasks ) ;
80
-
81
- _runMicrotasks = _runMicrotasks . runMicrotasks ;
82
-
83
- function tickDone ( ) {
84
- if ( tickInfo [ kLength ] !== 0 ) {
85
- if ( tickInfo [ kLength ] <= tickInfo [ kIndex ] ) {
86
- nextTickQueue . clear ( ) ;
87
- tickInfo [ kLength ] = 0 ;
88
- } else {
89
- tickInfo [ kLength ] -= tickInfo [ kIndex ] ;
90
- }
91
- }
92
- tickInfo [ kIndex ] = 0 ;
93
- }
94
-
95
70
const microTasksTickObject = {
96
71
callback : runMicrotasksCallback ,
97
72
args : undefined ,
@@ -105,38 +80,27 @@ function setupNextTick() {
105
80
// For the moment all microtasks come from the void until the PromiseHook
106
81
// API is implemented.
107
82
nextTickQueue . push ( microTasksTickObject ) ;
108
-
109
- tickInfo [ kLength ] ++ ;
110
83
microtasksScheduled = true ;
111
84
}
112
85
113
86
function runMicrotasksCallback ( ) {
114
87
microtasksScheduled = false ;
115
- _runMicrotasks ( ) ;
88
+ runMicrotasks ( ) ;
116
89
117
- if ( tickInfo [ kIndex ] < tickInfo [ kLength ] ||
118
- emitPendingUnhandledRejections ( ) ) {
90
+ if ( nextTickQueue . head !== null || emitPendingUnhandledRejections ( ) )
119
91
scheduleMicrotasks ( ) ;
120
- }
121
92
}
122
93
123
94
function _tickCallback ( ) {
95
+ let tock ;
124
96
do {
125
- while ( tickInfo [ kIndex ] < tickInfo [ kLength ] ) {
126
- ++ tickInfo [ kIndex ] ;
127
- const tock = nextTickQueue . shift ( ) ;
128
-
129
- // CHECK(Number.isSafeInteger(tock[async_id_symbol]))
130
- // CHECK(tock[async_id_symbol] > 0)
131
- // CHECK(Number.isSafeInteger(tock[trigger_async_id_symbol]))
132
- // CHECK(tock[trigger_async_id_symbol] > 0)
133
-
97
+ while ( tock = nextTickQueue . shift ( ) ) {
134
98
const asyncId = tock [ async_id_symbol ] ;
135
99
emitBefore ( asyncId , tock [ trigger_async_id_symbol ] ) ;
136
100
// emitDestroy() places the async_id_symbol into an asynchronous queue
137
101
// that calls the destroy callback in the future. It's called before
138
102
// calling tock.callback so destroy will be called even if the callback
139
- // throws an exception that is handles by 'uncaughtException' or a
103
+ // throws an exception that is handled by 'uncaughtException' or a
140
104
// domain.
141
105
// TODO(trevnorris): This is a bit of a hack. It relies on the fact
142
106
// that nextTick() doesn't allow the event loop to proceed, but if
@@ -152,24 +116,21 @@ function setupNextTick() {
152
116
Reflect . apply ( callback , undefined , tock . args ) ;
153
117
154
118
emitAfter ( asyncId ) ;
155
-
156
- if ( kMaxCallbacksPerLoop < tickInfo [ kIndex ] )
157
- tickDone ( ) ;
158
119
}
159
- tickDone ( ) ;
160
- _runMicrotasks ( ) ;
120
+ runMicrotasks ( ) ;
161
121
emitPendingUnhandledRejections ( ) ;
162
- } while ( tickInfo [ kLength ] !== 0 ) ;
122
+ } while ( nextTickQueue . head !== null ) ;
163
123
}
164
124
165
125
class TickObject {
166
- constructor ( callback , args , asyncId , triggerAsyncId ) {
126
+ constructor ( callback , args , triggerAsyncId ) {
167
127
// this must be set to null first to avoid function tracking
168
128
// on the hidden class, revisit in V8 versions after 6.2
169
129
this . callback = null ;
170
130
this . callback = callback ;
171
131
this . args = args ;
172
132
133
+ const asyncId = ++ async_id_fields [ kAsyncIdCounter ] ;
173
134
this [ async_id_symbol ] = asyncId ;
174
135
this [ trigger_async_id_symbol ] = triggerAsyncId ;
175
136
@@ -203,13 +164,7 @@ function setupNextTick() {
203
164
args [ i - 1 ] = arguments [ i ] ;
204
165
}
205
166
206
- // In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the
207
- // TickObject incurs a significant performance penalty in the
208
- // next-tick-breadth-args benchmark (revisit later)
209
- ++ tickInfo [ kLength ] ;
210
- nextTickQueue . push ( new TickObject ( callback ,
211
- args ,
212
- ++ async_id_fields [ kAsyncIdCounter ] ,
167
+ nextTickQueue . push ( new TickObject ( callback , args ,
213
168
getDefaultTriggerAsyncId ( ) ) ) ;
214
169
}
215
170
@@ -238,13 +193,6 @@ function setupNextTick() {
238
193
239
194
if ( triggerAsyncId === null )
240
195
triggerAsyncId = getDefaultTriggerAsyncId ( ) ;
241
- // In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the
242
- // TickObject incurs a significant performance penalty in the
243
- // next-tick-breadth-args benchmark (revisit later)
244
- ++ tickInfo [ kLength ] ;
245
- nextTickQueue . push ( new TickObject ( callback ,
246
- args ,
247
- ++ async_id_fields [ kAsyncIdCounter ] ,
248
- triggerAsyncId ) ) ;
196
+ nextTickQueue . push ( new TickObject ( callback , args , triggerAsyncId ) ) ;
249
197
}
250
198
}
0 commit comments