diff options
Diffstat (limited to 'src/node.js')
-rw-r--r-- | src/node.js | 124 |
1 files changed, 106 insertions, 18 deletions
diff --git a/src/node.js b/src/node.js index 071016e0d0..848c39d119 100644 --- a/src/node.js +++ b/src/node.js @@ -45,6 +45,7 @@ startup.processAssert(); startup.processConfig(); startup.processNextTick(); + startup.processMakeCallback(); startup.processStdio(); startup.processKillAndExit(); startup.processSignalHandlers(); @@ -224,38 +225,125 @@ if (value === 'false') return false; return value; }); - } + }; + + startup.processMakeCallback = function() { + process._makeCallback = function(obj, fn, args) { + var domain = obj.domain; + if (domain) { + if (domain._disposed) return; + domain.enter(); + } + + var ret = fn.apply(obj, args); + + if (domain) domain.exit(); + + // process the nextTicks after each time we get called. + process._tickCallback(); + return ret; + }; + }; startup.processNextTick = function() { var nextTickQueue = []; var nextTickIndex = 0; + var inTick = false; + var tickDepth = 0; + + // the maximum number of times it'll process something like + // nextTick(function f(){nextTick(f)}) + // It's unlikely, but not illegal, to hit this limit. When + // that happens, it yields to libuv's tick spinner. + // This is a loop counter, not a stack depth, so we aren't using + // up lots of memory here. I/O can sneak in before nextTick if this + // limit is hit, which is not ideal, but not terrible. + process.maxTickDepth = 1000; + + function tickDone(tickDepth_) { + tickDepth = tickDepth_ || 0; + nextTickQueue.splice(0, nextTickIndex); + nextTickIndex = 0; + inTick = false; + if (nextTickQueue.length) { + process._needTickCallback(); + } + } - process._tickCallback = function() { - var nextTickLength = nextTickQueue.length; - if (nextTickLength === 0) return; - - while (nextTickIndex < nextTickLength) { - var tock = nextTickQueue[nextTickIndex++]; - var callback = tock.callback; - if (tock.domain) { - if (tock.domain._disposed) continue; - tock.domain.enter(); - } - callback(); - if (tock.domain) { - tock.domain.exit(); + process._tickCallback = function(fromSpinner) { + + // if you add a nextTick in a domain's error handler, then + // it's possible to cycle indefinitely. Normally, the tickDone + // in the finally{} block below will prevent this, however if + // that error handler ALSO triggers multiple MakeCallbacks, then + // it'll try to keep clearing the queue, since the finally block + // fires *before* the error hits the top level and is handled. + if (tickDepth >= process.maxTickDepth) { + if (fromSpinner) { + // coming in from the event queue. reset. + tickDepth = 0; + } else { + if (nextTickQueue.length) { + process._needTickCallback(); + } + return; } } - nextTickQueue.splice(0, nextTickIndex); - nextTickIndex = 0; + if (!nextTickQueue.length) return tickDone(); + + if (inTick) return; + inTick = true; + + // always do this at least once. otherwise if process.maxTickDepth + // is set to some negative value, or if there were repeated errors + // preventing tickDepth from being cleared, we'd never process any + // of them. + do { + tickDepth++; + var nextTickLength = nextTickQueue.length; + if (nextTickLength === 0) return tickDone(); + while (nextTickIndex < nextTickLength) { + var tock = nextTickQueue[nextTickIndex++]; + var callback = tock.callback; + if (tock.domain) { + if (tock.domain._disposed) continue; + tock.domain.enter(); + } + var threw = true; + try { + callback(); + threw = false; + } finally { + // finally blocks fire before the error hits the top level, + // so we can't clear the tickDepth at this point. + if (threw) tickDone(tickDepth); + } + if (tock.domain) { + tock.domain.exit(); + } + } + nextTickQueue.splice(0, nextTickIndex); + nextTickIndex = 0; + + // continue until the max depth or we run out of tocks. + } while (tickDepth < process.maxTickDepth && + nextTickQueue.length > 0); + + tickDone(); }; process.nextTick = function(callback) { + // on the way out, don't bother. + // it won't get fired anyway. + if (process._exiting) return; + var tock = { callback: callback }; if (process.domain) tock.domain = process.domain; nextTickQueue.push(tock); - process._needTickCallback(); + if (nextTickQueue.length) { + process._needTickCallback(); + } }; }; |