Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.2k
timers: fix processing of nested same delay timers#3063
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Conversation
whitlockjc commented Sep 25, 2015 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
test/common.js Outdated
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.
Why add this for just one test?
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 question. :) I'm glad to remove it until others need something similar. My only worry is removing it means people will probably write their own stuff not knowing that we have a common need for a busy loop. Your call.
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.
Yes, if you could move it to a function in the bottom of the test file that would be better for 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.
Edit, maybe don't pending #2232
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.
Just let me know.
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.
yeah this should go into the test itself imo
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.
If this PR is still up for consideration, I'll update this. I'm not sure where it stands so I defer to your judgement.
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.
Seems like there is an actual bug here, so yeas, please update it. :)
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.
Will do. Thanks for your help.
mscdex commented Sep 25, 2015
/cc @bnoordhuis@trevnorris ? |
Fishrock123 commented Sep 26, 2015
Does #2232 need to land first, or does this incorporate that? Can we just port the test maybe? We should probably preserve julien's commit though. |
whitlockjc commented Sep 28, 2015
@misterdjules should comment. Last I talked to Julien I was asked to port this over but if there is already work to port things and this is superfluous, no big deal to me. Just let me know how I can help. |
test/timers/test-timers-nested.js Outdated
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.
I'd suggest adding a comment that this assert is done within a nextTick callback so that it runs after A completed, but before the next turn of libuv's event loop. Otherwise it's not clear for anyone but who wrote the code why it's done this way.
misterdjules commented Oct 1, 2015
This PR fixes one issue with #2232 by basically refactoring it, so most of the changes in #2232 are superseded by this PR. However, we need |
d7d5236 to d848924Comparewhitlockjc commented Oct 1, 2015
I have made the changes @misterdjules requested and the PR is updated with those changes. Let me know if there is more to be done and thanks for your help. |
misterdjules commented Nov 27, 2015
I don't see these changes in this PR, do you have some time to update this PR with these changes? Then we'll close github.com//pull/2232 in favor of this one. |
whitlockjc commented Nov 27, 2015
Sure thing. I am mobile right now but when I get back to the laptop, I will update this PR with those changes and resolve conflicts. |
whitlockjc commented Nov 30, 2015
I'll take care of this today. I didn't manage to get laptop time over the holiday. |
lib/timers.js Outdated
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.
I'm not sure this is actually a bug. Shouldn't the timer be scheduled in setImmediate() if you want to guarantee this?
Also, how does this not only apply to 0ms timeout timers?
setTimeout(function foo(){setTimeout(function bar(){}, 500) }, 500);
In this example, the 2nd timeout should be caught by https://github.com/nodejs/node/blob/master/lib/timers.js#L68
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.
I'm not sure this is actually a bug.
It is indeed a grey area. However, this is how node v4.x and later behaves now. It is also how node v0.10.x and v0.12.x used to behave before nodejs/node-v0.x-archive@d38e865, which is a regression that is fixed by nodejs/node-v0.x-archive#25763.
Shouldn't the timer be scheduled in setImmediate() if you want to guarantee this?
Scheduling the timer with setImmediate would basically make it not a timer anymore, as immediates are scheduled and ran in a completely different way.
Also, how does this not only apply to 0ms timeout timers?
setTimeout(function foo(){setTimeout(function bar(){}, 500) }, 500);
The comment in this PR should be clearer and mention that it happens with non-0 delay timers if the callback of the outer timer blocks the JS thread for a duration that is greater or equal to the timer delay. So the snippet should be:
setTimeout(function foo(){setTimeout(function bar(){}, 500); codeThatBlocksFor500ms()}, 500); In this example, the 2nd timeout should be caught by https://github.com/nodejs/node/blob/master/lib/timers.js#L68
Except that then we'd hit the bug fixed by nodejs/node-v0.x-archive@d38e865.
That bug does not have a big impact for timers with short delays, but it can be a problem for timers with large delays that are scheduled before code that blocks for a long time, as it can make the timer fire with a delay of originalDelay + timeSpentBlockingInOutterTimer.
Fishrock123 commented Nov 30, 2015
Sorry, after figuring out how timers work fully for #4007 I'm not convinced this is the correct behavior, or necessary? |
whitlockjc commented Nov 30, 2015
Have we ran |
Fishrock123 commented Nov 30, 2015
I think the included test isn't actually valid, so it doesn't really matter if it is flaky or not. :s |
whitlockjc commented Nov 30, 2015
It seemed to work with the original sources. ;) I guess that doesn't matter now. |
Fishrock123 commented Nov 30, 2015
@whitlockjc I think you may be misunderstanding. It may work but that doesn't mean it is correct. :/ |
Fishrock123 commented Nov 30, 2015
Is this PR just trying to make sure the callback is always Async per-se? |
whitlockjc commented Nov 30, 2015
Seeing as I'm not always right and I love to learn, I'd love to hear more about why it's not correct. At the time the issue was reported, the test was capable of reproducing the reported issue and when the code to fix the issue was in place, the test passed showing the issue had been addressed. Instead of just saying it's not right, why not provide a little context? Worst case someone learns something. |
Fishrock123 commented Nov 30, 2015
Sorry, I put some reasoning into #3063 (comment) -- basically I'm not sure we guarantee a callback will actually be called at least next tick, and timers by nature timeout as soon as possible. |
Fishrock123 commented Nov 30, 2015
I suppose users may expect it to be async. Would it also be possible to do this by always calling |
misterdjules commented Dec 1, 2015
It is at least how it behaves now. Having nested timers that expire when their outer timer's callback is done fire asynchronously has the nice side effect of not starving the event loop too.
I don't think we should use setImmediate for rescheduling timers for the reason I mentioned in #3063 (comment):
Using |
lib/timers.js Outdated
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.
I don't really understand how this won't be hit when there are just timers scheduled for a later date?
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.
first._idleStart is the time at which a timer was scheduled. Since now is updated to be the time at the start of listOnTimeout execution, now is always > first._idleStart, unlessfirst was scheduled from within the call to listOnTimeout.
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.
Oh duh, this is _idleStart not _idleTimeout, I'm sorry.
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#9333Fixes: nodejs/node-v0.x-archive#15447Fixes: nodejs/node-v0.x-archive#25607Fixes: #5426 PR-URL: #3063
bnoordhuis commented Jul 26, 2016
For the record: #7866 is caused by this PR although it it may not be a regression in a strict sense. |
whitlockjc commented Jul 26, 2016
I'll take a peek. |
misterdjules commented Jul 26, 2016
@bnoordhuis Thanks for the heads up, I responded in #7866. |
MylesBorins commented Aug 30, 2016
Not 100% if this should land in It seems like there is a non zero chance this could break production code, even if the production code itself is wrong. As Adding |
Fishrock123 commented Aug 30, 2016
@thealphanerd This should be backported along with any fixes. I'm not sure how it could break, but the new behavior is definitely the expected one. (And inferred by docs too) |
Fishrock123 commented Aug 31, 2016
Here's another issue about this problem: #8354 (comment) @thealphanerd lmk if you need any help backporting |
MylesBorins commented Aug 31, 2016
@nodejs/lts how does everyone feel about backporting this given the above information @Fishrock123 is this commit dependent on your other timers changes? |
Fishrock123 commented Aug 31, 2016
I don't think so? |
MylesBorins commented Oct 11, 2016
@Fishrock123 will need help with the backport if you can |
This is a port of https://github.com/nodejs/nodejs/node-v0.x-archive/pull/17203 and nodejs/node-v0.x-archive#25763 which fixesnodejs/node-v0.x-archive#9333, nodejs/node-v0.x-archive#15447, nodejs/node-v0.x-archive#25607 and #5426.
/cc @Fishrock123, @misterdjules, @nodejs/collaborators and @nodejs/tsc