11'use strict' ;
22
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-
93exports . setup = setupNextTick ;
104// Will be overwritten when setupNextTick() is called.
115exports . nextTick = null ;
126
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-
457function setupNextTick ( ) {
468 const async_wrap = process . binding ( 'async_wrap' ) ;
479 const async_hooks = require ( 'internal/async_hooks' ) ;
@@ -56,15 +18,47 @@ function setupNextTick() {
5618 // Grab the constants necessary for working with internal arrays.
5719 const { kInit, kDestroy, kAsyncIdCounter } = async_wrap . constants ;
5820 const { async_id_symbol, trigger_async_id_symbol } = async_wrap ;
59- const nextTickQueue = new NextTickQueue ( ) ;
60- var microtasksScheduled = false ;
6121
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 ) ;
6430
6531 // *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 ;
6862
6963 process . nextTick = nextTick ;
7064 // Needs to be accessible from beyond this scope.
@@ -73,25 +67,6 @@ function setupNextTick() {
7367 // Set the nextTick() function for internal usage.
7468 exports . nextTick = internalNextTick ;
7569
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-
9570 const microTasksTickObject = {
9671 callback : runMicrotasksCallback ,
9772 args : undefined ,
@@ -105,38 +80,27 @@ function setupNextTick() {
10580 // For the moment all microtasks come from the void until the PromiseHook
10681 // API is implemented.
10782 nextTickQueue . push ( microTasksTickObject ) ;
108-
109- tickInfo [ kLength ] ++ ;
11083 microtasksScheduled = true ;
11184 }
11285
11386 function runMicrotasksCallback ( ) {
11487 microtasksScheduled = false ;
115- _runMicrotasks ( ) ;
88+ runMicrotasks ( ) ;
11689
117- if ( tickInfo [ kIndex ] < tickInfo [ kLength ] ||
118- emitPendingUnhandledRejections ( ) ) {
90+ if ( nextTickQueue . head !== null || emitPendingUnhandledRejections ( ) )
11991 scheduleMicrotasks ( ) ;
120- }
12192 }
12293
12394 function _tickCallback ( ) {
95+ let tock ;
12496 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 ( ) ) {
13498 const asyncId = tock [ async_id_symbol ] ;
13599 emitBefore ( asyncId , tock [ trigger_async_id_symbol ] ) ;
136100 // emitDestroy() places the async_id_symbol into an asynchronous queue
137101 // that calls the destroy callback in the future. It's called before
138102 // 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
140104 // domain.
141105 // TODO(trevnorris): This is a bit of a hack. It relies on the fact
142106 // that nextTick() doesn't allow the event loop to proceed, but if
@@ -152,24 +116,21 @@ function setupNextTick() {
152116 Reflect . apply ( callback , undefined , tock . args ) ;
153117
154118 emitAfter ( asyncId ) ;
155-
156- if ( kMaxCallbacksPerLoop < tickInfo [ kIndex ] )
157- tickDone ( ) ;
158119 }
159- tickDone ( ) ;
160- _runMicrotasks ( ) ;
120+ runMicrotasks ( ) ;
161121 emitPendingUnhandledRejections ( ) ;
162- } while ( tickInfo [ kLength ] !== 0 ) ;
122+ } while ( nextTickQueue . head !== null ) ;
163123 }
164124
165125 class TickObject {
166- constructor ( callback , args , asyncId , triggerAsyncId ) {
126+ constructor ( callback , args , triggerAsyncId ) {
167127 // this must be set to null first to avoid function tracking
168128 // on the hidden class, revisit in V8 versions after 6.2
169129 this . callback = null ;
170130 this . callback = callback ;
171131 this . args = args ;
172132
133+ const asyncId = ++ async_id_fields [ kAsyncIdCounter ] ;
173134 this [ async_id_symbol ] = asyncId ;
174135 this [ trigger_async_id_symbol ] = triggerAsyncId ;
175136
@@ -203,13 +164,7 @@ function setupNextTick() {
203164 args [ i - 1 ] = arguments [ i ] ;
204165 }
205166
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 ,
213168 getDefaultTriggerAsyncId ( ) ) ) ;
214169 }
215170
@@ -238,13 +193,6 @@ function setupNextTick() {
238193
239194 if ( triggerAsyncId === null )
240195 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 ) ) ;
249197 }
250198}
0 commit comments