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: https://github.com/nodejs/node/pull/3407 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This commit is contained in:
parent
57f99a9479
commit
3eecdf9f14
@ -119,16 +119,27 @@ function listOnTimeoutNT(list) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const unenroll = exports.unenroll = function(item) {
|
function reuse(item) {
|
||||||
L.remove(item);
|
L.remove(item);
|
||||||
|
|
||||||
var list = lists[item._idleTimeout];
|
var list = lists[item._idleTimeout];
|
||||||
// if empty then stop the watcher
|
// if empty - reuse the watcher
|
||||||
debug('unenroll');
|
|
||||||
if (list && L.isEmpty(list)) {
|
if (list && L.isEmpty(list)) {
|
||||||
|
debug('reuse hit');
|
||||||
|
list.stop();
|
||||||
|
delete lists[item._idleTimeout];
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const unenroll = exports.unenroll = function(item) {
|
||||||
|
var list = reuse(item);
|
||||||
|
if (list) {
|
||||||
debug('unenroll: list empty');
|
debug('unenroll: list empty');
|
||||||
list.close();
|
list.close();
|
||||||
delete lists[item._idleTimeout];
|
|
||||||
}
|
}
|
||||||
// if active is called later, then we want to make sure not to insert again
|
// if active is called later, then we want to make sure not to insert again
|
||||||
item._idleTimeout = -1;
|
item._idleTimeout = -1;
|
||||||
@ -312,12 +323,16 @@ Timeout.prototype.unref = function() {
|
|||||||
if (!this._idleStart) this._idleStart = now;
|
if (!this._idleStart) this._idleStart = now;
|
||||||
var delay = this._idleStart + this._idleTimeout - now;
|
var delay = this._idleStart + this._idleTimeout - now;
|
||||||
if (delay < 0) delay = 0;
|
if (delay < 0) delay = 0;
|
||||||
exports.unenroll(this);
|
|
||||||
|
|
||||||
// Prevent running cb again when unref() is called during the same cb
|
// Prevent running cb again when unref() is called during the same cb
|
||||||
if (this._called && !this._repeat) return;
|
if (this._called && !this._repeat) {
|
||||||
|
exports.unenroll(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._handle = new Timer();
|
var handle = reuse(this);
|
||||||
|
|
||||||
|
this._handle = handle || new Timer();
|
||||||
this._handle.owner = this;
|
this._handle.owner = this;
|
||||||
this._handle[kOnTimeout] = unrefdHandle;
|
this._handle[kOnTimeout] = unrefdHandle;
|
||||||
this._handle.start(delay, 0);
|
this._handle.start(delay, 0);
|
||||||
|
20
test/parallel/test-timers-unrefed-in-beforeexit.js
Normal file
20
test/parallel/test-timers-unrefed-in-beforeexit.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
var once = 0;
|
||||||
|
|
||||||
|
process.on('beforeExit', () => {
|
||||||
|
if (once > 1)
|
||||||
|
throw new RangeError('beforeExit should only have been called once!');
|
||||||
|
|
||||||
|
setTimeout(() => {}, 1).unref();
|
||||||
|
once++;
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('exit', (code) => {
|
||||||
|
if (code !== 0) return;
|
||||||
|
|
||||||
|
assert.strictEqual(once, 1);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user