summaryrefslogtreecommitdiff
path: root/lib/domain.js
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2017-11-20 19:57:20 +0100
committerAnna Henningsen <anna@addaleax.net>2017-11-29 15:58:42 +0100
commit4503da8a3a3b0b71d950a63de729ce495965f6ea (patch)
tree66ad243cd90cea3df8fc11eb7cf29597ddd56eb3 /lib/domain.js
parent04e3aa28bbcdf62f677dd314ad8c12556c18c15f (diff)
downloadandroid-node-v8-4503da8a3a3b0b71d950a63de729ce495965f6ea.tar.gz
android-node-v8-4503da8a3a3b0b71d950a63de729ce495965f6ea.tar.bz2
android-node-v8-4503da8a3a3b0b71d950a63de729ce495965f6ea.zip
process: add flag for uncaught exception abort
Introduce `process.shouldAbortOnUncaughtException` to control `--abort-on-uncaught-exception` behaviour, and implement some of the domains functionality on top of it. PR-URL: https://github.com/nodejs/node/pull/17159 Refs: https://github.com/nodejs/node/issues/17143 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Diffstat (limited to 'lib/domain.js')
-rw-r--r--lib/domain.js84
1 files changed, 73 insertions, 11 deletions
diff --git a/lib/domain.js b/lib/domain.js
index dc3c550866..6c85ca2b17 100644
--- a/lib/domain.js
+++ b/lib/domain.js
@@ -28,6 +28,7 @@
const util = require('util');
const EventEmitter = require('events');
+const errors = require('internal/errors');
const { createHook } = require('async_hooks');
// communicate with events module, but don't require that
@@ -81,19 +82,77 @@ const asyncHook = createHook({
}
});
+// When domains are in use, they claim full ownership of the
+// uncaught exception capture callback.
+if (process.hasUncaughtExceptionCaptureCallback()) {
+ throw new errors.Error('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE');
+}
+
+// Get the stack trace at the point where `domain` was required.
+const domainRequireStack = new Error('require(`domain`) at this point').stack;
+
+const { setUncaughtExceptionCaptureCallback } = process;
+process.setUncaughtExceptionCaptureCallback = function(fn) {
+ const err =
+ new errors.Error('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE');
+ err.stack = err.stack + '\n' + '-'.repeat(40) + '\n' + domainRequireStack;
+ throw err;
+};
+
// It's possible to enter one domain while already inside
// another one. The stack is each entered domain.
const stack = [];
exports._stack = stack;
-process._setupDomainUse(stack);
+process._setupDomainUse();
-class Domain extends EventEmitter {
+function updateExceptionCapture() {
+ if (stack.every((domain) => domain.listenerCount('error') === 0)) {
+ setUncaughtExceptionCaptureCallback(null);
+ } else {
+ setUncaughtExceptionCaptureCallback(null);
+ setUncaughtExceptionCaptureCallback((er) => {
+ return process.domain._errorHandler(er);
+ });
+ }
+}
+
+
+process.on('newListener', (name, listener) => {
+ if (name === 'uncaughtException' &&
+ listener !== domainUncaughtExceptionClear) {
+ // Make sure the first listener for `uncaughtException` always clears
+ // the domain stack.
+ process.removeListener(name, domainUncaughtExceptionClear);
+ process.prependListener(name, domainUncaughtExceptionClear);
+ }
+});
+
+process.on('removeListener', (name, listener) => {
+ if (name === 'uncaughtException' &&
+ listener !== domainUncaughtExceptionClear) {
+ // If the domain listener would be the only remaining one, remove it.
+ const listeners = process.listeners('uncaughtException');
+ if (listeners.length === 1 && listeners[0] === domainUncaughtExceptionClear)
+ process.removeListener(name, domainUncaughtExceptionClear);
+ }
+});
+function domainUncaughtExceptionClear() {
+ stack.length = 0;
+ exports.active = process.domain = null;
+ updateExceptionCapture();
+}
+
+
+class Domain extends EventEmitter {
constructor() {
super();
this.members = [];
asyncHook.enable();
+
+ this.on('removeListener', updateExceptionCapture);
+ this.on('newListener', updateExceptionCapture);
}
}
@@ -131,14 +190,14 @@ Domain.prototype._errorHandler = function _errorHandler(er) {
// 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
+ setUncaughtExceptionCaptureCallback(null);
try {
- // Set the _emittingTopLevelDomainError 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
- process._emittingTopLevelDomainError = true;
caught = this.emit('error', er);
} finally {
- process._emittingTopLevelDomainError = false;
+ updateExceptionCapture();
}
}
} else {
@@ -161,11 +220,13 @@ Domain.prototype._errorHandler = function _errorHandler(er) {
if (this === exports.active) {
stack.pop();
}
+ updateExceptionCapture();
if (stack.length) {
exports.active = process.domain = stack[stack.length - 1];
- caught = process._fatalException(er2);
+ caught = process.domain._errorHandler(er2);
} else {
- caught = false;
+ // Pass on to the next exception handler.
+ throw er2;
}
}
}
@@ -173,8 +234,7 @@ Domain.prototype._errorHandler = function _errorHandler(er) {
// Exit all domains on the stack. Uncaught exceptions end the
// current tick and no domains should be left on the stack
// between ticks.
- stack.length = 0;
- exports.active = process.domain = null;
+ domainUncaughtExceptionClear();
return caught;
};
@@ -185,6 +245,7 @@ Domain.prototype.enter = function() {
// to push it onto the stack so that we can pop it later.
exports.active = process.domain = this;
stack.push(this);
+ updateExceptionCapture();
};
@@ -198,6 +259,7 @@ Domain.prototype.exit = function() {
exports.active = stack[stack.length - 1];
process.domain = exports.active;
+ updateExceptionCapture();
};