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
Create custom Selectors (Kqueue, Epoll, Poll, and Select) Backport #1001
Conversation
@SethMichaelLarson Rather than us doing this, would it be better simply to vendor Python 3.5's Thoughts? |
@Lukasa This is what asyncio does, while possible I think it may be overkill in this situation as a selector has much more overhead compared to a polling object. Asyncio has a different use-case for those selectors to what we're using them for. |
@SethMichaelLarson Do we have a good feel on what the overhead is? |
@Lukasa Probably minimal but I can take a look if you're interested. I'm +1 on the vendoring |
Heh, if we get to the point of doing IOCP we're going to be in a whole separate world of pain anyway. ;) |
So I'll open a separate PR or just keep it in this one? |
Let's keep the work here and just retitle the PR a bit. |
Sure thing, will re-title when I commit. |
So a lot of talk has happened on this issue but took place in issue #1000 and I'm going to summarize this here. Basically there is no easy option. The The basic requirements for this issue are now:
Did I miss anything, @Lukasa ? |
Nope, looks good. By the time we get to the last bullet we should come to a position on whether we want to use something like coveralls for test coverage across multiple platforms, but let's do one step at a time. |
I'd recommend codecov over coveralls, just better functionality and integration with Github. /bikeshed 😉 But honestly, being able for it to comment on PRs is nice. |
Let's not put the cart before the horse. ;) Let's get kqueue support first, and then worry about testing it. =D |
@Lukasa Agreed! |
So I've done a little work and implemented I've decided to keep the From this starting point it will be adding a test suite for all selectors and implementing I'd like a review on this code before progressing further. |
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 cool, some feedback inline.
if hasattr(time, "monotime"): | ||
monotonic = time.monotonic | ||
else: | ||
monotonic = time.time |
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.
Rather than do if hasattr...else
, better to do try...except AttributeError
.
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.
Sure, also noticed a mistake using "monotime" instead of "monotonic".
result = None | ||
while expires is None: | ||
current_time = monotonic() | ||
if current_time > expires: |
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.
With timeout=0
this may fail immediately, because expires
is calculated based off an earlier call to monotonic()
. We should probably allow one run with timeout=0
, so you may want to have current_time
updated once before this loop begins (and use it to calculate expires
), and then update at the bottom of the loop.
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 should note as well that this guarantees that expires
is None
, and on Python 3 comparing a number to None
raises a TypeError
.
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 will fix this check, the next commit cycle will include tests so I can catch these trivial errors.
expires = monotonic() + timeout | ||
|
||
result = None | ||
while expires is 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.
As noted below, this loop condition cannot possibly be right. Presumably you mean while result is 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.
However, this doesn't allow for the possibility that the syscall may return None
. Now, I think that's unlikely, but to be defensive against that possibility let's create a sentinel object that we use instead of 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.
Wow, complete mistype, can't believe I missed that one. I'll change that. And yes we can use a sentinel object just in case a syscall returns None.
self.close() | ||
|
||
|
||
class _BaseSelectorImpl(BaseSelector): |
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.
Given that we don't need the utility of an ABC (and indeed given that BaseSelector
is not an ABC in this codebase), do we need this separation between BaseSelector
and _BaseSelectorImpl
?
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 was wondering this myself, probably not. I'll merge the two classes. This first revision was mostly to get the basics from selectors down.
if events & EVENT_READ: | ||
self._readers.add(key.fd) | ||
if events & EVENT_WRITE: | ||
self._readers.add(key.fd) |
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 wrong.
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.
You are correct.
elif 'EpollSelector' in globals(): | ||
DefaultSelector = EpollSelector | ||
elif 'PollSelector' in globals(): | ||
DefaultSelector = PollSelector |
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.
Obviously all of this jazz raises NameError
s all over the place.
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.
Shouldn't, this is the same method of doing things that's used in selectors.py
. I can change to something else though.
Thanks for the initial review @Lukasa |
@Lukasa Bit of information that I gleaned from PEP 475: I'm in favor of recalculating the timeout time for certain system calls, perhaps have a parameter called Edit: |
So I added a lot of tests (inspired by Would appreciate another review at this point, especially if all tests pass on Travis. |
Well identified. Sadly I have no evidence that #681 will be helped by this PR, so we can't close that one. |
@Lukasa Yeah I didn't know if this work would have any effect on that issue, I can try to recreate it and see what happens later. |
Will today be the big day? 🎉 |
Once we can get a green test run, you bet it will! |
WHY MUST THAT BUTTON BE THERE??? :( |
Just to punish you I'm afraid. |
Now it's done after my silly mistake. :) |
Hooray, merged! |
Version 1.9.1 + merge ba92f65f2cf7ff65eb03ffc42b7ea065da8152ca Fix urllib3/urllib3#1001
Create custom Selectors (Kqueue, Epoll, Poll, and Select) Backport
Changelog entry for urllib3#1001
This PR adds all the
select
methods to a new util module calledwait
. Uses the same ordering used byselectors.py
in the standard library to determine which selector should be used. Also factored in a way to allow easy patching and testing. Similar to #998 but uses a different way to break up the different methods to wait for I/O events. Also adds tests to exercise the selectors which can probably be extended.