-
Notifications
You must be signed in to change notification settings - Fork 7.3k
timers: fix timeout when added in timer's callback #17203
Conversation
/cc @ilc-opensource, @obastemur, @hankduan, @joyent/node-coreteam. |
var first; | ||
while (first = L.peek(list)) { | ||
var now = Timer.now(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should do it only if the list was changed? I believe this will come with a performance loss for a many-timer case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I added a _addedTimer
property on timers lists to determine when a timer has been added to the list. There are other ways to do that, but this one seemed to be the more lightweight and less intrusive.
4d34651
to
8b00d74
Compare
@indutny @trevnorris I updated the PR with your review comments. I also took the opportunity to slightly refactor the included test:
Please let me know what you think. Thank you! |
LGTM! Thank you! |
@misterdjules I don't think this is a better way in terms of performance. This implementation calls timer.now twice instead of one. |
@obastemur I believe this is a bug fix. If it's comparable in performance then that's fine. |
@obastemur I think it calls it only once... |
@trevnorris I was comparing this to original suggested fix. This is a different implementation since it tries to call timer.now less. IMHO timer.now doesn't cost anything serious. I had made run a perf. test on a range of devices. The most scary one takes 0.001 ms on a mips32r2 board which is still nothing. Yet, this implementation calls at least +1. If there is only one timeout. It calls twice. @indutny it calls twice. First calls before the loop and second a timeout needs to be active to be there right? |
@obastemur it should not invoke |
@indutny I don't think it's very hard to test and see a basic setTimeout case. |
@obastemur could you please share a test case with us? |
|
As you can see |
@indutny My bad. After you insisting it won't. I've checked it again. Seems like the result was coming from the original fix. Somehow https://github.com/misterdjules/node/tree/8b00d748aee6c1cc3f06faea51339b74b4763d8c didn't build on the test machine and results mixed. This one calls at least 25% less (if system wasn't busy) Sorry for the confusion. |
No problem at all, @obastemur . I have seen this too, before @misterdjules fixed the PR. |
a minor detail. I've applied this patch to JXcore to run the tests on our test environment. After this fix, test/simple/test-timers-ordering is no longer a reliable test case. It can be very hard to reproduce on a single run but I'm running the test cases on multiple devices at once and it fails as expected. (bsd, osx, debian on mips..) This doesn't mean the fix is wrong though. As expected, if the system is busy, multiple timeouts may end-up inside the same cycle which this test case fails to measure. |
Credit goes to @misterdjules nodejs/node-v0.x-archive#17203
Thank you @indutny @obastemur and @trevnorris for your feedback! Ran the tests suite and found no regression on Windows and on UNIX. |
@joyent/node-coreteam Added to the 0.10.39 milestone as we may want to release that with the next release. |
@obastemur @trevnorris @indutny As I was reviewing this change one last time before landing it, I thought that its implementation could and should be simpler. This resulted in misterdjules@52c35bd that builds on top of the previous commit in the same PR. If that looks better to you too, I'll squash both commits and land the resulting new commit. Thank you! |
@indutny ping, did you have some time to read my latest comment? |
/cc @joyent/node-coreteam |
@@ -83,10 +83,19 @@ function listOnTimeout() { | |||
debug('timeout callback ' + msecs); | |||
|
|||
var now = Timer.now(); | |||
debug('now: ' + now); | |||
debug('now: %d', now); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
debug('now: %d', now);
Is using %d appropriate? or should we use %s, now can also have double value right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@saquibkhan now
is a non negative 64 bits integer number that is stored as a double if its value is larger than a V8 SMI. Plus the %d
format supports non-integer numbers since it uses the function Number
to convert the parameter corresponding to this format to a number.
LGTM |
52c35bd
to
127f653
Compare
👍 |
When a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect. The reason is that the value that represents the current time is not updated between the time the original callback is called and the time the added timer is processed by timers.listOnTimeout. That leads the logic in timers.listOnTimeout to do an incorrect computation that makes the added timer fire with a timeout of scheduledTimeout + timeSpentInCallback. This change fixes that and make timers scheduled within other timers' callbacks fire as expected. Fixes nodejs#9333 and nodejs#15447.
127f653
to
fe78c98
Compare
@joyent/node-collaborators Squashed, finally landing in v0.10. |
When a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect. The reason is that the value that represents the current time is not updated between the time the original callback is called and the time the added timer is processed by timers.listOnTimeout. That leads the logic in timers.listOnTimeout to do an incorrect computation that makes the added timer fire with a timeout of scheduledTimeout + timeSpentInCallback. This change fixes that and make timers scheduled within other timers' callbacks fire as expected. Fixes nodejs#9333 and nodejs#15447. PR: nodejs#17203 PR-URL: nodejs#17203 Reviewed-By: Fedor Indutny <[email protected]>
@joyent/node-collaborators Landed in d38e865, thank you @obastemur, @indutny and @trevnorris! |
When a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect. The reason is that the value that represents the current time is not updated between the time the original callback is called and the time the added timer is processed by timers.listOnTimeout. That leads the logic in timers.listOnTimeout to do an incorrect computation that makes the added timer fire with a timeout of scheduledTimeout + timeSpentInCallback. This change fixes that and make timers scheduled within other timers' callbacks fire as expected. Fixes: nodejs/node-v0.x-archive#9333 Fixes: nodejs/node-v0.x-archive#15447 PR: nodejs/node-v0.x-archive#17203 PR-URL: nodejs/node-v0.x-archive#17203 Reviewed-By: Fedor Indutny <[email protected]> Conflicts: lib/timers.js test/common.js
Whenever a timer is scheduled within another timer, there are a few known issues that we are fixing: * Whenever the timer being scheduled has the same timeout value as the outer timer, the newly created timer can fire on the same tick of the event loop instead of during the next tick of the event loop * Whenever a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect This commit consists of nodejs/node-v0.x-archive#17203 and nodejs/node-v0.x-archive#25763. Fixes: nodejs/node-v0.x-archive#9333 Fixes: nodejs/node-v0.x-archive#15447 Fixes: nodejs/node-v0.x-archive#25607 Fixes: nodejs#5426 PR-URL: nodejs#3063
Whenever a timer is scheduled within another timer, there are a few known issues that we are fixing: * Whenever the timer being scheduled has the same timeout value as the outer timer, the newly created timer can fire on the same tick of the event loop instead of during the next tick of the event loop * Whenever a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect This commit consists of nodejs/node-v0.x-archive#17203 and nodejs/node-v0.x-archive#25763. Fixes: nodejs/node-v0.x-archive#9333 Fixes: nodejs/node-v0.x-archive#15447 Fixes: nodejs/node-v0.x-archive#25607 Fixes: #5426 PR-URL: #3063
Whenever a timer is scheduled within another timer, there are a few known issues that we are fixing: * Whenever the timer being scheduled has the same timeout value as the outer timer, the newly created timer can fire on the same tick of the event loop instead of during the next tick of the event loop * Whenever a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect This commit consists of nodejs/node-v0.x-archive#17203 and nodejs/node-v0.x-archive#25763. Fixes: nodejs/node-v0.x-archive#9333 Fixes: nodejs/node-v0.x-archive#15447 Fixes: nodejs/node-v0.x-archive#25607 Fixes: #5426 PR-URL: #3063
Whenever a timer is scheduled within another timer, there are a few known issues that we are fixing: * Whenever the timer being scheduled has the same timeout value as the outer timer, the newly created timer can fire on the same tick of the event loop instead of during the next tick of the event loop * Whenever a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect This commit consists of nodejs/node-v0.x-archive#17203 and nodejs/node-v0.x-archive#25763. Fixes: nodejs/node-v0.x-archive#9333 Fixes: nodejs/node-v0.x-archive#15447 Fixes: nodejs/node-v0.x-archive#25607 Fixes: #5426 PR-URL: #3063
When a timer is added in another timer's callback, its underlying timer handle will be started with a timeout that is actually incorrect. The reason is that the value that represents the current time is not updated between the time the original callback is called and the time the added timer is processed by timers.listOnTimeout. That leads the logic in timers.listOnTimeout to do an incorrect computation that makes the added timer fire with a timeout of scheduledTimeout + timeSpentInCallback. This change fixes that and make timers scheduled within other timers' callbacks fire as expected. Fixes nodejs#9333 and nodejs#15447. PR: nodejs#17203 PR-URL: nodejs#17203 Reviewed-By: Fedor Indutny <[email protected]>
When a timer is added in another timer's callback, its underlying timer
handle will be started with a timeout that is actually incorrect.
The reason is that the value that represents the current time is not
updated between the time the original callback is called and the time
the added timer is processed by timers.listOnTimeout. That leads the
logic in timers.listOnTimeout to do an incorrect computation that makes
the added timer fire with a timeout of scheduledTimeout +
timeSpentInCallback.
This change fixes that and make timers scheduled within other timers'
callbacks fire as expected.
Fixes #9333 and #15447.