@@ -514,17 +514,58 @@ Timeout.prototype.close = function() {
514514} ;
515515
516516
517- var immediateQueue = L . create ( ) ;
517+ // A linked list for storing `setImmediate()` requests
518+ function ImmediateList ( ) {
519+ this . head = null ;
520+ this . tail = null ;
521+ }
522+
523+ // Appends an item to the end of the linked list, adjusting the current tail's
524+ // previous and next pointers where applicable
525+ ImmediateList . prototype . append = function ( item ) {
526+ if ( this . tail ) {
527+ this . tail . _idleNext = item ;
528+ item . _idlePrev = this . tail ;
529+ } else {
530+ this . head = item ;
531+ }
532+ this . tail = item ;
533+ } ;
534+
535+ // Removes an item from the linked list, adjusting the pointers of adjacent
536+ // items and the linked list's head or tail pointers as necessary
537+ ImmediateList . prototype . remove = function ( item ) {
538+ if ( item . _idleNext ) {
539+ item . _idleNext . _idlePrev = item . _idlePrev ;
540+ }
541+
542+ if ( item . _idlePrev ) {
543+ item . _idlePrev . _idleNext = item . _idleNext ;
544+ }
545+
546+ if ( item === this . head )
547+ this . head = item . _idleNext ;
548+ if ( item === this . tail )
549+ this . tail = item . _idlePrev ;
550+
551+ item . _idleNext = null ;
552+ item . _idlePrev = null ;
553+ } ;
554+
555+ // Create a single linked list instance only once at startup
556+ var immediateQueue = new ImmediateList ( ) ;
518557
519558
520559function processImmediate ( ) {
521- const queue = immediateQueue ;
522- var domain , immediate ;
560+ var immediate = immediateQueue . head ;
561+ var tail = immediateQueue . tail ;
562+ var domain ;
523563
524- immediateQueue = L . create ( ) ;
564+ // Clear the linked list early in case new `setImmediate()` calls occur while
565+ // immediate callbacks are executed
566+ immediateQueue . head = immediateQueue . tail = null ;
525567
526- while ( L . isEmpty ( queue ) === false ) {
527- immediate = L . shift ( queue ) ;
568+ while ( immediate ) {
528569 domain = immediate . domain ;
529570
530571 if ( ! immediate . _onImmediate )
@@ -534,36 +575,45 @@ function processImmediate() {
534575 domain . enter ( ) ;
535576
536577 immediate . _callback = immediate . _onImmediate ;
537- tryOnImmediate ( immediate , queue ) ;
578+ tryOnImmediate ( immediate , tail ) ;
538579
539580 if ( domain )
540581 domain . exit ( ) ;
582+
583+ immediate = immediate . _idleNext ;
541584 }
542585
543586 // Only round-trip to C++ land if we have to. Calling clearImmediate() on an
544587 // immediate that's in |queue| is okay. Worst case is we make a superfluous
545588 // call to NeedImmediateCallbackSetter().
546- if ( L . isEmpty ( immediateQueue ) ) {
589+ if ( ! immediateQueue . head ) {
547590 process . _needImmediateCallback = false ;
548591 }
549592}
550593
551594
552595// An optimization so that the try/finally only de-optimizes (since at least v8
553596// 4.7) what is in this smaller function.
554- function tryOnImmediate ( immediate , queue ) {
597+ function tryOnImmediate ( immediate , oldTail ) {
555598 var threw = true ;
556599 try {
557600 // make the actual call outside the try/catch to allow it to be optimized
558601 runCallback ( immediate ) ;
559602 threw = false ;
560603 } finally {
561- if ( threw && ! L . isEmpty ( queue ) ) {
604+ if ( threw && immediate . _idleNext ) {
562605 // Handle any remaining on next tick, assuming we're still alive to do so.
563- while ( ! L . isEmpty ( immediateQueue ) ) {
564- L . append ( queue , L . shift ( immediateQueue ) ) ;
606+ const curHead = immediateQueue . head ;
607+ const next = immediate . _idleNext ;
608+ if ( curHead ) {
609+ curHead . _idlePrev = oldTail ;
610+ oldTail . _idleNext = curHead ;
611+ next . _idlePrev = null ;
612+ immediateQueue . head = next ;
613+ } else {
614+ immediateQueue . head = next ;
615+ immediateQueue . tail = oldTail ;
565616 }
566- immediateQueue = queue ;
567617 process . nextTick ( processImmediate ) ;
568618 }
569619 }
@@ -617,17 +667,17 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
617667 case 3 :
618668 args = [ arg1 , arg2 ] ;
619669 break ;
620- case 4 :
621- args = [ arg1 , arg2 , arg3 ] ;
622- break ;
623- // slow case
624670 default :
625671 args = [ arg1 , arg2 , arg3 ] ;
626672 for ( i = 4 ; i < arguments . length ; i ++ )
627673 // extend array dynamically, makes .apply run much faster in v6.0.0
628674 args [ i - 1 ] = arguments [ i ] ;
629675 break ;
630676 }
677+ return createImmediate ( args , callback ) ;
678+ } ;
679+
680+ function createImmediate ( args , callback ) {
631681 // declaring it `const immediate` causes v6.0.0 to deoptimize this function
632682 var immediate = new Immediate ( ) ;
633683 immediate . _callback = callback ;
@@ -639,20 +689,20 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
639689 process . _immediateCallback = processImmediate ;
640690 }
641691
642- L . append ( immediateQueue , immediate ) ;
692+ immediateQueue . append ( immediate ) ;
643693
644694 return immediate ;
645- } ;
695+ }
646696
647697
648698exports . clearImmediate = function ( immediate ) {
649699 if ( ! immediate ) return ;
650700
651- immediate . _onImmediate = undefined ;
701+ immediate . _onImmediate = null ;
652702
653- L . remove ( immediate ) ;
703+ immediateQueue . remove ( immediate ) ;
654704
655- if ( L . isEmpty ( immediateQueue ) ) {
705+ if ( ! immediateQueue . head ) {
656706 process . _needImmediateCallback = false ;
657707 }
658708} ;
0 commit comments