timers: truncate decimal values
Reverts some timers behavior back to as it was before 2930bd1317d15d12738a4896c0a6c05700411b47 That commit introduced an unintended change which allowed non-integer timeouts to actually exist since the value is no longer converted to an integer via a TimeWrap handle directly. Even with the fix in e9de43549843da9f4f081cce917945878967df7 non-integer timeouts are still indeterministic, because libuv does not support them. This fixes the issue by emulating the old behavior: truncate the `_idleTimeout` before using it. See comments in https://github.com/nodejs/node/pull/24214 for more background on this. PR-URL: https://github.com/nodejs/node/pull/24819 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
a0419dd8ca
commit
a7c66b6aae
@ -186,7 +186,7 @@ added: v0.0.1
|
|||||||
Schedules repeated execution of `callback` every `delay` milliseconds.
|
Schedules repeated execution of `callback` every `delay` milliseconds.
|
||||||
|
|
||||||
When `delay` is larger than `2147483647` or less than `1`, the `delay` will be
|
When `delay` is larger than `2147483647` or less than `1`, the `delay` will be
|
||||||
set to `1`.
|
set to `1`. Non-integer delays are truncated to an integer.
|
||||||
|
|
||||||
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ nor of their ordering. The callback will be called as close as possible to the
|
|||||||
time specified.
|
time specified.
|
||||||
|
|
||||||
When `delay` is larger than `2147483647` or less than `1`, the `delay`
|
When `delay` is larger than `2147483647` or less than `1`, the `delay`
|
||||||
will be set to `1`.
|
will be set to `1`. Non-integer delays are truncated to an integer.
|
||||||
|
|
||||||
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
If `callback` is not a function, a [`TypeError`][] will be thrown.
|
||||||
|
|
||||||
|
@ -193,10 +193,13 @@ exports._unrefActive = function(item) {
|
|||||||
// Appends a timer onto the end of an existing timers list, or creates a new
|
// Appends a timer onto the end of an existing timers list, or creates a new
|
||||||
// list if one does not already exist for the specified timeout duration.
|
// list if one does not already exist for the specified timeout duration.
|
||||||
function insert(item, refed, start) {
|
function insert(item, refed, start) {
|
||||||
const msecs = item._idleTimeout;
|
let msecs = item._idleTimeout;
|
||||||
if (msecs < 0 || msecs === undefined)
|
if (msecs < 0 || msecs === undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Truncate so that accuracy of sub-milisecond timers is not assumed.
|
||||||
|
msecs = Math.trunc(msecs);
|
||||||
|
|
||||||
item._idleStart = start;
|
item._idleStart = start;
|
||||||
|
|
||||||
// Use an existing list if there is one, otherwise we need to make a new one.
|
// Use an existing list if there is one, otherwise we need to make a new one.
|
||||||
@ -378,7 +381,9 @@ function unenroll(item) {
|
|||||||
// clearTimeout that makes it clear that the list should not be deleted.
|
// clearTimeout that makes it clear that the list should not be deleted.
|
||||||
// That function could then be used by http and other similar modules.
|
// That function could then be used by http and other similar modules.
|
||||||
if (item[kRefed]) {
|
if (item[kRefed]) {
|
||||||
const list = lists[item._idleTimeout];
|
// Compliment truncation during insert().
|
||||||
|
const msecs = Math.trunc(item._idleTimeout);
|
||||||
|
const list = lists[msecs];
|
||||||
if (list !== undefined && L.isEmpty(list)) {
|
if (list !== undefined && L.isEmpty(list)) {
|
||||||
debug('unenroll: list empty');
|
debug('unenroll: list empty');
|
||||||
queue.removeAt(list.priorityQueuePosition);
|
queue.removeAt(list.priorityQueuePosition);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This test makes sure that non-integer timer delays do not make the process
|
* This test makes sure that non-integer timer delays do not make the process
|
||||||
@ -47,3 +48,37 @@ const interval = setInterval(common.mustCall(() => {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
}, N), TIMEOUT_DELAY);
|
}, N), TIMEOUT_DELAY);
|
||||||
|
|
||||||
|
// Test non-integer delay ordering
|
||||||
|
{
|
||||||
|
const ordering = [];
|
||||||
|
|
||||||
|
setTimeout(common.mustCall(() => {
|
||||||
|
ordering.push(1);
|
||||||
|
}), 1);
|
||||||
|
|
||||||
|
setTimeout(common.mustCall(() => {
|
||||||
|
ordering.push(2);
|
||||||
|
}), 1.8);
|
||||||
|
|
||||||
|
setTimeout(common.mustCall(() => {
|
||||||
|
ordering.push(3);
|
||||||
|
}), 1.1);
|
||||||
|
|
||||||
|
setTimeout(common.mustCall(() => {
|
||||||
|
ordering.push(4);
|
||||||
|
}), 1);
|
||||||
|
|
||||||
|
setTimeout(common.mustCall(() => {
|
||||||
|
const expected = [1, 2, 3, 4];
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
ordering,
|
||||||
|
expected,
|
||||||
|
`Non-integer delay ordering should be ${expected}, but got ${ordering}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2 should always be last of these delays due to ordering guarentees by
|
||||||
|
// the implementation.
|
||||||
|
}), 2);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user