Skip to content

Commit 41f8c8d

Browse files
committed
process: do not directly schedule _tickCallback in _fatalException
When a process encounters a _fatalException that is caught, it should schedule execution of nextTicks but not in an arbitrary place of the next Immediates queue. Instead, add a no-op function to the queue that will ensure processImmediate runs, which will then ensure that nextTicks are processed at the end. PR-URL: #17841 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent fd724c5 commit 41f8c8d

File tree

2 files changed

+44
-25
lines changed

2 files changed

+44
-25
lines changed

lib/internal/bootstrap_node.js

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@
362362
}
363363
}
364364

365+
function noop() {}
366+
365367
function setupProcessFatal() {
366368
const async_wrap = process.binding('async_wrap');
367369
// Arrays containing hook flags and ids for async_hook calls.
@@ -372,23 +374,15 @@
372374
kDefaultTriggerAsyncId, kStackLength } = async_wrap.constants;
373375

374376
process._fatalException = function(er) {
375-
var caught;
376-
377377
// It's possible that kDefaultTriggerAsyncId was set for a constructor
378378
// call that threw and was never cleared. So clear it now.
379379
async_id_fields[kDefaultTriggerAsyncId] = -1;
380380

381381
if (exceptionHandlerState.captureFn !== null) {
382382
exceptionHandlerState.captureFn(er);
383-
caught = true;
384-
}
385-
386-
if (!caught)
387-
caught = process.emit('uncaughtException', er);
388-
389-
// If someone handled it, then great. otherwise, die in C++ land
390-
// since that means that we'll exit the process, emit the 'exit' event
391-
if (!caught) {
383+
} else if (!process.emit('uncaughtException', er)) {
384+
// If someone handled it, then great. otherwise, die in C++ land
385+
// since that means that we'll exit the process, emit the 'exit' event
392386
try {
393387
if (!process._exiting) {
394388
process._exiting = true;
@@ -397,24 +391,25 @@
397391
} catch (er) {
398392
// nothing to be done about it at this point.
399393
}
394+
return false;
395+
}
400396

397+
// If we handled an error, then make sure any ticks get processed
398+
// by ensuring that the next Immediate cycle isn't empty
399+
NativeModule.require('timers').setImmediate(noop);
400+
401+
// Emit the after() hooks now that the exception has been handled.
402+
if (async_hook_fields[kAfter] > 0) {
403+
const { emitAfter } = NativeModule.require('internal/async_hooks');
404+
do {
405+
emitAfter(async_id_fields[kExecutionAsyncId]);
406+
} while (async_hook_fields[kStackLength] > 0);
407+
// Or completely empty the id stack.
401408
} else {
402-
// If we handled an error, then make sure any ticks get processed
403-
NativeModule.require('timers').setImmediate(process._tickCallback);
404-
405-
// Emit the after() hooks now that the exception has been handled.
406-
if (async_hook_fields[kAfter] > 0) {
407-
do {
408-
NativeModule.require('internal/async_hooks').emitAfter(
409-
async_id_fields[kExecutionAsyncId]);
410-
} while (async_hook_fields[kStackLength] > 0);
411-
// Or completely empty the id stack.
412-
} else {
413-
clearAsyncIdStack();
414-
}
409+
clearAsyncIdStack();
415410
}
416411

417-
return caught;
412+
return true;
418413
};
419414
}
420415

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
6+
// If a process encounters an uncaughtException, it should schedule
7+
// processing of nextTicks on the next Immediates cycle but not
8+
// before all Immediates are handled
9+
10+
let stage = 0;
11+
12+
process.once('uncaughtException', common.expectsError({
13+
type: Error,
14+
message: 'caughtException'
15+
}));
16+
17+
setImmediate(() => {
18+
stage++;
19+
process.nextTick(() => assert.strictEqual(stage, 2));
20+
});
21+
const now = Date.now();
22+
setTimeout(() => setImmediate(() => stage++), 1);
23+
while (now + 10 >= Date.now());
24+
throw new Error('caughtException');

0 commit comments

Comments
 (0)