Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

timers: support BigInt in timers API #22086

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/api/timers.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ added: v0.0.1
-->

* `callback` {Function} The function to call when the timer elapses.
* `delay` {number} The number of milliseconds to wait before calling the
* `delay` {number|bigint} The number of milliseconds to wait before calling the
`callback`.
* `...args` {any} Optional arguments to pass when the `callback` is called.
* Returns: {Timeout} for use with [`clearInterval()`][]
Expand All @@ -196,7 +196,7 @@ added: v0.0.1
-->

* `callback` {Function} The function to call when the timer elapses.
* `delay` {number} The number of milliseconds to wait before calling the
* `delay` {number|bigint} The number of milliseconds to wait before calling the
`callback`.
* `...args` {any} Optional arguments to pass when the `callback` is called.
* Returns: {Timeout} for use with [`clearTimeout()`][]
Expand Down
21 changes: 16 additions & 5 deletions lib/internal/timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ function initAsyncResource(resource, type) {
// Timer constructor function.
// The entire prototype is defined in lib/timers.js
function Timeout(callback, after, args, isRepeat) {
after *= 1; // coalesce to number or NaN
if (typeof after !== 'bigint') { // eslint-disable-line valid-typeof
after *= 1; // coalesce to number or NaN
}

if (!(after >= 1 && after <= TIMEOUT_MAX)) {
if (after > TIMEOUT_MAX) {
process.emitWarning(`${after} does not fit into` +
Expand All @@ -65,6 +68,11 @@ function Timeout(callback, after, args, isRepeat) {
after = 1; // schedule on next tick, follows browser behavior
}

// at this point after is either a number or bigint and in both
// cases its value is in the limit of signed int so it is safe to
// convert after to number
after = Number(after);

this._idleTimeout = after;
this._idlePrev = this;
this._idleNext = this;
Expand Down Expand Up @@ -126,11 +134,14 @@ function setUnrefTimeout(callback, after, arg1, arg2, arg3) {

// Type checking used by timers.enroll() and Socket#setTimeout()
function validateTimerDuration(msecs) {
if (typeof msecs !== 'number') {
throw new ERR_INVALID_ARG_TYPE('msecs', 'number', msecs);
if ((typeof msecs !== 'number') &&
(typeof msecs !== 'bigint')) { // eslint-disable-line valid-typeof
throw new ERR_INVALID_ARG_TYPE('msecs', ['number', 'bigint'], msecs);
}

if (msecs < 0 || !isFinite(msecs)) {
// ensure that msecs is non-negative and finite
// note: bigint is always a finite (typeof Infinity and NaN is 'number')
if (msecs < 0 || (typeof msecs === 'number' && !isFinite(msecs))) {
throw new ERR_OUT_OF_RANGE('msecs', 'a non-negative finite number', msecs);
}

Expand All @@ -142,5 +153,5 @@ function validateTimerDuration(msecs) {
return TIMEOUT_MAX;
}

return msecs;
return Number(msecs);
}
2 changes: 1 addition & 1 deletion test/parallel/test-http2-timeouts.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ server.on('stream', common.mustCall((stream) => {
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message:
'The "msecs" argument must be of type number. Received type string'
'The "msecs" argument must be one of type number or bigint. Received type string'
}
);
common.expectsError(
Expand Down
42 changes: 22 additions & 20 deletions test/parallel/test-timers-max-duration-warning.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const assert = require('assert');
const timers = require('timers');

const OVERFLOW = Math.pow(2, 31); // TIMEOUT_MAX is 2^31-1
const TEST_MATRIX = [OVERFLOW, BigInt(OVERFLOW)];

function timerNotCanceled() {
assert.fail('Timer should be canceled');
Expand All @@ -19,24 +20,25 @@ process.on('warning', common.mustCall((warning) => {
assert.strictEqual(lines[0], `${OVERFLOW} does not fit into a 32-bit signed` +
' integer.');
assert.strictEqual(lines.length, 2);
}, 5));


{
const timeout = setTimeout(timerNotCanceled, OVERFLOW);
clearTimeout(timeout);
}

{
const interval = setInterval(timerNotCanceled, OVERFLOW);
clearInterval(interval);
}

{
const timer = {
_onTimeout: timerNotCanceled
};
timers.enroll(timer, OVERFLOW);
timers.active(timer);
timers.unenroll(timer);
}, 8));

for (let i = 0; i < TEST_MATRIX.length; i++) {
{
const timeout = setTimeout(timerNotCanceled, TEST_MATRIX[i]);
clearTimeout(timeout);
}

{
const interval = setInterval(timerNotCanceled, TEST_MATRIX[i]);
clearInterval(interval);
}

{
const timer = {
_onTimeout: timerNotCanceled
};
timers.enroll(timer, TEST_MATRIX[i]);
timers.active(timer);
timers.unenroll(timer);
}
}
10 changes: 9 additions & 1 deletion test/parallel/test-timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,15 @@ const inputs = [
1,
1.0,
2147483648, // browser behavior: timeouts > 2^31-1 run on next tick
12345678901234 // ditto
12345678901234, // ditto
BigInt(2 ** 72), // ditto
BigInt(0),
BigInt(-10),
BigInt(-1),
BigInt(true),
BigInt(false),
BigInt(1),
BigInt('')
];

const timeouts = [];
Expand Down