@@ -254,16 +254,17 @@ function processTimers(now) {
254254 debug ( 'process timer lists %d' , now ) ;
255255 nextExpiry = Infinity ;
256256
257- let list , ran ;
257+ let list ;
258+ let ranAtLeastOneList = false ;
258259 while ( list = queue . peek ( ) ) {
259260 if ( list . expiry > now ) {
260261 nextExpiry = list . expiry ;
261262 return refCount > 0 ? nextExpiry : - nextExpiry ;
262263 }
263- if ( ran )
264+ if ( ranAtLeastOneList )
264265 runNextTicks ( ) ;
265266 else
266- ran = true ;
267+ ranAtLeastOneList = true ;
267268 listOnTimeout ( list , now ) ;
268269 }
269270 return 0 ;
@@ -275,6 +276,7 @@ function listOnTimeout(list, now) {
275276 debug ( 'timeout callback %d' , msecs ) ;
276277
277278 var diff , timer ;
279+ let ranAtLeastOneTimer = false ;
278280 while ( timer = L . peek ( list ) ) {
279281 diff = now - timer . _idleStart ;
280282
@@ -288,6 +290,11 @@ function listOnTimeout(list, now) {
288290 return ;
289291 }
290292
293+ if ( ranAtLeastOneTimer )
294+ runNextTicks ( ) ;
295+ else
296+ ranAtLeastOneTimer = true ;
297+
291298 // The actual logic for when a timeout happens.
292299 L . remove ( timer ) ;
293300
@@ -307,7 +314,33 @@ function listOnTimeout(list, now) {
307314
308315 emitBefore ( asyncId , timer [ trigger_async_id_symbol ] ) ;
309316
310- tryOnTimeout ( timer ) ;
317+ let start ;
318+ if ( timer . _repeat )
319+ start = getLibuvNow ( ) ;
320+
321+ try {
322+ const args = timer . _timerArgs ;
323+ if ( ! args )
324+ timer . _onTimeout ( ) ;
325+ else
326+ Reflect . apply ( timer . _onTimeout , timer , args ) ;
327+ } finally {
328+ if ( timer . _repeat && timer . _idleTimeout !== - 1 ) {
329+ timer . _idleTimeout = timer . _repeat ;
330+ if ( start === undefined )
331+ start = getLibuvNow ( ) ;
332+ insert ( timer , timer [ kRefed ] , start ) ;
333+ } else {
334+ if ( timer [ kRefed ] )
335+ refCount -- ;
336+ timer [ kRefed ] = null ;
337+
338+ if ( destroyHooksExist ( ) && ! timer . _destroyed ) {
339+ emitDestroy ( timer [ async_id_symbol ] ) ;
340+ timer . _destroyed = true ;
341+ }
342+ }
343+ }
311344
312345 emitAfter ( asyncId ) ;
313346 }
@@ -327,30 +360,6 @@ function listOnTimeout(list, now) {
327360}
328361
329362
330- // An optimization so that the try/finally only de-optimizes (since at least v8
331- // 4.7) what is in this smaller function.
332- function tryOnTimeout ( timer , start ) {
333- if ( start === undefined && timer . _repeat )
334- start = getLibuvNow ( ) ;
335- try {
336- ontimeout ( timer ) ;
337- } finally {
338- if ( timer . _repeat ) {
339- rearm ( timer , start ) ;
340- } else {
341- if ( timer [ kRefed ] )
342- refCount -- ;
343- timer [ kRefed ] = null ;
344-
345- if ( destroyHooksExist ( ) && ! timer . _destroyed ) {
346- emitDestroy ( timer [ async_id_symbol ] ) ;
347- timer . _destroyed = true ;
348- }
349- }
350- }
351- }
352-
353-
354363// Remove a timer. Cancels the timeout and resets the relevant timer properties.
355364function unenroll ( item ) {
356365 // Fewer checks may be possible, but these cover everything.
@@ -456,26 +465,6 @@ setTimeout[internalUtil.promisify.custom] = function(after, value) {
456465exports . setTimeout = setTimeout ;
457466
458467
459- function ontimeout ( timer ) {
460- const args = timer . _timerArgs ;
461- if ( typeof timer . _onTimeout !== 'function' )
462- return Promise . resolve ( timer . _onTimeout , args [ 0 ] ) ;
463- if ( ! args )
464- timer . _onTimeout ( ) ;
465- else
466- Reflect . apply ( timer . _onTimeout , timer , args ) ;
467- }
468-
469- function rearm ( timer , start ) {
470- // Do not re-arm unenroll'd or closed timers.
471- if ( timer . _idleTimeout === - 1 )
472- return ;
473-
474- timer . _idleTimeout = timer . _repeat ;
475- insert ( timer , timer [ kRefed ] , start ) ;
476- }
477-
478-
479468const clearTimeout = exports . clearTimeout = function clearTimeout ( timer ) {
480469 if ( timer && timer . _onTimeout ) {
481470 timer . _onTimeout = null ;
@@ -601,75 +590,63 @@ function processImmediate() {
601590 const queue = outstandingQueue . head !== null ?
602591 outstandingQueue : immediateQueue ;
603592 var immediate = queue . head ;
604- const tail = queue . tail ;
605593
606594 // Clear the linked list early in case new `setImmediate()` calls occur while
607595 // immediate callbacks are executed
608- queue . head = queue . tail = null ;
609-
610- let count = 0 ;
611- let refCount = 0 ;
596+ if ( queue !== outstandingQueue ) {
597+ queue . head = queue . tail = null ;
598+ immediateInfo [ kHasOutstanding ] = 1 ;
599+ }
612600
601+ let prevImmediate ;
602+ let ranAtLeastOneImmediate = false ;
613603 while ( immediate !== null ) {
614- immediate . _destroyed = true ;
604+ if ( ranAtLeastOneImmediate )
605+ runNextTicks ( ) ;
606+ else
607+ ranAtLeastOneImmediate = true ;
615608
616- const asyncId = immediate [ async_id_symbol ] ;
617- emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
609+ // It's possible for this current Immediate to be cleared while executing
610+ // the next tick queue above, which means we need to use the previous
611+ // Immediate's _idleNext which is guaranteed to not have been cleared.
612+ if ( immediate . _destroyed ) {
613+ outstandingQueue . head = immediate = prevImmediate . _idleNext ;
614+ continue ;
615+ }
618616
619- count ++ ;
617+ immediate . _destroyed = true ;
618+
619+ immediateInfo [ kCount ] -- ;
620620 if ( immediate [ kRefed ] )
621- refCount ++ ;
621+ immediateInfo [ kRefCount ] -- ;
622622 immediate [ kRefed ] = null ;
623623
624- tryOnImmediate ( immediate , tail , count , refCount ) ;
624+ prevImmediate = immediate ;
625625
626- emitAfter ( asyncId ) ;
626+ const asyncId = immediate [ async_id_symbol ] ;
627+ emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] ) ;
627628
628- immediate = immediate . _idleNext ;
629- }
629+ try {
630+ const argv = immediate . _argv ;
631+ if ( ! argv )
632+ immediate . _onImmediate ( ) ;
633+ else
634+ Reflect . apply ( immediate . _onImmediate , immediate , argv ) ;
635+ } finally {
636+ immediate . _onImmediate = null ;
630637
631- immediateInfo [ kCount ] -= count ;
632- immediateInfo [ kRefCount ] -= refCount ;
633- immediateInfo [ kHasOutstanding ] = 0 ;
634- }
638+ if ( destroyHooksExist ( ) )
639+ emitDestroy ( asyncId ) ;
635640
636- // An optimization so that the try/finally only de-optimizes (since at least v8
637- // 4.7) what is in this smaller function.
638- function tryOnImmediate ( immediate , oldTail , count , refCount ) {
639- var threw = true ;
640- try {
641- // make the actual call outside the try/finally to allow it to be optimized
642- runCallback ( immediate ) ;
643- threw = false ;
644- } finally {
645- immediate . _onImmediate = null ;
646-
647- if ( destroyHooksExist ( ) ) {
648- emitDestroy ( immediate [ async_id_symbol ] ) ;
641+ outstandingQueue . head = immediate = immediate . _idleNext ;
649642 }
650643
651- if ( threw ) {
652- immediateInfo [ kCount ] -= count ;
653- immediateInfo [ kRefCount ] -= refCount ;
654-
655- if ( immediate . _idleNext !== null ) {
656- // Handle any remaining Immediates after error handling has resolved,
657- // assuming we're still alive to do so.
658- outstandingQueue . head = immediate . _idleNext ;
659- outstandingQueue . tail = oldTail ;
660- immediateInfo [ kHasOutstanding ] = 1 ;
661- }
662- }
644+ emitAfter ( asyncId ) ;
663645 }
664- }
665646
666- function runCallback ( timer ) {
667- const argv = timer . _argv ;
668- if ( typeof timer . _onImmediate !== 'function' )
669- return Promise . resolve ( timer . _onImmediate , argv [ 0 ] ) ;
670- if ( ! argv )
671- return timer . _onImmediate ( ) ;
672- Reflect . apply ( timer . _onImmediate , timer , argv ) ;
647+ if ( queue === outstandingQueue )
648+ outstandingQueue . head = null ;
649+ immediateInfo [ kHasOutstanding ] = 0 ;
673650}
674651
675652
0 commit comments