Repository navigation
clearTimeout with invalid handle cause program runs longer #7722
Description
Activity
- addedtimersIssues and PRs related to timers, setImmediate(), setInterval(), and setTimeout().Issues and PRs related to timers, setImmediate(), setInterval(), and setTimeout().
on Jul 14, 2016 - addedduplicateIssues and PRs that are duplicates of other issues or PRs.Issues and PRs that are duplicates of other issues or PRs.
on Jul 14, 2016 This is not a duplicate of #5426. Applying the fix in #3063 on top of current node's master still allows to reproduce the problem described in this issue.
The problem that this issue describes is that, in the following code:
var handle = setTimeout(function(){ clearTimeout(handle); handle = setTimeout( function() { }, 10000 ); setTimeout( function() { clearTimeout( handle ); }, 500 ); }, 10000);
when the callback of the first timer with a 10000 delay fires, this is (some of) what happens:
- a reference to the timers list that represents all timers with a 10000ms delay is grabbed. This list contains one "userland timer".
- that list is cleared by
clearTimeout(handle). The list of timers with a 10000ms delay is removed from the list of lists of timers because at that time there was only 1 timer in that list. - A new timer instance with a 10000ms delay is added, so a new list of timers is created. Note that the "reference to the timers list that represents all timers with a 10000ms delay" grabbed at point 1 still references the old empty list.
- The callback of the first 10000ms delay timer completes and
listOnTimeoutdetermines that the list of timers referenced bylist, the "old timers list" for timers with a 10000 delay, is empty. But instead of removing that old list from the list of lists of timers, it removes the new list that contains the newly scheduled 10000ms delayed timer. - When the callback of the 500ms delayed timer fires, it clears the most recent 10000ms timer instance, which fails to find any list of timers with 10000ms delay and thus doesn't close the handle of that new timer, which keeps the libuv event loop open until the
kOnTimeoutcallback is called.
Here's a quick fix that I put together to demonstrate the problem. This is not a definite fix, it's mentioned here just to illustrate the problem. It is likely that a better fix can be written:
diff --git a/lib/timers.js b/lib/timers.js index 7379cfe..b7f2259 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -212,9 +212,11 @@ function listOnTimeout() { assert(L.isEmpty(list)); this.close(); if (list._unrefed === true) { - delete unrefedLists[msecs]; + if (list === unrefedLists[msecs]) + delete unrefedLists[msecs]; } else { - delete refedLists[msecs]; + if (list === refedLists[msecs]) + delete refedLists[msecs]; } }
With that fix, the problem cannot be reproduced and all tests pass. I'll try to dig a bit deeper in the next few days to come up with a better fix and tests, but if anyone is willing to take a shot at it, I'd be glad to help them too.
From what I described above, it seems this issue should be kept open, thus I'm reopening it.
- addedconfirmed-bugIssues and PRs for confirmed bugs.Issues and PRs for confirmed bugs.and removedduplicateIssues and PRs that are duplicates of other issues or PRs.Issues and PRs that are duplicates of other issues or PRs.
on Jul 14, 2016 Oh nice catch, I think I see what you are saying. Yes, that would be a bug in my refactoring.
The patch could probably be simplified a bit also.
11 remaining items
@Fishrock123 The problem also affects the master branch, shouldn't we keep the
masterlabel set, or is it meant to be used for other purposes?The fix in 7d75338 should be able to make its way to v6.x and v4.x releases soon. It's unlikely it will make it to any v0.12 and v0.10 release unless there's enough evidence that a significant number of users are impacted by it on these release lines.
- added a commit that references this issue
on Aug 10, 2016 - added a commit that references this issue
on Jul 27, 2026
This program cost 20 seconds, but 10.5 seconds was expected:
But all below codes work as expected:
Does clearTimeout with a invalid handle causes this problem?