From 289e325d529e5162e061db16adeb8c3eb1bfb1c8 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Tue, 24 May 2016 16:39:54 -0400 Subject: [PATCH] timers: warn on overflowed timeout duration Cherry-pick from ayo Ayo commit log: > Previously there wasn't any clear indicator when you hit the overflow > other than possibly unexpected behavior, and I think emitting a warning > may be appropriate. > PR-URL: https://github.com/ayojs/ayo/pull/71 > Reviewed-By: Scott Trinh > Reviewed-By: Alexey Orlenko > Reviewed-By: Stephen Belanger > Reviewed-By: Anna Henningsen > Reviewed-By: Benjamin Gruenbaum --- lib/timers.js | 21 +++++++++- .../test-timers-max-duration-warning.js | 40 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-timers-max-duration-warning.js diff --git a/lib/timers.js b/lib/timers.js index 1c41d5d0f3a44f..5727de159a4643 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -398,6 +398,9 @@ exports.enroll = function(item, msecs) { // Ensure that msecs fits into signed int32 if (msecs > TIMEOUT_MAX) { + process.emitWarning(`${msecs} does not fit into a 32-bit signed integer.` + + `\nTimer duration was truncated to ${TIMEOUT_MAX}.`, + 'TimeoutOverflowWarning'); msecs = TIMEOUT_MAX; } @@ -442,8 +445,15 @@ exports.setTimeout = setTimeout; function createSingleTimeout(callback, after, args) { after *= 1; // coalesce to number or NaN - if (!(after >= 1 && after <= TIMEOUT_MAX)) + if (!(after >= 1 && after <= TIMEOUT_MAX)) { + if (after > TIMEOUT_MAX) { + process.emitWarning(`${after} does not fit into` + + ' a 32-bit signed integer.' + + '\nTimeout duration was set to 1.', + 'TimeoutOverflowWarning'); + } after = 1; // schedule on next tick, follows browser behavior + } var timer = new Timeout(after, callback, args); if (process.domain) @@ -531,8 +541,15 @@ exports.setInterval = function(callback, repeat, arg1, arg2, arg3) { function createRepeatTimeout(callback, repeat, args) { repeat *= 1; // coalesce to number or NaN - if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) + if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) { + if (repeat > TIMEOUT_MAX) { + process.emitWarning(`${repeat} does not fit into` + + ' a 32-bit signed integer.' + + '\nInterval duration was set to 1.', + 'TimeoutOverflowWarning'); + } repeat = 1; // schedule on next tick, follows browser behavior + } var timer = new Timeout(repeat, callback, args); timer._repeat = repeat; diff --git a/test/parallel/test-timers-max-duration-warning.js b/test/parallel/test-timers-max-duration-warning.js new file mode 100644 index 00000000000000..b352b745c504de --- /dev/null +++ b/test/parallel/test-timers-max-duration-warning.js @@ -0,0 +1,40 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const timers = require('timers'); + +const OVERFLOW = Math.pow(2, 31); // TIMEOUT_MAX is 2^31-1 + +function timerNotCanceled() { + common.fail('Timer should be canceled'); +} + +process.on('warning', common.mustCall((warning) => { + const lines = warning.message.split('\n'); + + assert.strictEqual(warning.name, 'TimeoutOverflowWarning'); + assert.strictEqual(lines[0], `${OVERFLOW} does not fit into a 32-bit signed` + + ' integer.'); + assert.strictEqual(lines.length, 2); +}, 3)); + + +{ + 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); +}