summaryrefslogtreecommitdiff
path: root/src/node.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/node.js')
-rw-r--r--src/node.js124
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();
+ }
};
};