-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
bpo-31334: Fix timeout in select.poll.poll() #3277
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
Conversation
Always pass -1, or INFTIM where defined, to the poll() system call when a negative timeout is passed to the poll.poll([timeout]) method in the select module. Various OSes throw an error with arbitrary negative values.
|
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately our records indicate you have not signed the CLA. For legal reasons we need you to sign this before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. Thanks again to your contribution and we look forward to looking at it! |
|
For the record I've signed the CLA and my status has been updated on bugs.python.org. |
| if (ms >= 0) | ||
| deadline = _PyTime_GetMonotonicClock() + timeout; | ||
| else /* On many OSes timeout must be -1 or INFTIM, issue 31334 */ | ||
| #ifdef INFTIM |
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.
Can't we do this in the following block?
if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
#ifdef INFTIM
ms = INFTIM;
#else
ms = -1;
#endif
deadline = 0;
}Then we can just change
deadline = _PyTime_GetMonotonicClock() + timeout;to
if (ms >= 0) {
deadline = _PyTime_GetMonotonicClock() + timeout;
}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 see how what you're proposing can fix the issue. When a user passes a negative timeout parameter it enters the else block at line 542, so ms will be negative and the system call will still fail. The problem is not the deadline but the ms variable.
But your comment made me realise that I should add the #ifdef to the if block too, for coherence and in case an OS will ever have INFTIM != -1. To be DRY I'll move the #ifdef after the else block.
| if (timeout_obj == NULL || timeout_obj == Py_None) { | ||
| timeout = -1; | ||
| ms = -1; | ||
| deadline = 0; /* initialize to prevent gcc warning */ |
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.
See my comment about INFTIM below. I think we don't need to move this line out of this if branch.
| @@ -0,0 +1,2 @@ | |||
| Fix ``poll.poll([timeout])`` in the ``select`` module for arbitrary negative | |||
| timeouts on all OSes where it can only be a non-negative integer or -1. | |||
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.
Please add "Patch by Riccardo Coccioli.".
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.
Ok, thanks.
Lib/test/test_poll.py
Outdated
| pollster = select.poll() | ||
| pollster.register( p, select.POLLIN ) | ||
| for tout in (0, 1000, 2000, 4000, 8000, 16000) + (-1,)*10: | ||
| for tout in (0, 1000, 2000, 4000, 8000, 16000, -1000) + (-1,)*10: |
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.
Style nitpick: I'd move -1000 to the beginning of the tuple to make it ordered.
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.
Ok.
| deadline = 0; /* initialize to prevent gcc warning */ | ||
| } | ||
| else { | ||
| if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, |
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.
Another question: Can't we just set timeout to INFTIM or -1 if timeout_obj is less than -1 instead of messing with ms?
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 of all thanks for the review.
I'm not getting why this would be better. The current checks against INT_MIN and INT_MAX are done with ms, so I followed that pattern. Also ms is the actual parameter passed to the system call and the one causing the issue right now, it seemed more logic to me to change directly this one.
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
|
@berkerpeksag did you had any chance to look at my replies and changes? |
|
I didn't expect the Spanish Inquisition! |
|
Nobody expects the Spanish Inquisition! @berkerpeksag: please review the changes made to this pull request. |
|
@berkerpeksag do you have any additional feedback on this change? |
|
@volans- sorry for the delay. I need to take a closer look at this (and a BSD to test it locally) Please ping me again if I don't get to it by the next week. |
Modules/selectmodule.c
Outdated
| return NULL; | ||
| } | ||
|
|
||
| deadline = 0; /* initialize to prevent gcc warning */ |
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.
We can set this in deadline initialization:
_PyTime_t deadline = 0;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.
And the following comment is not needed anymore:
/* initialize to prevent gcc warning */
Modules/selectmodule.c
Outdated
| deadline = 0; /* initialize to prevent gcc warning */ | ||
|
|
||
| /* Check values for timeout */ | ||
| if (timeout_obj == NULL || timeout_obj == Py_None) { |
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.
Looking at this again, we can get rid of this if branch if we move out default values to variable declarations:
_PyTime_t timeout = -1, ms = -1, deadline = 0;Then we can get rid of the following comment too:
/* Check values for timeout */
Modules/selectmodule.c
Outdated
| } | ||
|
|
||
| deadline = _PyTime_GetMonotonicClock() + timeout; | ||
| if (timeout >= 0) |
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.
Style nit: Per PEP 7, we now use the following style in new C code:
if (spam) {
return 42;
}instead of
if (spam)
return 42;
Modules/selectmodule.c
Outdated
| deadline = _PyTime_GetMonotonicClock() + timeout; | ||
| } | ||
|
|
||
| if (ms < 0) /* On many OSes timeout must be INFTIM or -1, issue 31334 */ |
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.
Style nit: Same as above.
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.
Can we make this comment clearer? For example, it would be nice to mention this is the case if negative timeout is less than -1 on some OSes.
Also, I'd replace "many" with "some" or "some BSD OSes" since this is the first time this issue has been reported.
Lib/test/test_poll.py
Outdated
| pollster = select.poll() | ||
| pollster.register( p, select.POLLIN ) | ||
| for tout in (0, 1000, 2000, 4000, 8000, 16000) + (-1,)*10: | ||
| for tout in (-1000, 0, 1000, 2000, 4000, 8000, 16000) + (-1,)*10: |
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.
This is not directly related to this ticket, but I realized that None is not tested in test_poll. Could you also add None while we are here?
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
|
I have made the requested changes; please review again. @berkerpeksag : Thank you very much for the review and the suggestions to simplify the code. My first patch was as less impactful as possible because I've found in other communities harder to get patches approved if they were also refactoring existing code. I'm happy to see a different approach here. |
|
Thanks for making the requested changes! @berkerpeksag: please review the changes made to this pull request. |
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.
This looks pretty good to me, thank you! I just left a comment about the ms < 0 check.
I will ask for another pair of eyes from another core developer then merge it.
| /* On some OSes, typically BSD-based ones, the timeout parameter of the | ||
| poll() syscall, when negative, must be exactly INFTIM, where defined, | ||
| or -1. See issue 31334. */ | ||
| if (ms < 0) { |
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.
Nitpick: Can we change this to ms < -1? Since we set ms = -1 in line 528, we shouldn't get any "invalid argument" exception if we don't pass a timeout value to p.poll().
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.
@berkerpeksag what if a OS defines INFTIM different from -1? This ensures that we set ms to INFTIM in that case too. I can change it to ms <= -1 if you think it's more readable.
On a side note, the documentation says only that the timeout parameter is in milliseconds, but doesn't say it must be an integer. I've noticed that passing a float works too and in the special case of passing a float in the range (-1, 0), that is converted to 0 by ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);. I've tested that this behaviour does not change with my patch, but is arguably the desired one.
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.
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.
@volans- fair enough, thanks for the explanation. I think ms < 0 can stay as is, no need to change it to ms <= -1.
|
Wow, 3 approvals from 3 different core developers. Well done @volans- ! Thanks for this nice bug fix ;-) |
|
Sorry, @volans- and @Haypo, I could not cleanly backport this to |
|
Sorry, @volans- and @Haypo, I could not cleanly backport this to |
|
I removed the "needs backport" labels to prevent the bot to try again to create backport fixes. |
|
@Haypo thank you all for the review, comments and for approving/merging it. Should I create manual PRs for the backports given that the bot seems to have some issues today? |
|
First #4003 should be backported. |
|
Sorry, @volans- and @Haypo, I could not cleanly backport this to |
Always pass -1, or INFTIM where defined, to the poll() system call when a negative timeout is passed to the poll.poll([timeout]) method in the select module. Various OSes throw an error with arbitrary negative values. (cherry picked from commit 6cfa927)
|
GH-4033 is a backport of this pull request to the 3.6 branch. |
|
Sorry, @volans- and @Haypo, I could not cleanly backport this to |
Always pass -1, or INFTIM where defined, to the poll() system call when a negative timeout is passed to the poll.poll([timeout]) method in the select module. Various OSes throw an error with arbitrary negative values.. (cherry picked from commit 6cfa927)
|
GH-4034 is a backport of this pull request to the 2.7 branch. |
|
@serhiy-storchaka I've ported this to the 2.7 branch with GH-4034, it's a smaller patch given the code in the 2.7 branch. |
Always pass -1, or INFTIM where defined, to the poll() system call when a negative timeout is passed to the poll.poll([timeout]) method in the select module. Various OSes throw an error with arbitrary negative values.. (cherry picked from commit 6cfa927)
Always pass -1, or INFTIM where defined, to the poll() system call when a negative timeout is passed to the poll.poll([timeout]) method in the select module. Various OSes throw an error with arbitrary negative values. (cherry picked from commit 6cfa927)
|
Thanks @volans-! |
Always pass
-1, orINFTIMwhere defined, to thepoll()system call whena negative timeout is passed to the
poll.poll([timeout])method in theselectmodule. Various OSes throw an error with arbitrary negativevalues.
https://bugs.python.org/issue31334