# Copyright (c) 2009-2011 Denis Bilenko. See LICENSE for details. """ Waiting for I/O completion. """ from __future__ import absolute_import, division, print_function import sys import select as __select__ from gevent.event import Event 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._util import copy_globals from gevent._util import _NONE from errno import EINTR _real_original_select = __select__.select if sys.platform.startswith('win32'): def _original_select(r, w, x, t): # windows can't handle three empty lists, but we've always # accepted that if not r and not w and not x: return ((), (), ()) return _real_original_select(r, w, x, t) else: _original_select = _real_original_select # These will be replaced by copy_globals if they are defined by the # platform. They're not defined on Windows, but we still provide # poll() there. We only pay attention to POLLIN and POLLOUT. POLLIN = 1 POLLPRI = 2 POLLOUT = 4 POLLERR = 8 POLLHUP = 16 POLLNVAL = 32 POLLRDNORM = 64 POLLRDBAND = 128 POLLWRNORM = 4 POLLWRBAND = 256 __implements__ = [ 'select', ] if hasattr(__select__, 'poll'): __implements__.append('poll') else: __extra__ = [ 'poll', ] __all__ = ['error'] + __implements__ error = __select__.error __imports__ = copy_globals(__select__, globals(), names_to_ignore=__all__, dunder_names_to_keep=()) _EV_READ = 1 _EV_WRITE = 2 def get_fileno(obj): try: fileno_f = obj.fileno except AttributeError: if not isinstance(obj, integer_types): raise TypeError('argument must be an int, or have a fileno() method: %r' % (obj,)) return obj return fileno_f() class SelectResult(object): __slots__ = () @staticmethod def _make_callback(ready_collection, event, mask): def cb(fd, watcher): ready_collection.append(fd) watcher.close() event.set() cb.mask = mask return cb @classmethod def _make_watchers(cls, watchers, *fd_cb): loop = get_hub().loop io = loop.io MAXPRI = loop.MAXPRI for fdlist, callback in fd_cb: try: for fd in fdlist: watcher = io(get_fileno(fd), callback.mask) watcher.priority = MAXPRI watchers.append(watcher) watcher.start(callback, fd, watcher) except IOError as ex: raise error(*ex.args) @staticmethod def _closeall(watchers): for watcher in watchers: watcher.stop() watcher.close() del watchers[:] def select(self, rlist, wlist, timeout): watchers = [] # read and write are the collected ready objects, accumulated # by the callback. Note that we could get spurious callbacks # if the socket is closed while we're blocked. We can't easily # detect that (libev filters the events passed so we can't # pass arbitrary events). After an iteration of polling for # IO, libev will invoke all the pending IO watchers, and then # any newly added (fed) events, and then we will invoke added # callbacks. With libev 4.27+ and EV_VERIFY, it's critical to # close our watcher immediately once we get an event. That # could be the close event (coming just before the actual # close happens), and once the FD is closed, libev will abort # the process if we stop the watcher. read = [] write = [] event = Event() add_read = self._make_callback(read, event, _EV_READ) add_write = self._make_callback(write, event, _EV_WRITE) try: self._make_watchers(watchers, (rlist, add_read), (wlist, add_write)) event.wait(timeout=timeout) return read, write, [] finally: self._closeall(watchers) def select(rlist, wlist, xlist, timeout=None): # pylint:disable=unused-argument """An implementation of :obj:`select.select` that blocks only the current greenlet. .. caution:: *xlist* is ignored. .. versionchanged:: 1.2a1 Raise a :exc:`ValueError` if timeout is negative. This matches Python 3's behaviour (Python 2 would raise a ``select.error``). Previously gevent had undefined behaviour. .. versionchanged:: 1.2a1 Raise an exception if any of the file descriptors are invalid. """ if timeout is not None and timeout < 0: # Raise an error like the real implementation; which error # depends on the version. Python 3, where select.error is OSError, # raises a ValueError (which makes sense). Older pythons raise # the error from the select syscall...but we don't actually get there. # We choose to just raise the ValueError as it makes more sense and is # forward compatible raise ValueError("timeout must be non-negative") # since rlist and wlist can be any iterable we will have to first # copy them into a list, so we can use them in both _original_select # and in SelectResult.select. We don't need to do it for xlist, since # that one will only be passed into _original_select rlist = rlist if isinstance(rlist, (list, tuple)) else list(rlist) wlist = wlist if isinstance(wlist, (list, tuple)) else list(wlist) # First, do a poll with the original select system call. This is # the most efficient way to check to see if any of the file # descriptors have previously been closed and raise the correct # corresponding exception. (Because libev tends to just return # them as ready, or, if built with EV_VERIFY >= 2 and libev >= # 4.27, crash the process. And libuv also tends to crash the # process.) # # We accept the *xlist* here even though we can't # below because this is all about error handling. sel_results = ((), (), ()) try: sel_results = _original_select(rlist, wlist, xlist, 0) except error as e: enumber = getattr(e, 'errno', None) or e.args[0] if enumber != EINTR: # Ignore interrupted syscalls raise 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.) _g_sleep() return sel_results result = SelectResult() return result.select(rlist, wlist, timeout) class PollResult(object): __slots__ = ('events', 'event') def __init__(self): self.events = set() self.event = Event() def add_event(self, events, fd): if events < 0: result_flags = POLLNVAL else: result_flags = 0 if events & _EV_READ: result_flags = POLLIN if events & _EV_WRITE: result_flags |= POLLOUT self.events.add((fd, result_flags)) self.event.set() class poll(object): """ An implementation of :obj:`select.poll` that blocks only the current greenlet. With only one exception, the interface is the same as the standard library interface. .. caution:: ``POLLPRI`` data is not supported. .. versionadded:: 1.1b1 .. versionchanged:: 1.5 This is now always defined, regardless of whether the standard library defines :func:`select.poll` or not. Note that it may have different performance characteristics. """ def __init__(self): # {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): """ Register a file descriptor *fd* with the polling object. Future calls to the :meth:`poll`` method will then check whether the file descriptor has any pending I/O events. *fd* can be either an integer, or an object with a ``fileno()`` method that returns an integer. File objects implement ``fileno()``, so they can also be used as the argument (but remember that regular files are usually always ready). *eventmask* is an optional bitmask describing the type of events you want to check for, and can be a combination of the constants ``POLLIN``, and ``POLLOUT`` (``POLLPRI`` is not supported). """ if eventmask is _NONE: flags = _EV_READ | _EV_WRITE else: flags = 0 if eventmask & POLLIN: flags = _EV_READ if eventmask & POLLOUT: flags |= _EV_WRITE # If they ask for POLLPRI, we can't support # that. Should we raise an error? fileno = get_fileno(fd) self.fds[fileno] = flags def modify(self, fd, eventmask): """ Change the set of events being watched on *fd*. """ self.register(fd, eventmask) def _get_started_watchers(self, watcher_cb): watchers = [] io = self.loop.io MAXPRI = self.loop.MAXPRI try: for fd, flags in iteritems(self.fds): watcher = io(fd, flags) watchers.append(watcher) watcher.priority = MAXPRI watcher.start(watcher_cb, fd, pass_events=True) except: for awatcher in watchers: awatcher.stop() awatcher.close() raise return watchers def poll(self, timeout=None): """ poll the registered fds. .. 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 = self._get_started_watchers(result.add_event) try: 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 watchers: awatcher.stop() awatcher.close() def unregister(self, fd): """ Unregister the *fd*. .. versionchanged:: 1.2a1 Raise a `KeyError` if *fd* was not registered, like the standard library. Previously gevent did nothing. """ fileno = get_fileno(fd) del self.fds[fileno] def _gevent_do_monkey_patch(patch_request): aggressive = patch_request.patch_kwargs['aggressive'] patch_request.default_patch_items() if aggressive: # since these are blocking we're removing them here. This makes some other # modules (e.g. asyncore) non-blocking, as they use select that we provide # when none of these are available. patch_request.remove_item( 'epoll', 'kqueue', 'kevent', 'devpoll', )