Skip to content

Commit 25fef3d

Browse files
lundibunditargos
authored andcommitted
workers: fix invalid exit code in parent upon uncaught exception
Now worker.on('exit') reports correct exit code (1) if worker has exited with uncaught exception. Fixes: #21707 PR-URL: #21713 Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Yuta Hiroto <[email protected]> Reviewed-By: Weijia Wang <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 122ae24 commit 25fef3d

4 files changed

+141
-0
lines changed

lib/internal/worker.js

+3
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,9 @@ function setupChild(evalScript) {
454454
debug(`[${threadId}] fatal exception caught = ${caught}`);
455455

456456
if (!caught) {
457+
// set correct code (uncaughtException) for [kOnExit](code) handler
458+
process.exitCode = 1;
459+
457460
let serialized;
458461
try {
459462
serialized = serializeError(error);
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Flags: --experimental-worker
2+
'use strict';
3+
const common = require('../common');
4+
5+
// This test checks that Worker has correct exit codes on parent side
6+
// in multiple situations.
7+
8+
const assert = require('assert');
9+
const worker = require('worker_threads');
10+
const { Worker, isMainThread, parentPort } = worker;
11+
12+
if (isMainThread) {
13+
parent();
14+
} else {
15+
if (!parentPort) {
16+
console.error('Parent port must not be null');
17+
process.exit(100);
18+
return;
19+
}
20+
parentPort.once('message', (msg) => {
21+
switch (msg) {
22+
case 'child1':
23+
return child1();
24+
case 'child2':
25+
return child2();
26+
case 'child3':
27+
return child3();
28+
case 'child4':
29+
return child4();
30+
case 'child5':
31+
return child5();
32+
case 'child6':
33+
return child6();
34+
case 'child7':
35+
return child7();
36+
default:
37+
throw new Error('invalid');
38+
}
39+
});
40+
}
41+
42+
function child1() {
43+
process.exitCode = 42;
44+
process.on('exit', (code) => {
45+
assert.strictEqual(code, 42);
46+
});
47+
}
48+
49+
function child2() {
50+
process.exitCode = 99;
51+
process.on('exit', (code) => {
52+
assert.strictEqual(code, 42);
53+
});
54+
process.exit(42);
55+
}
56+
57+
function child3() {
58+
process.exitCode = 99;
59+
process.on('exit', (code) => {
60+
assert.strictEqual(code, 0);
61+
});
62+
process.exit(0);
63+
}
64+
65+
function child4() {
66+
process.exitCode = 99;
67+
process.on('exit', (code) => {
68+
// cannot use assert because it will be uncaughtException -> 1 exit code
69+
// that will render this test useless
70+
if (code !== 1) {
71+
console.error('wrong code! expected 1 for uncaughtException');
72+
process.exit(99);
73+
}
74+
});
75+
throw new Error('ok');
76+
}
77+
78+
function child5() {
79+
process.exitCode = 95;
80+
process.on('exit', (code) => {
81+
assert.strictEqual(code, 95);
82+
process.exitCode = 99;
83+
});
84+
}
85+
86+
function child6() {
87+
process.on('exit', (code) => {
88+
assert.strictEqual(code, 0);
89+
});
90+
process.on('uncaughtException', common.mustCall(() => {
91+
// handle
92+
}));
93+
throw new Error('ok');
94+
}
95+
96+
function child7() {
97+
process.on('exit', (code) => {
98+
assert.strictEqual(code, 97);
99+
});
100+
process.on('uncaughtException', common.mustCall(() => {
101+
process.exitCode = 97;
102+
}));
103+
throw new Error('ok');
104+
}
105+
106+
function parent() {
107+
const test = (arg, exit, error = null) => {
108+
const w = new Worker(__filename);
109+
w.on('exit', common.mustCall((code) => {
110+
assert.strictEqual(
111+
code, exit,
112+
`wrong exit for ${arg}\nexpected:${exit} but got:${code}`);
113+
console.log(`ok - ${arg} exited with ${exit}`);
114+
}));
115+
if (error) {
116+
w.on('error', common.mustCall((err) => {
117+
assert(error.test(err));
118+
}));
119+
}
120+
w.postMessage(arg);
121+
};
122+
123+
test('child1', 42);
124+
test('child2', 42);
125+
test('child3', 0);
126+
test('child4', 1, /^Error: ok$/);
127+
test('child5', 99);
128+
test('child6', 0);
129+
test('child7', 97);
130+
}

test/parallel/test-worker-uncaught-exception-async.js

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ if (!process.env.HAS_STARTED_WORKER) {
1212
w.on('error', common.mustCall((err) => {
1313
assert(/^Error: foo$/.test(err));
1414
}));
15+
w.on('exit', common.mustCall((code) => {
16+
// uncaughtException is code 1
17+
assert.strictEqual(code, 1);
18+
}));
1519
} else {
1620
setImmediate(() => {
1721
throw new Error('foo');

test/parallel/test-worker-uncaught-exception.js

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ if (!process.env.HAS_STARTED_WORKER) {
1212
w.on('error', common.mustCall((err) => {
1313
assert(/^Error: foo$/.test(err));
1414
}));
15+
w.on('exit', common.mustCall((code) => {
16+
// uncaughtException is code 1
17+
assert.strictEqual(code, 1);
18+
}));
1519
} else {
1620
throw new Error('foo');
1721
}

0 commit comments

Comments
 (0)