Skip to content

Commit 23a56e0

Browse files
committed
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: #20555 Fixes: #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>
1 parent 6f6f7f7 commit 23a56e0

13 files changed

+242
-329
lines changed

benchmark/timers/timers-cancel-unpooled.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ const assert = require('assert');
44

55
const bench = common.createBenchmark(main, {
66
n: [1e6],
7+
direction: ['start', 'end']
78
});
89

9-
function main({ n }) {
10+
function main({ n, direction }) {
1011

1112
const timersList = [];
1213
for (var i = 0; i < n; i++) {
1314
timersList.push(setTimeout(cb, i + 1));
1415
}
1516

17+
var j;
1618
bench.start();
17-
for (var j = 0; j < n + 1; j++) {
18-
clearTimeout(timersList[j]);
19+
if (direction === 'start') {
20+
for (j = 0; j < n; j++) {
21+
clearTimeout(timersList[j]);
22+
}
23+
} else {
24+
for (j = n - 1; j >= 0; j--) {
25+
clearTimeout(timersList[j]);
26+
}
1927
}
2028
bench.end(n);
2129
}

benchmark/timers/timers-insert-unpooled.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ const assert = require('assert');
44

55
const bench = common.createBenchmark(main, {
66
n: [1e6],
7+
direction: ['start', 'end']
78
});
89

9-
function main({ n }) {
10+
function main({ direction, n }) {
1011
const timersList = [];
1112

13+
var i;
1214
bench.start();
13-
for (var i = 0; i < n; i++) {
14-
timersList.push(setTimeout(cb, i + 1));
15+
if (direction === 'start') {
16+
for (i = 1; i <= n; i++) {
17+
timersList.push(setTimeout(cb, i));
18+
}
19+
} else {
20+
for (i = n; i > 0; i--) {
21+
timersList.push(setTimeout(cb, i));
22+
}
1523
}
1624
bench.end(n);
1725

18-
for (var j = 0; j < n + 1; j++) {
26+
for (var j = 0; j < n; j++) {
1927
clearTimeout(timersList[j]);
2028
}
2129
}

lib/internal/timers.js

+8-14
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ const {
1919
// Timeout values > TIMEOUT_MAX are set to 1.
2020
const TIMEOUT_MAX = 2 ** 31 - 1;
2121

22-
const unrefedSymbol = Symbol('unrefed');
22+
const kRefed = Symbol('refed');
2323

2424
module.exports = {
2525
TIMEOUT_MAX,
2626
kTimeout: Symbol('timeout'), // For hiding Timeouts on other internals.
2727
async_id_symbol,
2828
trigger_async_id_symbol,
2929
Timeout,
30+
kRefed,
3031
initAsyncResource,
3132
setUnrefTimeout,
3233
validateTimerDuration
@@ -50,7 +51,7 @@ function initAsyncResource(resource, type) {
5051

5152
// Timer constructor function.
5253
// The entire prototype is defined in lib/timers.js
53-
function Timeout(callback, after, args, isRepeat, isUnrefed) {
54+
function Timeout(callback, after, args, isRepeat) {
5455
after *= 1; // coalesce to number or NaN
5556
if (!(after >= 1 && after <= TIMEOUT_MAX)) {
5657
if (after > TIMEOUT_MAX) {
@@ -62,7 +63,6 @@ function Timeout(callback, after, args, isRepeat, isUnrefed) {
6263
after = 1; // schedule on next tick, follows browser behavior
6364
}
6465

65-
this._called = false;
6666
this._idleTimeout = after;
6767
this._idlePrev = this;
6868
this._idleNext = this;
@@ -75,22 +75,16 @@ function Timeout(callback, after, args, isRepeat, isUnrefed) {
7575
this._repeat = isRepeat ? after : null;
7676
this._destroyed = false;
7777

78-
this[unrefedSymbol] = isUnrefed;
78+
this[kRefed] = null;
7979

8080
initAsyncResource(this, 'Timeout');
8181
}
8282

8383
Timeout.prototype.refresh = function() {
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.start(this._idleTimeout);
89-
} else if (this[unrefedSymbol]) {
90-
getTimers()._unrefActive(this);
91-
} else {
84+
if (this[kRefed])
9285
getTimers().active(this);
93-
}
86+
else
87+
getTimers()._unrefActive(this);
9488

9589
return this;
9690
};
@@ -122,7 +116,7 @@ function setUnrefTimeout(callback, after, arg1, arg2, arg3) {
122116
break;
123117
}
124118

125-
const timer = new Timeout(callback, after, args, false, true);
119+
const timer = new Timeout(callback, after, args, false);
126120
getTimers()._unrefActive(timer);
127121

128122
return timer;

0 commit comments

Comments
 (0)