aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/select.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/select.py')
-rw-r--r--python/gevent/select.py74
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):
"""