Skip to content

Commit bb5575a

Browse files
committed
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: #18065 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
1 parent 54fe0a6 commit bb5575a

File tree

5 files changed

+90
-13
lines changed

5 files changed

+90
-13
lines changed

lib/internal/http2/core.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,10 @@ const {
5757
const {
5858
kTimeout,
5959
setUnrefTimeout,
60-
validateTimerDuration
60+
validateTimerDuration,
61+
refreshFnSymbol
6162
} = require('internal/timers');
6263

63-
const { _unrefActive } = require('timers');
64-
6564
const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap');
6665
const { constants } = binding;
6766

@@ -912,7 +911,7 @@ class Http2Session extends EventEmitter {
912911
[kUpdateTimer]() {
913912
if (this.destroyed)
914913
return;
915-
if (this[kTimeout]) _unrefActive(this[kTimeout]);
914+
if (this[kTimeout]) this[kTimeout][refreshFnSymbol]();
916915
}
917916

918917
// Sets the id of the next stream to be created by this Http2Session.
@@ -1478,7 +1477,7 @@ class Http2Stream extends Duplex {
14781477
if (this.destroyed)
14791478
return;
14801479
if (this[kTimeout])
1481-
_unrefActive(this[kTimeout]);
1480+
this[kTimeout][refreshFnSymbol]();
14821481
if (this[kSession])
14831482
this[kSession][kUpdateTimer]();
14841483
}

lib/internal/timers.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ const errors = require('internal/errors');
1919
// Timeout values > TIMEOUT_MAX are set to 1.
2020
const TIMEOUT_MAX = 2 ** 31 - 1;
2121

22+
const refreshFnSymbol = Symbol('refresh()');
23+
const unrefedSymbol = Symbol('unrefed');
24+
2225
module.exports = {
2326
TIMEOUT_MAX,
2427
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
2528
async_id_symbol,
2629
trigger_async_id_symbol,
2730
Timeout,
31+
refreshFnSymbol,
2832
setUnrefTimeout,
2933
validateTimerDuration
3034
};
@@ -39,7 +43,7 @@ function getTimers() {
3943

4044
// Timer constructor function.
4145
// The entire prototype is defined in lib/timers.js
42-
function Timeout(callback, after, args, isRepeat) {
46+
function Timeout(callback, after, args, isRepeat, isUnrefed) {
4347
after *= 1; // coalesce to number or NaN
4448
if (!(after >= 1 && after <= TIMEOUT_MAX)) {
4549
if (after > TIMEOUT_MAX) {
@@ -64,6 +68,8 @@ function Timeout(callback, after, args, isRepeat) {
6468
this._repeat = isRepeat ? after : null;
6569
this._destroyed = false;
6670

71+
this[unrefedSymbol] = isUnrefed;
72+
6773
this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter];
6874
this[trigger_async_id_symbol] = getDefaultTriggerAsyncId();
6975
if (async_hook_fields[kInit] > 0) {
@@ -74,6 +80,19 @@ function Timeout(callback, after, args, isRepeat) {
7480
}
7581
}
7682

83+
Timeout.prototype[refreshFnSymbol] = function refresh() {
84+
if (this._handle) {
85+
// Would be more ideal with uv_timer_again(), however that API does not
86+
// cause libuv's sorted timers data structure (a binary heap at the time
87+
// of writing) to re-sort itself. This causes ordering inconsistencies.
88+
this._handle.stop();
89+
this._handle.start(this._idleTimeout);
90+
} else if (this[unrefedSymbol]) {
91+
getTimers()._unrefActive(this);
92+
} else {
93+
getTimers().active(this);
94+
}
95+
};
7796

7897
function setUnrefTimeout(callback, after, arg1, arg2, arg3) {
7998
// Type checking identical to setTimeout()
@@ -102,7 +121,7 @@ function setUnrefTimeout(callback, after, arg1, arg2, arg3) {
102121
break;
103122
}
104123

105-
const timer = new Timeout(callback, after, args, false);
124+
const timer = new Timeout(callback, after, args, false, true);
106125
getTimers()._unrefActive(timer);
107126

108127
return timer;

lib/net.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
const EventEmitter = require('events');
2525
const stream = require('stream');
26-
const timers = require('timers');
2726
const util = require('util');
2827
const internalUtil = require('internal/util');
2928
const {
@@ -64,7 +63,8 @@ const exceptionWithHostPort = util._exceptionWithHostPort;
6463
const {
6564
kTimeout,
6665
setUnrefTimeout,
67-
validateTimerDuration
66+
validateTimerDuration,
67+
refreshFnSymbol
6868
} = require('internal/timers');
6969

7070
function noop() {}
@@ -291,7 +291,7 @@ util.inherits(Socket, stream.Duplex);
291291
Socket.prototype._unrefTimer = function _unrefTimer() {
292292
for (var s = this; s !== null; s = s._parent) {
293293
if (s[kTimeout])
294-
timers._unrefActive(s[kTimeout]);
294+
s[kTimeout][refreshFnSymbol]();
295295
}
296296
};
297297

lib/timers.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -432,15 +432,15 @@ function setTimeout(callback, after, arg1, arg2, arg3) {
432432
break;
433433
}
434434

435-
const timeout = new Timeout(callback, after, args, false);
435+
const timeout = new Timeout(callback, after, args, false, false);
436436
active(timeout);
437437

438438
return timeout;
439439
}
440440

441441
setTimeout[internalUtil.promisify.custom] = function(after, value) {
442442
const promise = createPromise();
443-
const timeout = new Timeout(promise, after, [value], false);
443+
const timeout = new Timeout(promise, after, [value], false, false);
444444
active(timeout);
445445

446446
return promise;
@@ -523,7 +523,7 @@ exports.setInterval = function(callback, repeat, arg1, arg2, arg3) {
523523
break;
524524
}
525525

526-
const timeout = new Timeout(callback, repeat, args, true);
526+
const timeout = new Timeout(callback, repeat, args, true, false);
527527
active(timeout);
528528

529529
return timeout;

test/parallel/test-timers-refresh.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Flags: --expose-internals
2+
3+
'use strict';
4+
5+
const common = require('../common');
6+
7+
const { strictEqual } = require('assert');
8+
const { setUnrefTimeout, refreshFnSymbol } = require('internal/timers');
9+
10+
// Schedule the unrefed cases first so that the later case keeps the event loop
11+
// active.
12+
13+
// Every case in this test relies on implicit sorting within either Node's or
14+
// libuv's timers storage data structures.
15+
16+
// unref()'d timer
17+
{
18+
let called = false;
19+
const timer = setTimeout(common.mustCall(() => {
20+
called = true;
21+
}), 1);
22+
timer.unref();
23+
24+
// This relies on implicit timers handle sorting withing libuv.
25+
26+
setTimeout(common.mustCall(() => {
27+
strictEqual(called, false, 'unref()\'d timer returned before check');
28+
}), 1);
29+
30+
timer[refreshFnSymbol]();
31+
}
32+
33+
// unref pooled timer
34+
{
35+
let called = false;
36+
const timer = setUnrefTimeout(common.mustCall(() => {
37+
called = true;
38+
}), 1);
39+
40+
setUnrefTimeout(common.mustCall(() => {
41+
strictEqual(called, false, 'unref pooled timer returned before check');
42+
}), 1);
43+
44+
timer[refreshFnSymbol]();
45+
}
46+
47+
// regular timer
48+
{
49+
let called = false;
50+
const timer = setTimeout(common.mustCall(() => {
51+
called = true;
52+
}), 1);
53+
54+
setTimeout(common.mustCall(() => {
55+
strictEqual(called, false, 'pooled timer returned before check');
56+
}), 1);
57+
58+
timer[refreshFnSymbol]();
59+
}

0 commit comments

Comments
 (0)