Skip to content

Conversation

@volans-
Copy link
Contributor

@volans- volans- commented Sep 3, 2017

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.

https://bugs.python.org/issue31334

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.
@the-knights-who-say-ni
Copy link

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!

@volans-
Copy link
Contributor Author

volans- commented Sep 6, 2017

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
Copy link
Member

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;
}

Copy link
Contributor Author

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 */
Copy link
Member

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.
Copy link
Member

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.".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, thanks.

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:
Copy link
Member

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.

Copy link
Contributor Author

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,
Copy link
Member

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?

Copy link
Contributor Author

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.

@bedevere-bot
Copy link

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 didn't expect the Spanish Inquisition!. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@volans-
Copy link
Contributor Author

volans- commented Sep 11, 2017

@berkerpeksag did you had any chance to look at my replies and changes?

@volans-
Copy link
Contributor Author

volans- commented Sep 11, 2017

I didn't expect the Spanish Inquisition!

@bedevere-bot
Copy link

Nobody expects the Spanish Inquisition!

@berkerpeksag: please review the changes made to this pull request.

@volans-
Copy link
Contributor Author

volans- commented Oct 9, 2017

@berkerpeksag do you have any additional feedback on this change?

@berkerpeksag
Copy link
Member

@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.

return NULL;
}

deadline = 0; /* initialize to prevent gcc warning */
Copy link
Member

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;

Copy link
Member

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 */

deadline = 0; /* initialize to prevent gcc warning */

/* Check values for timeout */
if (timeout_obj == NULL || timeout_obj == Py_None) {
Copy link
Member

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 */

}

deadline = _PyTime_GetMonotonicClock() + timeout;
if (timeout >= 0)
Copy link
Member

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;

deadline = _PyTime_GetMonotonicClock() + timeout;
}

if (ms < 0) /* On many OSes timeout must be INFTIM or -1, issue 31334 */
Copy link
Member

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.

Copy link
Member

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.

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:
Copy link
Member

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?

@bedevere-bot
Copy link

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. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@volans-
Copy link
Contributor Author

volans- commented Oct 13, 2017

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.

@bedevere-bot
Copy link

Thanks for making the requested changes!

@berkerpeksag: please review the changes made to this pull request.

Copy link
Member

@berkerpeksag berkerpeksag left a 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) {
Copy link
Member

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().

Copy link
Contributor Author

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.

Copy link
Member

Choose a reason for hiding this comment

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

I concur with @volans-. It is better to keep ms < 0.

As for rounding problem, I just have opened bpo-31786. I didn't mentioned it here because seems this change doesn't change this behavior.

Copy link
Member

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.

@miss-islington
Copy link
Contributor

Thanks @volans- for the PR, and @Haypo for merging it 🌮🎉.. I'm working now to backport this PR to: 2.7, 3.6.
🐍🍒⛏🤖

@vstinner
Copy link
Member

Wow, 3 approvals from 3 different core developers. Well done @volans- ! Thanks for this nice bug fix ;-)

@miss-islington
Copy link
Contributor

Sorry, @volans- and @Haypo, I could not cleanly backport this to 3.6 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 6cfa927ceb931ad968b5b03e4a2bffb64a8a0604 3.6

@miss-islington
Copy link
Contributor

Sorry, @volans- and @Haypo, I could not cleanly backport this to 2.7 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 6cfa927ceb931ad968b5b03e4a2bffb64a8a0604 2.7

@vstinner
Copy link
Member

I removed the "needs backport" labels to prevent the bot to try again to create backport fixes.

@volans-
Copy link
Contributor Author

volans- commented Oct 17, 2017

@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?

@serhiy-storchaka
Copy link
Member

First #4003 should be backported.

@miss-islington
Copy link
Contributor

Thanks @volans- for the PR, and @Haypo for merging it 🌮🎉.. I'm working now to backport this PR to: 3.6.
🐍🍒⛏🤖

@miss-islington
Copy link
Contributor

Sorry, @volans- and @Haypo, I could not cleanly backport this to 3.6 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 6cfa927ceb931ad968b5b03e4a2bffb64a8a0604 3.6

@miss-islington
Copy link
Contributor

Thanks @volans- for the PR, and @Haypo for merging it 🌮🎉.. I'm working now to backport this PR to: 3.6.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Oct 18, 2017
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)
@bedevere-bot
Copy link

GH-4033 is a backport of this pull request to the 3.6 branch.

@miss-islington
Copy link
Contributor

Thanks @volans- for the PR, and @Haypo for merging it 🌮🎉.. I'm working now to backport this PR to: 2.7.
🐍🍒⛏🤖 I'm not a witch! I'm not a witch!

@miss-islington
Copy link
Contributor

Sorry, @volans- and @Haypo, I could not cleanly backport this to 2.7 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 6cfa927ceb931ad968b5b03e4a2bffb64a8a0604 2.7

volans- added a commit to volans-/cpython that referenced this pull request Oct 18, 2017
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)
@bedevere-bot
Copy link

GH-4034 is a backport of this pull request to the 2.7 branch.

@volans- volans- deleted the fix-select-poll branch October 18, 2017 11:26
@volans-
Copy link
Contributor Author

volans- commented Oct 18, 2017

@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.

serhiy-storchaka pushed a commit that referenced this pull request Oct 18, 2017
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)
serhiy-storchaka pushed a commit that referenced this pull request Oct 18, 2017
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)
@serhiy-storchaka
Copy link
Member

Thanks @volans-!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-bug An unexpected behavior, bug, or error

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants