process: fix process.exitCode handling for fatalException
* set process.exitCode before calling 'exit' handlers so that there will not be a situation where process.exitCode !== code in 'exit' callback during uncaughtException handling * don't ignore process.exitCode set in 'exit' callback when failed with uncaughtException and there is no uncaughtException listener PR-URL: https://github.com/nodejs/node/pull/21739 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
998f9ffd42
commit
35326f27fd
@ -151,9 +151,13 @@ added: v0.1.18
|
|||||||
|
|
||||||
The `'uncaughtException'` event is emitted when an uncaught JavaScript
|
The `'uncaughtException'` event is emitted when an uncaught JavaScript
|
||||||
exception bubbles all the way back to the event loop. By default, Node.js
|
exception bubbles all the way back to the event loop. By default, Node.js
|
||||||
handles such exceptions by printing the stack trace to `stderr` and exiting.
|
handles such exceptions by printing the stack trace to `stderr` and exiting
|
||||||
|
with code 1, overriding any previously set [`process.exitCode`][].
|
||||||
Adding a handler for the `'uncaughtException'` event overrides this default
|
Adding a handler for the `'uncaughtException'` event overrides this default
|
||||||
behavior.
|
behavior. You may also change the [`process.exitCode`][] in
|
||||||
|
`'uncaughtException'` handler which will result in process exiting with
|
||||||
|
provided exit code, otherwise in the presence of such handler the process will
|
||||||
|
exit with 0.
|
||||||
|
|
||||||
The listener function is called with the `Error` object passed as the only
|
The listener function is called with the `Error` object passed as the only
|
||||||
argument.
|
argument.
|
||||||
|
@ -475,6 +475,7 @@
|
|||||||
try {
|
try {
|
||||||
if (!process._exiting) {
|
if (!process._exiting) {
|
||||||
process._exiting = true;
|
process._exiting = true;
|
||||||
|
process.exitCode = 1;
|
||||||
process.emit('exit', 1);
|
process.emit('exit', 1);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -454,9 +454,6 @@ function setupChild(evalScript) {
|
|||||||
debug(`[${threadId}] fatal exception caught = ${caught}`);
|
debug(`[${threadId}] fatal exception caught = ${caught}`);
|
||||||
|
|
||||||
if (!caught) {
|
if (!caught) {
|
||||||
// set correct code (uncaughtException) for [kOnExit](code) handler
|
|
||||||
process.exitCode = 1;
|
|
||||||
|
|
||||||
let serialized;
|
let serialized;
|
||||||
try {
|
try {
|
||||||
serialized = serializeError(error);
|
serialized = serializeError(error);
|
||||||
|
@ -1439,8 +1439,17 @@ void FatalException(Isolate* isolate,
|
|||||||
exit(7);
|
exit(7);
|
||||||
} else if (caught->IsFalse()) {
|
} else if (caught->IsFalse()) {
|
||||||
ReportException(env, error, message);
|
ReportException(env, error, message);
|
||||||
|
|
||||||
|
// fatal_exception_function call before may have set a new exit code ->
|
||||||
|
// read it again, otherwise use default for uncaughtException 1
|
||||||
|
Local<String> exit_code = env->exit_code_string();
|
||||||
|
Local<Value> code;
|
||||||
|
if (!process_object->Get(env->context(), exit_code).ToLocal(&code) ||
|
||||||
|
!code->IsInt32()) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
exit(code.As<v8::Int32>()->Value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,14 @@ switch (process.argv[2]) {
|
|||||||
return child4();
|
return child4();
|
||||||
case 'child5':
|
case 'child5':
|
||||||
return child5();
|
return child5();
|
||||||
|
case 'child6':
|
||||||
|
return child6();
|
||||||
|
case 'child7':
|
||||||
|
return child7();
|
||||||
|
case 'child8':
|
||||||
|
return child8();
|
||||||
|
case 'child9':
|
||||||
|
return child9();
|
||||||
case undefined:
|
case undefined:
|
||||||
return parent();
|
return parent();
|
||||||
default:
|
default:
|
||||||
@ -43,6 +51,7 @@ switch (process.argv[2]) {
|
|||||||
function child1() {
|
function child1() {
|
||||||
process.exitCode = 42;
|
process.exitCode = 42;
|
||||||
process.on('exit', function(code) {
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 42);
|
||||||
assert.strictEqual(code, 42);
|
assert.strictEqual(code, 42);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,6 +59,7 @@ function child1() {
|
|||||||
function child2() {
|
function child2() {
|
||||||
process.exitCode = 99;
|
process.exitCode = 99;
|
||||||
process.on('exit', function(code) {
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 42);
|
||||||
assert.strictEqual(code, 42);
|
assert.strictEqual(code, 42);
|
||||||
});
|
});
|
||||||
process.exit(42);
|
process.exit(42);
|
||||||
@ -58,6 +68,7 @@ function child2() {
|
|||||||
function child3() {
|
function child3() {
|
||||||
process.exitCode = 99;
|
process.exitCode = 99;
|
||||||
process.on('exit', function(code) {
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 0);
|
||||||
assert.strictEqual(code, 0);
|
assert.strictEqual(code, 0);
|
||||||
});
|
});
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@ -66,7 +77,7 @@ function child3() {
|
|||||||
function child4() {
|
function child4() {
|
||||||
process.exitCode = 99;
|
process.exitCode = 99;
|
||||||
process.on('exit', function(code) {
|
process.on('exit', function(code) {
|
||||||
if (code !== 1) {
|
if (code !== 1 || process.exitCode !== 1) {
|
||||||
console.log('wrong code! expected 1 for uncaughtException');
|
console.log('wrong code! expected 1 for uncaughtException');
|
||||||
process.exit(99);
|
process.exit(99);
|
||||||
}
|
}
|
||||||
@ -77,11 +88,50 @@ function child4() {
|
|||||||
function child5() {
|
function child5() {
|
||||||
process.exitCode = 95;
|
process.exitCode = 95;
|
||||||
process.on('exit', function(code) {
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 95);
|
||||||
assert.strictEqual(code, 95);
|
assert.strictEqual(code, 95);
|
||||||
process.exitCode = 99;
|
process.exitCode = 99;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function child6() {
|
||||||
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 0);
|
||||||
|
assert.strictEqual(code, 0);
|
||||||
|
});
|
||||||
|
process.on('uncaughtException', () => {});
|
||||||
|
throw new Error('ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
function child7() {
|
||||||
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 97);
|
||||||
|
assert.strictEqual(code, 97);
|
||||||
|
});
|
||||||
|
process.on('uncaughtException', () => {
|
||||||
|
process.exitCode = 97;
|
||||||
|
});
|
||||||
|
throw new Error('ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
function child8() {
|
||||||
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 1);
|
||||||
|
assert.strictEqual(code, 1);
|
||||||
|
process.exitCode = 98;
|
||||||
|
});
|
||||||
|
throw new Error('ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
function child9() {
|
||||||
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 1);
|
||||||
|
assert.strictEqual(code, 1);
|
||||||
|
process.exitCode = 0;
|
||||||
|
});
|
||||||
|
throw new Error('ok');
|
||||||
|
}
|
||||||
|
|
||||||
function parent() {
|
function parent() {
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
const node = process.execPath;
|
const node = process.execPath;
|
||||||
@ -102,4 +152,8 @@ function parent() {
|
|||||||
test('child3', 0);
|
test('child3', 0);
|
||||||
test('child4', 1);
|
test('child4', 1);
|
||||||
test('child5', 99);
|
test('child5', 99);
|
||||||
|
test('child6', 0);
|
||||||
|
test('child7', 97);
|
||||||
|
test('child8', 98);
|
||||||
|
test('child9', 0);
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,10 @@ if (!process.env.HAS_STARTED_WORKER) {
|
|||||||
return child6();
|
return child6();
|
||||||
case 'child7':
|
case 'child7':
|
||||||
return child7();
|
return child7();
|
||||||
|
case 'child8':
|
||||||
|
return child8();
|
||||||
|
case 'child9':
|
||||||
|
return child9();
|
||||||
default:
|
default:
|
||||||
throw new Error('invalid');
|
throw new Error('invalid');
|
||||||
}
|
}
|
||||||
@ -105,6 +109,24 @@ function child7() {
|
|||||||
throw new Error('ok');
|
throw new Error('ok');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function child8() {
|
||||||
|
process.on('exit', (code) => {
|
||||||
|
assert.strictEqual(process.exitCode, 1);
|
||||||
|
assert.strictEqual(code, 1);
|
||||||
|
process.exitCode = 98;
|
||||||
|
});
|
||||||
|
throw new Error('ok');
|
||||||
|
}
|
||||||
|
|
||||||
|
function child9() {
|
||||||
|
process.on('exit', function(code) {
|
||||||
|
assert.strictEqual(process.exitCode, 1);
|
||||||
|
assert.strictEqual(code, 1);
|
||||||
|
process.exitCode = 0;
|
||||||
|
});
|
||||||
|
throw new Error('ok');
|
||||||
|
}
|
||||||
|
|
||||||
function parent() {
|
function parent() {
|
||||||
const test = (arg, exit, error = null) => {
|
const test = (arg, exit, error = null) => {
|
||||||
const w = new Worker(__filename);
|
const w = new Worker(__filename);
|
||||||
@ -116,7 +138,9 @@ function parent() {
|
|||||||
}));
|
}));
|
||||||
if (error) {
|
if (error) {
|
||||||
w.on('error', common.mustCall((err) => {
|
w.on('error', common.mustCall((err) => {
|
||||||
assert(error.test(err));
|
console.log(err);
|
||||||
|
assert(error.test(err),
|
||||||
|
`wrong error for ${arg}\nexpected:${error} but got:${err}`);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
w.postMessage(arg);
|
w.postMessage(arg);
|
||||||
@ -129,4 +153,6 @@ function parent() {
|
|||||||
test('child5', 99);
|
test('child5', 99);
|
||||||
test('child6', 0);
|
test('child6', 0);
|
||||||
test('child7', 97);
|
test('child7', 97);
|
||||||
|
test('child8', 98, /^Error: ok$/);
|
||||||
|
test('child9', 0, /^Error: ok$/);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ if (!process.env.HAS_STARTED_WORKER) {
|
|||||||
const w = new Worker(__filename);
|
const w = new Worker(__filename);
|
||||||
w.on('message', common.mustNotCall());
|
w.on('message', common.mustNotCall());
|
||||||
w.on('error', common.mustCall((err) => {
|
w.on('error', common.mustCall((err) => {
|
||||||
|
console.log(err.message);
|
||||||
assert(/^Error: foo$/.test(err));
|
assert(/^Error: foo$/.test(err));
|
||||||
}));
|
}));
|
||||||
w.on('exit', common.mustCall((code) => {
|
w.on('exit', common.mustCall((code) => {
|
||||||
@ -17,5 +18,14 @@ if (!process.env.HAS_STARTED_WORKER) {
|
|||||||
assert.strictEqual(code, 1);
|
assert.strictEqual(code, 1);
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
|
// cannot use common.mustCall as it cannot catch this
|
||||||
|
let called = false;
|
||||||
|
process.on('exit', (code) => {
|
||||||
|
if (!called) {
|
||||||
|
called = true;
|
||||||
|
} else {
|
||||||
|
assert.fail('Exit callback called twice in worker');
|
||||||
|
}
|
||||||
|
});
|
||||||
throw new Error('foo');
|
throw new Error('foo');
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user