-
Notifications
You must be signed in to change notification settings - Fork 29.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
timers: refactor setImmediate error handling
If an error is encountered during the processing of Immediates, schedule the remaining queue to finish after all error handling code runs (if the process is still alive to do so). The new changes make the Immediates error handling behaviour entirely deterministic and predictable, as the full queue will be flushed on each Immediates cycle, regardless of whether an error is encountered or not. Currently this processing is scheduled for nextTick which can yield unpredictable results as the nextTick might happen as early as close callbacks phase or as late as after the next event loop turns Immediates all fully processed. The latter can result in two full cycles of Immediates processing during one even loop turn. The current implementation also doesn't differentiate between Immediates scheduled for the current queue run or the next one, so Immediates that were scheduled for the next turn of the event loop, will process alongside the ones that were scheduled for the current turn. Backport-PR-URL: #19006 PR-URL: #17879 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
- Loading branch information
1 parent
07c6fb9
commit 37f253e
Showing
6 changed files
with
160 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const domain = require('domain'); | ||
|
||
// setImmediate should run clear its queued cbs once per event loop turn | ||
// but immediates queued while processing the current queue should happen | ||
// on the next turn of the event loop. | ||
|
||
// In addition, if any setImmediate throws, the rest of the queue should | ||
// be processed after all error handling is resolved, but that queue | ||
// should not include any setImmediate calls scheduled after the | ||
// processing of the queue started. | ||
|
||
let threw = false; | ||
let stage = -1; | ||
|
||
const QUEUE = 10; | ||
|
||
const errObj = { | ||
type: Error, | ||
message: 'setImmediate Err' | ||
}; | ||
|
||
process.once('uncaughtException', common.expectsError(errObj)); | ||
process.once('uncaughtException', () => assert.strictEqual(stage, 0)); | ||
|
||
const d1 = domain.create(); | ||
d1.once('error', common.expectsError(errObj)); | ||
d1.once('error', () => assert.strictEqual(stage, 0)); | ||
|
||
const run = common.mustCall((callStage) => { | ||
assert(callStage >= stage); | ||
stage = callStage; | ||
if (threw) | ||
return; | ||
|
||
setImmediate(run, 2); | ||
}, QUEUE * 3); | ||
|
||
for (let i = 0; i < QUEUE; i++) | ||
setImmediate(run, 0); | ||
setImmediate(() => { | ||
threw = true; | ||
process.nextTick(() => assert.strictEqual(stage, 1)); | ||
throw new Error('setImmediate Err'); | ||
}); | ||
d1.run(() => setImmediate(() => { | ||
throw new Error('setImmediate Err'); | ||
})); | ||
for (let i = 0; i < QUEUE; i++) | ||
setImmediate(run, 1); |