Skip to content

Commit 8d78d68

Browse files
indutnyjasnell
authored andcommitted
timers: reuse timer in setTimeout().unref()
Instead of creating new timer - reuse the timer from the freelist. This won't make the freelist timer active for the duration of `uv_close()`, and will let the event-loop exit properly. Fix: #1264 PR-URL: #3407 Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
1 parent 7cad182 commit 8d78d68

File tree

2 files changed

+42
-7
lines changed

2 files changed

+42
-7
lines changed

lib/timers.js

+22-7
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,27 @@ function listOnTimeoutNT(list) {
119119
}
120120

121121

122-
const unenroll = exports.unenroll = function(item) {
122+
function reuse(item) {
123123
L.remove(item);
124124

125125
var list = lists[item._idleTimeout];
126-
// if empty then stop the watcher
127-
debug('unenroll');
126+
// if empty - reuse the watcher
128127
if (list && L.isEmpty(list)) {
128+
debug('reuse hit');
129+
list.stop();
130+
delete lists[item._idleTimeout];
131+
return list;
132+
}
133+
134+
return null;
135+
}
136+
137+
138+
const unenroll = exports.unenroll = function(item) {
139+
var list = reuse(item);
140+
if (list) {
129141
debug('unenroll: list empty');
130142
list.close();
131-
delete lists[item._idleTimeout];
132143
}
133144
// if active is called later, then we want to make sure not to insert again
134145
item._idleTimeout = -1;
@@ -312,12 +323,16 @@ Timeout.prototype.unref = function() {
312323
if (!this._idleStart) this._idleStart = now;
313324
var delay = this._idleStart + this._idleTimeout - now;
314325
if (delay < 0) delay = 0;
315-
exports.unenroll(this);
316326

317327
// Prevent running cb again when unref() is called during the same cb
318-
if (this._called && !this._repeat) return;
328+
if (this._called && !this._repeat) {
329+
exports.unenroll(this);
330+
return;
331+
}
332+
333+
var handle = reuse(this);
319334

320-
this._handle = new Timer();
335+
this._handle = handle || new Timer();
321336
this._handle.owner = this;
322337
this._handle[kOnTimeout] = unrefdHandle;
323338
this._handle.start(delay, 0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
6+
var once = 0;
7+
8+
process.on('beforeExit', () => {
9+
if (once > 1)
10+
throw new RangeError('beforeExit should only have been called once!');
11+
12+
setTimeout(() => {}, 1).unref();
13+
once++;
14+
});
15+
16+
process.on('exit', (code) => {
17+
if (code !== 0) return;
18+
19+
assert.strictEqual(once, 1);
20+
});

0 commit comments

Comments
 (0)