Skip to content

Commit d0705bd

Browse files
Fishrock123targos
authored andcommitted
timers: truncate decimal values
Reverts some timers behavior back to as it was before 2930bd1 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 e9de435 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 #24214 for more background on this. PR-URL: #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>
1 parent edc9ceb commit d0705bd

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

doc/api/timers.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ added: v0.0.1
186186
Schedules repeated execution of `callback` every `delay` milliseconds.
187187

188188
When `delay` is larger than `2147483647` or less than `1`, the `delay` will be
189-
set to `1`.
189+
set to `1`. Non-integer delays are truncated to an integer.
190190

191191
If `callback` is not a function, a [`TypeError`][] will be thrown.
192192

@@ -209,7 +209,7 @@ nor of their ordering. The callback will be called as close as possible to the
209209
time specified.
210210

211211
When `delay` is larger than `2147483647` or less than `1`, the `delay`
212-
will be set to `1`.
212+
will be set to `1`. Non-integer delays are truncated to an integer.
213213

214214
If `callback` is not a function, a [`TypeError`][] will be thrown.
215215

lib/timers.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,13 @@ exports._unrefActive = function(item) {
193193
// Appends a timer onto the end of an existing timers list, or creates a new
194194
// list if one does not already exist for the specified timeout duration.
195195
function insert(item, refed, start) {
196-
const msecs = item._idleTimeout;
196+
let msecs = item._idleTimeout;
197197
if (msecs < 0 || msecs === undefined)
198198
return;
199199

200+
// Truncate so that accuracy of sub-milisecond timers is not assumed.
201+
msecs = Math.trunc(msecs);
202+
200203
item._idleStart = start;
201204

202205
// Use an existing list if there is one, otherwise we need to make a new one.
@@ -378,7 +381,9 @@ function unenroll(item) {
378381
// clearTimeout that makes it clear that the list should not be deleted.
379382
// That function could then be used by http and other similar modules.
380383
if (item[kRefed]) {
381-
const list = lists[item._idleTimeout];
384+
// Compliment truncation during insert().
385+
const msecs = Math.trunc(item._idleTimeout);
386+
const list = lists[msecs];
382387
if (list !== undefined && L.isEmpty(list)) {
383388
debug('unenroll: list empty');
384389
queue.removeAt(list.priorityQueuePosition);

test/parallel/test-timers-non-integer-delay.js

+35
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
'use strict';
2323
const common = require('../common');
24+
const assert = require('assert');
2425

2526
/*
2627
* This test makes sure that non-integer timer delays do not make the process
@@ -47,3 +48,37 @@ const interval = setInterval(common.mustCall(() => {
4748
process.exit(0);
4849
}
4950
}, N), TIMEOUT_DELAY);
51+
52+
// Test non-integer delay ordering
53+
{
54+
const ordering = [];
55+
56+
setTimeout(common.mustCall(() => {
57+
ordering.push(1);
58+
}), 1);
59+
60+
setTimeout(common.mustCall(() => {
61+
ordering.push(2);
62+
}), 1.8);
63+
64+
setTimeout(common.mustCall(() => {
65+
ordering.push(3);
66+
}), 1.1);
67+
68+
setTimeout(common.mustCall(() => {
69+
ordering.push(4);
70+
}), 1);
71+
72+
setTimeout(common.mustCall(() => {
73+
const expected = [1, 2, 3, 4];
74+
75+
assert.deepStrictEqual(
76+
ordering,
77+
expected,
78+
`Non-integer delay ordering should be ${expected}, but got ${ordering}`
79+
);
80+
81+
// 2 should always be last of these delays due to ordering guarentees by
82+
// the implementation.
83+
}), 2);
84+
}

0 commit comments

Comments
 (0)