diff options
Diffstat (limited to 'python/gevent/select.py')
-rw-r--r-- | python/gevent/select.py | 74 |
1 files changed, 58 insertions, 16 deletions
diff --git a/python/gevent/select.py b/python/gevent/select.py index e5680b5..a4c182c 100644 --- a/python/gevent/select.py +++ b/python/gevent/select.py @@ -2,27 +2,30 @@ """ Waiting for I/O completion. """ -from __future__ import absolute_import +from __future__ import absolute_import, division, print_function import sys from gevent.event import Event -from gevent.hub import get_hub +from gevent.hub import _get_hub_noargs as get_hub from gevent.hub import sleep as _g_sleep from gevent._compat import integer_types from gevent._compat import iteritems -from gevent._compat import itervalues from gevent._util import copy_globals from gevent._util import _NONE from errno import EINTR +from select import select as _real_original_select if sys.platform.startswith('win32'): - def _original_select(_r, _w, _x, _t): + def _original_select(r, w, x, t): # windows cant handle three empty lists, but we've always - # accepted that, so don't try the compliance check on windows - return ((), (), ()) + # accepted that + if not r and not w and not x: + return ((), (), ()) + return _real_original_select(r, w, x, t) else: - from select import select as _original_select + _original_select = _real_original_select + try: from select import poll as original_poll @@ -97,6 +100,7 @@ class SelectResult(object): def _closeall(self, watchers): for watcher in watchers: watcher.stop() + watcher.close() del watchers[:] def select(self, rlist, wlist, timeout): @@ -145,9 +149,16 @@ def select(rlist, wlist, xlist, timeout=None): # pylint:disable=unused-argument # Ignore interrupted syscalls raise - if sel_results[0] or sel_results[1] or sel_results[2]: + if sel_results[0] or sel_results[1] or sel_results[2] or (timeout is not None and timeout == 0): # If we actually had stuff ready, go ahead and return it. No need # to go through the trouble of doing our own stuff. + + # Likewise, if the timeout is 0, we already did a 0 timeout + # select and we don't need to do it again. Note that in libuv, + # zero duration timers may be called immediately, without + # cycling the event loop at all. 2.7/test_telnetlib.py "hangs" + # calling zero-duration timers if we go to the loop here. + # However, because this is typically a place where scheduling switches # can occur, we need to make sure that's still the case; otherwise a single # consumer could monopolize the thread. (shows up in test_ftplib.) @@ -188,7 +199,11 @@ if original_poll is not None: .. versionadded:: 1.1b1 """ def __init__(self): - self.fds = {} # {int -> watcher} + # {int -> flags} + # We can't keep watcher objects in here because people commonly + # just drop the poll object when they're done, without calling + # unregister(). dnspython does this. + self.fds = {} self.loop = get_hub().loop def register(self, fd, eventmask=_NONE): @@ -204,9 +219,7 @@ if original_poll is not None: # that. Should we raise an error? fileno = get_fileno(fd) - watcher = self.loop.io(fileno, flags) - watcher.priority = self.loop.MAXPRI - self.fds[fileno] = watcher + self.fds[fileno] = flags def modify(self, fd, eventmask): self.register(fd, eventmask) @@ -217,18 +230,47 @@ if original_poll is not None: .. versionchanged:: 1.2a1 File descriptors that are closed are reported with POLLNVAL. + + .. versionchanged:: 1.3a2 + Under libuv, interpret *timeout* values less than 0 the same as *None*, + i.e., block. This was always the case with libev. """ result = PollResult() + watchers = [] + io = self.loop.io + MAXPRI = self.loop.MAXPRI try: - for fd, watcher in iteritems(self.fds): + for fd, flags in iteritems(self.fds): + watcher = io(fd, flags) + watchers.append(watcher) + watcher.priority = MAXPRI watcher.start(result.add_event, fd, pass_events=True) - if timeout is not None and timeout > -1: - timeout /= 1000.0 + if timeout is not None: + if timeout < 0: + # The docs for python say that an omitted timeout, + # a negative timeout and a timeout of None are all + # supposed to block forever. Many, but not all + # OS's accept any negative number to mean that. Some + # OS's raise errors for anything negative but not -1. + # Python 3.7 changes to always pass exactly -1 in that + # case from selectors. + + # Our Timeout class currently does not have a defined behaviour + # for negative values. On libuv, it uses a check watcher and effectively + # doesn't block. On libev, it seems to block. In either case, we + # *want* to block, so turn this into the sure fire block request. + timeout = None + elif timeout: + # The docs for poll.poll say timeout is in + # milliseconds. Our result objects work in + # seconds, so this should be *=, shouldn't it? + timeout /= 1000.0 result.event.wait(timeout=timeout) return list(result.events) finally: - for awatcher in itervalues(self.fds): + for awatcher in watchers: awatcher.stop() + awatcher.close() def unregister(self, fd): """ |