timers: add internal [@@ refresh()] function

Hidden via a symbol because I'm unsure exactly what the API should look
like in the end.

Removes the need to use _unrefActive for efficiently refreshing
timeouts.
It still uses it under the hood but that could be replaced with
insert() directly if it were in the same file.

PR-URL: https://github.com/nodejs/node/pull/18065
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
This commit is contained in:
Jeremiah Senkpiel 2018-01-09 13:12:06 -05:00
Родитель 54fe0a6cbb
Коммит bb5575aa75
5 изменённых файлов: 90 добавлений и 13 удалений

Просмотреть файл

@ -57,11 +57,10 @@ const {
const {
kTimeout,
setUnrefTimeout,
validateTimerDuration
validateTimerDuration,
refreshFnSymbol
} = require('internal/timers');
const { _unrefActive } = require('timers');
const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap');
const { constants } = binding;
@ -912,7 +911,7 @@ class Http2Session extends EventEmitter {
[kUpdateTimer]() {
if (this.destroyed)
return;
if (this[kTimeout]) _unrefActive(this[kTimeout]);
if (this[kTimeout]) this[kTimeout][refreshFnSymbol]();
}
// Sets the id of the next stream to be created by this Http2Session.
@ -1478,7 +1477,7 @@ class Http2Stream extends Duplex {
if (this.destroyed)
return;
if (this[kTimeout])
_unrefActive(this[kTimeout]);
this[kTimeout][refreshFnSymbol]();
if (this[kSession])
this[kSession][kUpdateTimer]();
}

Просмотреть файл

@ -19,12 +19,16 @@ const errors = require('internal/errors');
// Timeout values > TIMEOUT_MAX are set to 1.
const TIMEOUT_MAX = 2 ** 31 - 1;
const refreshFnSymbol = Symbol('refresh()');
const unrefedSymbol = Symbol('unrefed');
module.exports = {
TIMEOUT_MAX,
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
async_id_symbol,
trigger_async_id_symbol,
Timeout,
refreshFnSymbol,
setUnrefTimeout,
validateTimerDuration
};
@ -39,7 +43,7 @@ function getTimers() {
// Timer constructor function.
// The entire prototype is defined in lib/timers.js
function Timeout(callback, after, args, isRepeat) {
function Timeout(callback, after, args, isRepeat, isUnrefed) {
after *= 1; // coalesce to number or NaN
if (!(after >= 1 && after <= TIMEOUT_MAX)) {
if (after > TIMEOUT_MAX) {
@ -64,6 +68,8 @@ function Timeout(callback, after, args, isRepeat) {
this._repeat = isRepeat ? after : null;
this._destroyed = false;
this[unrefedSymbol] = isUnrefed;
this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter];
this[trigger_async_id_symbol] = getDefaultTriggerAsyncId();
if (async_hook_fields[kInit] > 0) {
@ -74,6 +80,19 @@ function Timeout(callback, after, args, isRepeat) {
}
}
Timeout.prototype[refreshFnSymbol] = function refresh() {
if (this._handle) {
// Would be more ideal with uv_timer_again(), however that API does not
// cause libuv's sorted timers data structure (a binary heap at the time
// of writing) to re-sort itself. This causes ordering inconsistencies.
this._handle.stop();
this._handle.start(this._idleTimeout);
} else if (this[unrefedSymbol]) {
getTimers()._unrefActive(this);
} else {
getTimers().active(this);
}
};
function setUnrefTimeout(callback, after, arg1, arg2, arg3) {
// Type checking identical to setTimeout()
@ -102,7 +121,7 @@ function setUnrefTimeout(callback, after, arg1, arg2, arg3) {
break;
}
const timer = new Timeout(callback, after, args, false);
const timer = new Timeout(callback, after, args, false, true);
getTimers()._unrefActive(timer);
return timer;

Просмотреть файл

@ -23,7 +23,6 @@
const EventEmitter = require('events');
const stream = require('stream');
const timers = require('timers');
const util = require('util');
const internalUtil = require('internal/util');
const {
@ -64,7 +63,8 @@ const exceptionWithHostPort = util._exceptionWithHostPort;
const {
kTimeout,
setUnrefTimeout,
validateTimerDuration
validateTimerDuration,
refreshFnSymbol
} = require('internal/timers');
function noop() {}
@ -291,7 +291,7 @@ util.inherits(Socket, stream.Duplex);
Socket.prototype._unrefTimer = function _unrefTimer() {
for (var s = this; s !== null; s = s._parent) {
if (s[kTimeout])
timers._unrefActive(s[kTimeout]);
s[kTimeout][refreshFnSymbol]();
}
};

Просмотреть файл

@ -432,7 +432,7 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
break;
}
const timeout = new Timeout(callback, after, args, false);
const timeout = new Timeout(callback, after, args, false, false);
active(timeout);
return timeout;
@ -440,7 +440,7 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
setTimeout[internalUtil.promisify.custom] = function(after, value) {
const promise = createPromise();
const timeout = new Timeout(promise, after, [value], false);
const timeout = new Timeout(promise, after, [value], false, false);
active(timeout);
return promise;
@ -523,7 +523,7 @@ exports.setInterval = function(callback, repeat, arg1, arg2, arg3) {
break;
}
const timeout = new Timeout(callback, repeat, args, true);
const timeout = new Timeout(callback, repeat, args, true, false);
active(timeout);
return timeout;

Просмотреть файл

@ -0,0 +1,59 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
const { strictEqual } = require('assert');
const { setUnrefTimeout, refreshFnSymbol } = require('internal/timers');
// Schedule the unrefed cases first so that the later case keeps the event loop
// active.
// Every case in this test relies on implicit sorting within either Node's or
// libuv's timers storage data structures.
// unref()'d timer
{
let called = false;
const timer = setTimeout(common.mustCall(() => {
called = true;
}), 1);
timer.unref();
// This relies on implicit timers handle sorting withing libuv.
setTimeout(common.mustCall(() => {
strictEqual(called, false, 'unref()\'d timer returned before check');
}), 1);
timer[refreshFnSymbol]();
}
// unref pooled timer
{
let called = false;
const timer = setUnrefTimeout(common.mustCall(() => {
called = true;
}), 1);
setUnrefTimeout(common.mustCall(() => {
strictEqual(called, false, 'unref pooled timer returned before check');
}), 1);
timer[refreshFnSymbol]();
}
// regular timer
{
let called = false;
const timer = setTimeout(common.mustCall(() => {
called = true;
}), 1);
setTimeout(common.mustCall(() => {
strictEqual(called, false, 'pooled timer returned before check');
}), 1);
timer[refreshFnSymbol]();
}