mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
The depth of the stack depends not only on the stack size, but also on the size of each stack frame, which in turn depends on which tier the recursive function happens to be running at when the overflow occurs. Under load the background tier-up can land at a non-deterministic point in the recursion and flake the test. Keep the recursive function in the interpreter with %NeverOptimizeFunction() so the frame size - and thus the depth - is deterministic. Signed-off-by: Joyee Cheung <joyeec9h3@gmail.com> PR-URL: https://github.com/nodejs/node/pull/64271 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
73 lines
2.6 KiB
JavaScript
73 lines
2.6 KiB
JavaScript
'use strict';
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
const { once } = require('events');
|
|
const v8 = require('v8');
|
|
const { Worker } = require('worker_threads');
|
|
|
|
// Verify that Workers don't care about --stack-size, as they have their own
|
|
// fixed and known stack sizes.
|
|
|
|
// The depth of the stack depends not only on the stack size, but also on the size of each
|
|
// stack frame, which in turn depends on which tier the recursive function happens to be
|
|
// running at when the overflow occurs. Under load the background tier-up can land at a
|
|
// non-deterministic point in the recursion and flake the test. Keep the recursive
|
|
// function in the interpreter with %NeverOptimizeFunction() so the frame size -
|
|
// and thus the depth - is deterministic.
|
|
v8.setFlagsFromString('--allow-natives-syntax');
|
|
|
|
async function runWorker(options = {}) {
|
|
const empiricalStackDepth = new Uint32Array(new SharedArrayBuffer(4));
|
|
const worker = new Worker(`
|
|
const { workerData: { empiricalStackDepth } } = require('worker_threads');
|
|
function f() {
|
|
empiricalStackDepth[0]++;
|
|
f();
|
|
}
|
|
%NeverOptimizeFunction(f);
|
|
f();`, {
|
|
eval: true,
|
|
workerData: { empiricalStackDepth },
|
|
...options
|
|
});
|
|
|
|
const [ error ] = await once(worker, 'error');
|
|
if (!options.skipErrorCheck) {
|
|
common.expectsError({
|
|
constructor: RangeError,
|
|
message: 'Maximum call stack size exceeded'
|
|
})(error);
|
|
}
|
|
|
|
return empiricalStackDepth[0];
|
|
}
|
|
|
|
(async function() {
|
|
{
|
|
v8.setFlagsFromString('--stack-size=500');
|
|
const w1stack = await runWorker();
|
|
v8.setFlagsFromString('--stack-size=1000');
|
|
const w2stack = await runWorker();
|
|
// Make sure the two stack sizes are within 10 % of each other, i.e. not
|
|
// affected by the different `--stack-size` settings.
|
|
assert(Math.max(w1stack, w2stack) / Math.min(w1stack, w2stack) < 1.1,
|
|
`w1stack = ${w1stack}, w2stack = ${w2stack} are too far apart`);
|
|
}
|
|
|
|
{
|
|
const w1stack = await runWorker({ resourceLimits: { stackSizeMb: 0.5 } });
|
|
const w2stack = await runWorker({ resourceLimits: { stackSizeMb: 1.0 } });
|
|
// Make sure the two stack sizes are at least 40 % apart from each other,
|
|
// i.e. affected by the different `stackSizeMb` settings.
|
|
assert(w2stack > w1stack * 1.4,
|
|
`w1stack = ${w1stack}, w2stack = ${w2stack} are too close`);
|
|
}
|
|
|
|
// Test that various low stack sizes result in an 'error' event.
|
|
// Currently the stack size needs to be at least 0.3MB for the worker to be
|
|
// bootstrapped properly.
|
|
for (const stackSizeMb of [ 0.3, 0.5, 1 ]) {
|
|
await runWorker({ resourceLimits: { stackSizeMb }, skipErrorCheck: true });
|
|
}
|
|
})().then(common.mustCall());
|