0
0
mirror of https://github.com/nodejs/node.git synced 2024-11-30 07:27:22 +01:00
nodejs/lib/internal/timers.js
Anatoli Papirovski 23a56e0c28
timers: use only a single TimerWrap instance
Hang all timer lists off a single TimerWrap and use the PriorityQueue
to manage expiration priorities. This makes the Timers code clearer,
consumes significantly less resources and improves performance.

PR-URL: https://github.com/nodejs/node/pull/20555
Fixes: https://github.com/nodejs/node/issues/16105
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
2018-05-22 23:26:12 +04:00

145 lines
3.6 KiB
JavaScript

'use strict';
const {
getDefaultTriggerAsyncId,
newAsyncId,
initHooksExist,
emitInit
} = require('internal/async_hooks');
// Symbols for storing async id state.
const async_id_symbol = Symbol('asyncId');
const trigger_async_id_symbol = Symbol('triggerId');
const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK,
ERR_OUT_OF_RANGE
} = require('internal/errors').codes;
// Timeout values > TIMEOUT_MAX are set to 1.
const TIMEOUT_MAX = 2 ** 31 - 1;
const kRefed = Symbol('refed');
module.exports = {
TIMEOUT_MAX,
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
async_id_symbol,
trigger_async_id_symbol,
Timeout,
kRefed,
initAsyncResource,
setUnrefTimeout,
validateTimerDuration
};
var timers;
function getTimers() {
if (timers === undefined) {
timers = require('timers');
}
return timers;
}
function initAsyncResource(resource, type) {
const asyncId = resource[async_id_symbol] = newAsyncId();
const triggerAsyncId =
resource[trigger_async_id_symbol] = getDefaultTriggerAsyncId();
if (initHooksExist())
emitInit(asyncId, type, triggerAsyncId, resource);
}
// 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 (!(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
}
this._idleTimeout = after;
this._idlePrev = this;
this._idleNext = this;
this._idleStart = null;
// this must be set to null first to avoid function tracking
// on the hidden class, revisit in V8 versions after 6.2
this._onTimeout = null;
this._onTimeout = callback;
this._timerArgs = args;
this._repeat = isRepeat ? after : null;
this._destroyed = false;
this[kRefed] = null;
initAsyncResource(this, 'Timeout');
}
Timeout.prototype.refresh = function() {
if (this[kRefed])
getTimers().active(this);
else
getTimers()._unrefActive(this);
return this;
};
function setUnrefTimeout(callback, after, arg1, arg2, arg3) {
// Type checking identical to setTimeout()
if (typeof callback !== 'function') {
throw new ERR_INVALID_CALLBACK();
}
let i, args;
switch (arguments.length) {
// fast cases
case 1:
case 2:
break;
case 3:
args = [arg1];
break;
case 4:
args = [arg1, arg2];
break;
default:
args = [arg1, arg2, arg3];
for (i = 5; i < arguments.length; i++) {
// extend array dynamically, makes .apply run much faster in v6.0.0
args[i - 2] = arguments[i];
}
break;
}
const timer = new Timeout(callback, after, args, false);
getTimers()._unrefActive(timer);
return timer;
}
// 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 (msecs < 0 || !isFinite(msecs)) {
throw new ERR_OUT_OF_RANGE('msecs', 'a non-negative finite number', 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');
return TIMEOUT_MAX;
}
return msecs;
}