summaryrefslogtreecommitdiff
path: root/lib/domain.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/domain.js')
-rw-r--r--lib/domain.js60
1 files changed, 51 insertions, 9 deletions
diff --git a/lib/domain.js b/lib/domain.js
index eab0b352bc..b2ffed2741 100644
--- a/lib/domain.js
+++ b/lib/domain.js
@@ -132,7 +132,7 @@ function topLevelDomainCallback(cb, ...args) {
// It's possible to enter one domain while already inside
// another one. The stack is each entered domain.
-const stack = [];
+let stack = [];
exports._stack = stack;
internalBinding('domain').enable(topLevelDomainCallback);
@@ -211,6 +211,13 @@ Domain.prototype._errorHandler = function(er) {
});
er.domainThrown = true;
}
+ // Pop all adjacent duplicates of the currently active domain from the stack.
+ // This is done to prevent a domain's error handler to run within the context
+ // of itself, and re-entering itself recursively handler as a result of an
+ // exception thrown in its context.
+ while (exports.active === this) {
+ this.exit();
+ }
// The top-level domain-handler is handled separately.
//
@@ -221,15 +228,15 @@ Domain.prototype._errorHandler = function(er) {
// process abort. Using try/catch here would always make V8 think
// that these exceptions are caught, and thus would prevent it from
// aborting in these cases.
- if (stack.length === 1) {
+ if (stack.length === 0) {
// If there's no error handler, do not emit an 'error' event
// as this would throw an error, make the process exit, and thus
// prevent the process 'uncaughtException' event from being emitted
// if a listener is set.
if (EventEmitter.listenerCount(this, 'error') > 0) {
- // Clear the uncaughtExceptionCaptureCallback so that we know that, even
- // if technically the top-level domain is still active, it would
- // be ok to abort on an uncaught exception at this point
+ // Clear the uncaughtExceptionCaptureCallback so that we know that, since
+ // the top-level domain is not active anymore, it would be ok to abort on
+ // an uncaught exception at this point
setUncaughtExceptionCaptureCallback(null);
try {
caught = this.emit('error', er);
@@ -253,10 +260,6 @@ Domain.prototype._errorHandler = function(er) {
// The domain error handler threw! oh no!
// See if another domain can catch THIS error,
// or else crash on the original one.
- // If the user already exited it, then don't double-exit.
- if (this === exports.active) {
- stack.pop();
- }
updateExceptionCapture();
if (stack.length) {
exports.active = process.domain = stack[stack.length - 1];
@@ -486,7 +489,46 @@ EventEmitter.prototype.emit = function(...args) {
er.domainThrown = false;
}
+ // Remove the current domain (and its duplicates) from the domains stack and
+ // set the active domain to its parent (if any) so that the domain's error
+ // handler doesn't run in its own context. This prevents any event emitter
+ // created or any exception thrown in that error handler from recursively
+ // executing that error handler.
+ const origDomainsStack = stack.slice();
+ const origActiveDomain = process.domain;
+
+ // Travel the domains stack from top to bottom to find the first domain
+ // instance that is not a duplicate of the current active domain.
+ let idx = stack.length - 1;
+ while (idx > -1 && process.domain === stack[idx]) {
+ --idx;
+ }
+
+ // Change the stack to not contain the current active domain, and only the
+ // domains above it on the stack.
+ if (idx < 0) {
+ stack.length = 0;
+ } else {
+ stack.splice(idx + 1);
+ }
+
+ // Change the current active domain
+ if (stack.length > 0) {
+ exports.active = process.domain = stack[stack.length - 1];
+ } else {
+ exports.active = process.domain = null;
+ }
+
+ updateExceptionCapture();
+
domain.emit('error', er);
+
+ // Now that the domain's error handler has completed, restore the domains
+ // stack and the active domain to their original values.
+ exports._stack = stack = origDomainsStack;
+ exports.active = process.domain = origActiveDomain;
+ updateExceptionCapture();
+
return false;
}