@ -514,17 +514,58 @@ Timeout.prototype.close = function() {
} ;
var immediateQueue = L . create ( ) ;
// A linked list for storing `setImmediate()` requests
function ImmediateList ( ) {
this . head = null ;
this . tail = null ;
}
// Appends an item to the end of the linked list, adjusting the current tail's
// previous and next pointers where applicable
ImmediateList . prototype . append = function ( item ) {
if ( this . tail ) {
this . tail . _ idleNext = item ;
item . _ idlePrev = this . tail ;
} else {
this . head = item ;
}
this . tail = item ;
} ;
// Removes an item from the linked list, adjusting the pointers of adjacent
// items and the linked list's head or tail pointers as necessary
ImmediateList . prototype . remove = function ( item ) {
if ( item . _ idleNext ) {
item . _ idleNext . _ idlePrev = item . _ idlePrev ;
}
if ( item . _ idlePrev ) {
item . _ idlePrev . _ idleNext = item . _ idleNext ;
}
if ( item === this . head )
this . head = item . _ idleNext ;
if ( item === this . tail )
this . tail = item . _ idlePrev ;
item . _ idleNext = null ;
item . _ idlePrev = null ;
} ;
// Create a single linked list instance only once at startup
var immediateQueue = new ImmediateList ( ) ;
function processImmediate ( ) {
const queue = immediateQueue ;
var domain , immediate ;
var immediate = immediateQueue . head ;
var tail = immediateQueue . tail ;
var domain ;
immediateQueue = L . create ( ) ;
// Clear the linked list early in case new `setImmediate()` calls occur while
// immediate callbacks are executed
immediateQueue . head = immediateQueue . tail = null ;
while ( L . isEmpty ( queue ) === false ) {
immediate = L . shift ( queue ) ;
while ( immediate ) {
domain = immediate . domain ;
if ( ! immediate . _ onImmediate )
@ -534,16 +575,18 @@ function processImmediate() {
domain . enter ( ) ;
immediate . _ callback = immediate . _ onImmediate ;
tryOnImmediate ( immediate , queue ) ;
tryOnImmediate ( immediate , tail ) ;
if ( domain )
domain . exit ( ) ;
immediate = immediate . _ idleNext ;
}
// Only round-trip to C++ land if we have to. Calling clearImmediate() on an
// immediate that's in |queue| is okay. Worst case is we make a superfluous
// call to NeedImmediateCallbackSetter().
if ( L . isEmpty ( immediateQueue ) ) {
if ( ! immediateQueue . head ) {
process . _ needImmediateCallback = false ;
}
}
@ -551,19 +594,26 @@ function processImmediate() {
// An optimization so that the try/finally only de-optimizes (since at least v8
// 4.7) what is in this smaller function.
function tryOnImmediate ( immediate , queue ) {
function tryOnImmediate ( immediate , oldTail ) {
var threw = true ;
try {
// make the actual call outside the try/catch to allow it to be optimized
runCallback ( immediate ) ;
threw = false ;
} finally {
if ( threw && ! L . isEmpty ( queue ) ) {
if ( threw && immediate . _ idleNext ) {
// Handle any remaining on next tick, assuming we're still alive to do so.
while ( ! L . isEmpty ( immediateQueue ) ) {
L . append ( queue , L . shift ( immediateQueue ) ) ;
const curHead = immediateQueue . head ;
const next = immediate . _ idleNext ;
if ( curHead ) {
curHead . _ idlePrev = oldTail ;
oldTail . _ idleNext = curHead ;
next . _ idlePrev = null ;
immediateQueue . head = next ;
} else {
immediateQueue . head = next ;
immediateQueue . tail = oldTail ;
}
immediateQueue = queue ;
process . nextTick ( processImmediate ) ;
}
}
@ -617,10 +667,6 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
case 3 :
args = [ arg1 , arg2 ] ;
break ;
case 4 :
args = [ arg1 , arg2 , arg3 ] ;
break ;
// slow case
default :
args = [ arg1 , arg2 , arg3 ] ;
for ( i = 4 ; i < arguments . length ; i ++ )
@ -628,6 +674,10 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
args [ i - 1 ] = arguments [ i ] ;
break ;
}
return createImmediate ( args , callback ) ;
} ;
function createImmediate ( args , callback ) {
// declaring it `const immediate` causes v6.0.0 to deoptimize this function
var immediate = new Immediate ( ) ;
immediate . _ callback = callback ;
@ -639,20 +689,20 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
process . _ immediateCallback = processImmediate ;
}
L . append ( immediateQueue , immediate ) ;
immediateQueue . append ( immediate ) ;
return immediate ;
} ;
}
exports . clearImmediate = function ( immediate ) {
if ( ! immediate ) return ;
immediate . _ onImmediate = undefined ;
immediate . _ onImmediate = null ;
L . remove ( immediate ) ;
immediateQueue . remove ( immediate ) ;
if ( L . isEmpty ( immediateQueue ) ) {
if ( ! immediateQueue . head ) {
process . _ needImmediateCallback = false ;
}
} ;