Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
timers: fix setInterval() assert
Browse files Browse the repository at this point in the history
Test case:

  var t = setInterval(function() {}, 1);
  process.nextTick(t.unref);

Output:

  Assertion failed: (args.Holder()->InternalFieldCount() > 0),
  function Unref, file ../src/handle_wrap.cc, line 78.

setInterval() returns a binding layer object. Make it stop doing that,
wrap the raw process.binding('timer_wrap').Timer object in a Timeout
object.

Fixes #4261.
  • Loading branch information
bnoordhuis committed May 15, 2013
1 parent 1deeab2 commit 22533c0
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 12 deletions.
37 changes: 25 additions & 12 deletions lib/timers.js
Expand Up @@ -222,7 +222,7 @@ exports.setTimeout = function(callback, after) {
exports.clearTimeout = function(timer) {
if (timer && (timer.ontimeout || timer._onTimeout)) {
timer.ontimeout = timer._onTimeout = null;
if (timer instanceof Timer || timer instanceof Timeout) {
if (timer instanceof Timeout) {
timer.close(); // for after === 0
} else {
exports.unenroll(timer);
Expand All @@ -232,39 +232,52 @@ exports.clearTimeout = function(timer) {


exports.setInterval = function(callback, repeat) {
var timer = new Timer();

if (process.domain) timer.domain = process.domain;

repeat *= 1; // coalesce to number or NaN

if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) {
repeat = 1; // schedule on next tick, follows browser behaviour
}

var timer = new Timeout(repeat);
var args = Array.prototype.slice.call(arguments, 2);
timer.ontimeout = function() {
callback.apply(timer, args);
}
timer._onTimeout = wrapper;
timer._repeat = true;

if (process.domain) timer.domain = process.domain;
exports.active(timer);

timer.start(repeat, repeat);
return timer;

function wrapper() {
callback.apply(this, args);
// If callback called clearInterval().
if (timer._repeat === false) return;
// If timer is unref'd (or was - it's permanently removed from the list.)
if (this._handle) {
this._handle.start(repeat, 0);
} else {
timer._idleTimeout = repeat;
exports.active(timer);
}
}
};


exports.clearInterval = function(timer) {
if (timer instanceof Timer) {
timer.ontimeout = null;
timer.close();
if (timer && timer._repeat) {
timer._repeat = false;
clearTimeout(timer);
}
};


var Timeout = function(after) {
this._idleTimeout = after;
this._idlePrev = this;
this._idleNext = this;
this._idleStart = null;
this._onTimeout = null;
this._repeat = false;
};

Timeout.prototype.unref = function() {
Expand Down
7 changes: 7 additions & 0 deletions test/simple/test-timers-unref.js
Expand Up @@ -54,6 +54,13 @@ check_unref = setInterval(function() {
checks += 1;
}, 100);

// Should not assert on args.Holder()->InternalFieldCount() > 0. See #4261.
(function() {
var t = setInterval(function() {}, 1);
process.nextTick(t.unref.bind({}));
process.nextTick(t.unref.bind(t));
})();

process.on('exit', function() {
assert.strictEqual(interval_fired, false, 'Interval should not fire');
assert.strictEqual(timeout_fired, false, 'Timeout should not fire');
Expand Down

6 comments on commit 22533c0

@Fishrock123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes a little backwards-compatibility issue for anything that needs to get a timer's callback.

0.10.6 (and earlier): cb = timer.ontimeout;
vs
0.10.7 +: cb = timer._onTimeout;

Thankfully we can do this: cb = timer._onTimeout || timer.ontimeout;

@TooTallNate
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fishrock123 I mean you're pretty much relying on undocumented properties there... it's bound to break sooner or later.

@Fishrock123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but you have to do what you have to do sometimes. :s

@TooTallNate
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfhbrook
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh? ^^

@TooTallNate
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jesusabdullah hahah, idk what happened there...

Please sign in to comment.