mirror of
https://github.com/nodejs/node.git
synced 2025-05-10 14:09:34 +00:00

Fix two races in test-performance-eventlooputil resulting in a flaky test. elu1 was capture after start time t from spin look. If OS descides to reschedule the process after capturing t but before getting elu for >=50ms the spin loop is actually a nop. elu1 doesn't show this and as a result elut3 = eventLoopUtilization(elu1) results in elu3.active === 0. Moving capturing of t after capturing t, just before the spin look avoids this. Similar if OS decides to shedule a different process between getting the total elu from start and the diff elu showing the spin loop the check to verify that total active time is long then the spin loop fails. Exchanging these statements avoids this race. PR-URL: https://github.com/nodejs/node/pull/36028 Fixes: https://github.com/nodejs/node/issues/35309 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
107 lines
3.8 KiB
JavaScript
107 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const { mustCall } = require('../common');
|
|
|
|
const TIMEOUT = 10;
|
|
const SPIN_DUR = 50;
|
|
|
|
const assert = require('assert');
|
|
const { performance } = require('perf_hooks');
|
|
const { Worker, parentPort } = require('worker_threads');
|
|
|
|
const { nodeTiming, eventLoopUtilization } = performance;
|
|
const elu = eventLoopUtilization();
|
|
|
|
// Take into account whether this test was started as a Worker.
|
|
if (nodeTiming.loopStart === -1) {
|
|
assert.strictEqual(nodeTiming.idleTime, 0);
|
|
assert.deepStrictEqual(elu, { idle: 0, active: 0, utilization: 0 });
|
|
assert.deepStrictEqual(eventLoopUtilization(elu),
|
|
{ idle: 0, active: 0, utilization: 0 });
|
|
assert.deepStrictEqual(eventLoopUtilization(elu, eventLoopUtilization()),
|
|
{ idle: 0, active: 0, utilization: 0 });
|
|
}
|
|
|
|
setTimeout(mustCall(function r() {
|
|
const elu1 = eventLoopUtilization();
|
|
|
|
// Force idle time to accumulate before allowing test to continue.
|
|
if (elu1.idle <= 0)
|
|
return setTimeout(mustCall(r), 5);
|
|
|
|
const t = Date.now();
|
|
while (Date.now() - t < SPIN_DUR) { }
|
|
|
|
const elu2 = eventLoopUtilization(elu1);
|
|
const elu3 = eventLoopUtilization();
|
|
const elu4 = eventLoopUtilization(elu3, elu1);
|
|
|
|
assert.strictEqual(elu2.idle, 0);
|
|
assert.strictEqual(elu4.idle, 0);
|
|
assert.strictEqual(elu2.utilization, 1);
|
|
assert.strictEqual(elu4.utilization, 1);
|
|
assert.strictEqual(elu3.active - elu1.active, elu4.active);
|
|
assert.ok(elu2.active > SPIN_DUR - 10, `${elu2.active} <= ${SPIN_DUR - 10}`);
|
|
assert.ok(elu2.active < elu4.active, `${elu2.active} >= ${elu4.active}`);
|
|
assert.ok(elu3.active > elu2.active, `${elu3.active} <= ${elu2.active}`);
|
|
assert.ok(elu3.active > elu4.active, `${elu3.active} <= ${elu4.active}`);
|
|
|
|
setTimeout(mustCall(runIdleTimeTest), TIMEOUT);
|
|
}), 5);
|
|
|
|
function runIdleTimeTest() {
|
|
const idleTime = nodeTiming.idleTime;
|
|
const elu1 = eventLoopUtilization();
|
|
const sum = elu1.idle + elu1.active;
|
|
|
|
assert.ok(sum >= elu1.idle && sum >= elu1.active,
|
|
`idle: ${elu1.idle} active: ${elu1.active} sum: ${sum}`);
|
|
assert.strictEqual(elu1.idle, idleTime);
|
|
assert.strictEqual(elu1.utilization, elu1.active / sum);
|
|
|
|
setTimeout(mustCall(runCalcTest), TIMEOUT, elu1);
|
|
}
|
|
|
|
function runCalcTest(elu1) {
|
|
const now = performance.now();
|
|
const elu2 = eventLoopUtilization();
|
|
const elu3 = eventLoopUtilization(elu2, elu1);
|
|
const active_delta = elu2.active - elu1.active;
|
|
const idle_delta = elu2.idle - elu1.idle;
|
|
|
|
assert.ok(elu2.idle >= 0, `${elu2.idle} < 0`);
|
|
assert.ok(elu2.active >= 0, `${elu2.active} < 0`);
|
|
assert.ok(elu3.idle >= 0, `${elu3.idle} < 0`);
|
|
assert.ok(elu3.active >= 0, `${elu3.active} < 0`);
|
|
assert.ok(elu2.idle + elu2.active > elu1.idle + elu1.active,
|
|
`${elu2.idle + elu2.active} <= ${elu1.idle + elu1.active}`);
|
|
assert.ok(elu2.idle + elu2.active >= now - nodeTiming.loopStart,
|
|
`${elu2.idle + elu2.active} < ${now - nodeTiming.loopStart}`);
|
|
assert.strictEqual(elu3.active, elu2.active - elu1.active);
|
|
assert.strictEqual(elu3.idle, elu2.idle - elu1.idle);
|
|
assert.strictEqual(elu3.utilization,
|
|
active_delta / (idle_delta + active_delta));
|
|
|
|
setImmediate(mustCall(runWorkerTest));
|
|
}
|
|
|
|
function runWorkerTest() {
|
|
// Use argv to detect whether we're running as a Worker called by this test
|
|
// vs. this test also being called as a Worker.
|
|
if (process.argv[2] === 'iamalive') {
|
|
parentPort.postMessage(JSON.stringify(eventLoopUtilization()));
|
|
return;
|
|
}
|
|
|
|
const elu1 = eventLoopUtilization();
|
|
const worker = new Worker(__filename, { argv: [ 'iamalive' ] });
|
|
|
|
worker.on('message', mustCall((msg) => {
|
|
const elu2 = eventLoopUtilization(elu1);
|
|
const data = JSON.parse(msg);
|
|
|
|
assert.ok(elu2.active + elu2.idle > data.active + data.idle,
|
|
`${elu2.active + elu2.idle} <= ${data.active + data.idle}`);
|
|
}));
|
|
}
|