mercurial/selectors2.py
branchstable
changeset 35794 27b6df1b5adb
parent 35541 87676e8ee056
parent 35793 4fb2bb61597c
child 35795 7aaea7d323dc
equal deleted inserted replaced
35541:87676e8ee056 35794:27b6df1b5adb
     1 """ Back-ported, durable, and portable selectors """
       
     2 
       
     3 # MIT License
       
     4 #
       
     5 # Copyright (c) 2017 Seth Michael Larson
       
     6 #
       
     7 # Permission is hereby granted, free of charge, to any person obtaining a copy
       
     8 # of this software and associated documentation files (the "Software"), to deal
       
     9 # in the Software without restriction, including without limitation the rights
       
    10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    11 # copies of the Software, and to permit persons to whom the Software is
       
    12 # furnished to do so, subject to the following conditions:
       
    13 #
       
    14 # The above copyright notice and this permission notice shall be included in all
       
    15 # copies or substantial portions of the Software.
       
    16 #
       
    17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       
    20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       
    23 # SOFTWARE.
       
    24 
       
    25 # no-check-code
       
    26 
       
    27 from __future__ import absolute_import
       
    28 
       
    29 import collections
       
    30 import errno
       
    31 import math
       
    32 import select
       
    33 import socket
       
    34 import sys
       
    35 import time
       
    36 
       
    37 from . import pycompat
       
    38 
       
    39 namedtuple = collections.namedtuple
       
    40 Mapping = collections.Mapping
       
    41 
       
    42 try:
       
    43     monotonic = time.monotonic
       
    44 except AttributeError:
       
    45     monotonic = time.time
       
    46 
       
    47 __author__ = 'Seth Michael Larson'
       
    48 __email__ = 'sethmichaellarson@protonmail.com'
       
    49 __version__ = '2.0.0'
       
    50 __license__ = 'MIT'
       
    51 __url__ = 'https://www.github.com/SethMichaelLarson/selectors2'
       
    52 
       
    53 __all__ = ['EVENT_READ',
       
    54            'EVENT_WRITE',
       
    55            'SelectorKey',
       
    56            'DefaultSelector',
       
    57            'BaseSelector']
       
    58 
       
    59 EVENT_READ = (1 << 0)
       
    60 EVENT_WRITE = (1 << 1)
       
    61 _DEFAULT_SELECTOR = None
       
    62 _SYSCALL_SENTINEL = object()  # Sentinel in case a system call returns None.
       
    63 _ERROR_TYPES = (OSError, IOError, socket.error)
       
    64 
       
    65 
       
    66 SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
       
    67 
       
    68 
       
    69 class _SelectorMapping(Mapping):
       
    70     """ Mapping of file objects to selector keys """
       
    71 
       
    72     def __init__(self, selector):
       
    73         self._selector = selector
       
    74 
       
    75     def __len__(self):
       
    76         return len(self._selector._fd_to_key)
       
    77 
       
    78     def __getitem__(self, fileobj):
       
    79         try:
       
    80             fd = self._selector._fileobj_lookup(fileobj)
       
    81             return self._selector._fd_to_key[fd]
       
    82         except KeyError:
       
    83             raise KeyError("{0!r} is not registered.".format(fileobj))
       
    84 
       
    85     def __iter__(self):
       
    86         return iter(self._selector._fd_to_key)
       
    87 
       
    88 
       
    89 def _fileobj_to_fd(fileobj):
       
    90     """ Return a file descriptor from a file object. If
       
    91     given an integer will simply return that integer back. """
       
    92     if isinstance(fileobj, int):
       
    93         fd = fileobj
       
    94     else:
       
    95         try:
       
    96             fd = int(fileobj.fileno())
       
    97         except (AttributeError, TypeError, ValueError):
       
    98             raise ValueError("Invalid file object: {0!r}".format(fileobj))
       
    99     if fd < 0:
       
   100         raise ValueError("Invalid file descriptor: {0}".format(fd))
       
   101     return fd
       
   102 
       
   103 
       
   104 class BaseSelector(object):
       
   105     """ Abstract Selector class
       
   106 
       
   107     A selector supports registering file objects to be monitored
       
   108     for specific I/O events.
       
   109 
       
   110     A file object is a file descriptor or any object with a
       
   111     `fileno()` method. An arbitrary object can be attached to the
       
   112     file object which can be used for example to store context info,
       
   113     a callback, etc.
       
   114 
       
   115     A selector can use various implementations (select(), poll(), epoll(),
       
   116     and kqueue()) depending on the platform. The 'DefaultSelector' class uses
       
   117     the most efficient implementation for the current platform.
       
   118     """
       
   119     def __init__(self):
       
   120         # Maps file descriptors to keys.
       
   121         self._fd_to_key = {}
       
   122 
       
   123         # Read-only mapping returned by get_map()
       
   124         self._map = _SelectorMapping(self)
       
   125 
       
   126     def _fileobj_lookup(self, fileobj):
       
   127         """ Return a file descriptor from a file object.
       
   128         This wraps _fileobj_to_fd() to do an exhaustive
       
   129         search in case the object is invalid but we still
       
   130         have it in our map. Used by unregister() so we can
       
   131         unregister an object that was previously registered
       
   132         even if it is closed. It is also used by _SelectorMapping
       
   133         """
       
   134         try:
       
   135             return _fileobj_to_fd(fileobj)
       
   136         except ValueError:
       
   137 
       
   138             # Search through all our mapped keys.
       
   139             for key in self._fd_to_key.values():
       
   140                 if key.fileobj is fileobj:
       
   141                     return key.fd
       
   142 
       
   143             # Raise ValueError after all.
       
   144             raise
       
   145 
       
   146     def register(self, fileobj, events, data=None):
       
   147         """ Register a file object for a set of events to monitor. """
       
   148         if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
       
   149             raise ValueError("Invalid events: {0!r}".format(events))
       
   150 
       
   151         key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
       
   152 
       
   153         if key.fd in self._fd_to_key:
       
   154             raise KeyError("{0!r} (FD {1}) is already registered"
       
   155                            .format(fileobj, key.fd))
       
   156 
       
   157         self._fd_to_key[key.fd] = key
       
   158         return key
       
   159 
       
   160     def unregister(self, fileobj):
       
   161         """ Unregister a file object from being monitored. """
       
   162         try:
       
   163             key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
       
   164         except KeyError:
       
   165             raise KeyError("{0!r} is not registered".format(fileobj))
       
   166 
       
   167         # Getting the fileno of a closed socket on Windows errors with EBADF.
       
   168         except socket.error as err:
       
   169             if err.errno != errno.EBADF:
       
   170                 raise
       
   171             else:
       
   172                 for key in self._fd_to_key.values():
       
   173                     if key.fileobj is fileobj:
       
   174                         self._fd_to_key.pop(key.fd)
       
   175                         break
       
   176                 else:
       
   177                     raise KeyError("{0!r} is not registered".format(fileobj))
       
   178         return key
       
   179 
       
   180     def modify(self, fileobj, events, data=None):
       
   181         """ Change a registered file object monitored events and data. """
       
   182         # NOTE: Some subclasses optimize this operation even further.
       
   183         try:
       
   184             key = self._fd_to_key[self._fileobj_lookup(fileobj)]
       
   185         except KeyError:
       
   186             raise KeyError("{0!r} is not registered".format(fileobj))
       
   187 
       
   188         if events != key.events:
       
   189             self.unregister(fileobj)
       
   190             key = self.register(fileobj, events, data)
       
   191 
       
   192         elif data != key.data:
       
   193             # Use a shortcut to update the data.
       
   194             key = key._replace(data=data)
       
   195             self._fd_to_key[key.fd] = key
       
   196 
       
   197         return key
       
   198 
       
   199     def select(self, timeout=None):
       
   200         """ Perform the actual selection until some monitored file objects
       
   201         are ready or the timeout expires. """
       
   202         raise NotImplementedError()
       
   203 
       
   204     def close(self):
       
   205         """ Close the selector. This must be called to ensure that all
       
   206         underlying resources are freed. """
       
   207         self._fd_to_key.clear()
       
   208         self._map = None
       
   209 
       
   210     def get_key(self, fileobj):
       
   211         """ Return the key associated with a registered file object. """
       
   212         mapping = self.get_map()
       
   213         if mapping is None:
       
   214             raise RuntimeError("Selector is closed")
       
   215         try:
       
   216             return mapping[fileobj]
       
   217         except KeyError:
       
   218             raise KeyError("{0!r} is not registered".format(fileobj))
       
   219 
       
   220     def get_map(self):
       
   221         """ Return a mapping of file objects to selector keys """
       
   222         return self._map
       
   223 
       
   224     def _key_from_fd(self, fd):
       
   225         """ Return the key associated to a given file descriptor
       
   226          Return None if it is not found. """
       
   227         try:
       
   228             return self._fd_to_key[fd]
       
   229         except KeyError:
       
   230             return None
       
   231 
       
   232     def __enter__(self):
       
   233         return self
       
   234 
       
   235     def __exit__(self, *_):
       
   236         self.close()
       
   237 
       
   238 
       
   239 # Almost all platforms have select.select()
       
   240 if hasattr(select, "select"):
       
   241     class SelectSelector(BaseSelector):
       
   242         """ Select-based selector. """
       
   243         def __init__(self):
       
   244             super(SelectSelector, self).__init__()
       
   245             self._readers = set()
       
   246             self._writers = set()
       
   247 
       
   248         def register(self, fileobj, events, data=None):
       
   249             key = super(SelectSelector, self).register(fileobj, events, data)
       
   250             if events & EVENT_READ:
       
   251                 self._readers.add(key.fd)
       
   252             if events & EVENT_WRITE:
       
   253                 self._writers.add(key.fd)
       
   254             return key
       
   255 
       
   256         def unregister(self, fileobj):
       
   257             key = super(SelectSelector, self).unregister(fileobj)
       
   258             self._readers.discard(key.fd)
       
   259             self._writers.discard(key.fd)
       
   260             return key
       
   261 
       
   262         def select(self, timeout=None):
       
   263             # Selecting on empty lists on Windows errors out.
       
   264             if not len(self._readers) and not len(self._writers):
       
   265                 return []
       
   266 
       
   267             timeout = None if timeout is None else max(timeout, 0.0)
       
   268             ready = []
       
   269             r, w, _ = _syscall_wrapper(self._wrap_select, True, self._readers,
       
   270                                        self._writers, timeout)
       
   271             r = set(r)
       
   272             w = set(w)
       
   273             for fd in r | w:
       
   274                 events = 0
       
   275                 if fd in r:
       
   276                     events |= EVENT_READ
       
   277                 if fd in w:
       
   278                     events |= EVENT_WRITE
       
   279 
       
   280                 key = self._key_from_fd(fd)
       
   281                 if key:
       
   282                     ready.append((key, events & key.events))
       
   283             return ready
       
   284 
       
   285         def _wrap_select(self, r, w, timeout=None):
       
   286             """ Wrapper for select.select because timeout is a positional arg """
       
   287             return select.select(r, w, [], timeout)
       
   288 
       
   289     __all__.append('SelectSelector')
       
   290 
       
   291     # Jython has a different implementation of .fileno() for socket objects.
       
   292     if pycompat.isjython:
       
   293         class _JythonSelectorMapping(object):
       
   294             """ This is an implementation of _SelectorMapping that is built
       
   295             for use specifically with Jython, which does not provide a hashable
       
   296             value from socket.socket.fileno(). """
       
   297 
       
   298             def __init__(self, selector):
       
   299                 assert isinstance(selector, JythonSelectSelector)
       
   300                 self._selector = selector
       
   301 
       
   302             def __len__(self):
       
   303                 return len(self._selector._sockets)
       
   304 
       
   305             def __getitem__(self, fileobj):
       
   306                 for sock, key in self._selector._sockets:
       
   307                     if sock is fileobj:
       
   308                         return key
       
   309                 else:
       
   310                     raise KeyError("{0!r} is not registered.".format(fileobj))
       
   311 
       
   312         class JythonSelectSelector(SelectSelector):
       
   313             """ This is an implementation of SelectSelector that is for Jython
       
   314             which works around that Jython's socket.socket.fileno() does not
       
   315             return an integer fd value. All SelectorKey.fd will be equal to -1
       
   316             and should not be used. This instead uses object id to compare fileobj
       
   317             and will only use select.select as it's the only selector that allows
       
   318             directly passing in socket objects rather than registering fds.
       
   319             See: http://bugs.jython.org/issue1678
       
   320                  https://wiki.python.org/jython/NewSocketModule#socket.fileno.28.29_does_not_return_an_integer
       
   321             """
       
   322 
       
   323             def __init__(self):
       
   324                 super(JythonSelectSelector, self).__init__()
       
   325 
       
   326                 self._sockets = []  # Uses a list of tuples instead of dictionary.
       
   327                 self._map = _JythonSelectorMapping(self)
       
   328                 self._readers = []
       
   329                 self._writers = []
       
   330 
       
   331                 # Jython has a select.cpython_compatible_select function in older versions.
       
   332                 self._select_func = getattr(select, 'cpython_compatible_select', select.select)
       
   333 
       
   334             def register(self, fileobj, events, data=None):
       
   335                 for sock, _ in self._sockets:
       
   336                     if sock is fileobj:
       
   337                         raise KeyError("{0!r} is already registered"
       
   338                                        .format(fileobj, sock))
       
   339 
       
   340                 key = SelectorKey(fileobj, -1, events, data)
       
   341                 self._sockets.append((fileobj, key))
       
   342 
       
   343                 if events & EVENT_READ:
       
   344                     self._readers.append(fileobj)
       
   345                 if events & EVENT_WRITE:
       
   346                     self._writers.append(fileobj)
       
   347                 return key
       
   348 
       
   349             def unregister(self, fileobj):
       
   350                 for i, (sock, key) in enumerate(self._sockets):
       
   351                     if sock is fileobj:
       
   352                         break
       
   353                 else:
       
   354                     raise KeyError("{0!r} is not registered.".format(fileobj))
       
   355 
       
   356                 if key.events & EVENT_READ:
       
   357                     self._readers.remove(fileobj)
       
   358                 if key.events & EVENT_WRITE:
       
   359                     self._writers.remove(fileobj)
       
   360 
       
   361                 del self._sockets[i]
       
   362                 return key
       
   363 
       
   364             def _wrap_select(self, r, w, timeout=None):
       
   365                 """ Wrapper for select.select because timeout is a positional arg """
       
   366                 return self._select_func(r, w, [], timeout)
       
   367 
       
   368         __all__.append('JythonSelectSelector')
       
   369         SelectSelector = JythonSelectSelector  # Override so the wrong selector isn't used.
       
   370 
       
   371 
       
   372 if hasattr(select, "poll"):
       
   373     class PollSelector(BaseSelector):
       
   374         """ Poll-based selector """
       
   375         def __init__(self):
       
   376             super(PollSelector, self).__init__()
       
   377             self._poll = select.poll()
       
   378 
       
   379         def register(self, fileobj, events, data=None):
       
   380             key = super(PollSelector, self).register(fileobj, events, data)
       
   381             event_mask = 0
       
   382             if events & EVENT_READ:
       
   383                 event_mask |= select.POLLIN
       
   384             if events & EVENT_WRITE:
       
   385                 event_mask |= select.POLLOUT
       
   386             self._poll.register(key.fd, event_mask)
       
   387             return key
       
   388 
       
   389         def unregister(self, fileobj):
       
   390             key = super(PollSelector, self).unregister(fileobj)
       
   391             self._poll.unregister(key.fd)
       
   392             return key
       
   393 
       
   394         def _wrap_poll(self, timeout=None):
       
   395             """ Wrapper function for select.poll.poll() so that
       
   396             _syscall_wrapper can work with only seconds. """
       
   397             if timeout is not None:
       
   398                 if timeout <= 0:
       
   399                     timeout = 0
       
   400                 else:
       
   401                     # select.poll.poll() has a resolution of 1 millisecond,
       
   402                     # round away from zero to wait *at least* timeout seconds.
       
   403                     timeout = math.ceil(timeout * 1000)
       
   404 
       
   405             result = self._poll.poll(timeout)
       
   406             return result
       
   407 
       
   408         def select(self, timeout=None):
       
   409             ready = []
       
   410             fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
       
   411             for fd, event_mask in fd_events:
       
   412                 events = 0
       
   413                 if event_mask & ~select.POLLIN:
       
   414                     events |= EVENT_WRITE
       
   415                 if event_mask & ~select.POLLOUT:
       
   416                     events |= EVENT_READ
       
   417 
       
   418                 key = self._key_from_fd(fd)
       
   419                 if key:
       
   420                     ready.append((key, events & key.events))
       
   421 
       
   422             return ready
       
   423 
       
   424     __all__.append('PollSelector')
       
   425 
       
   426 if hasattr(select, "epoll"):
       
   427     class EpollSelector(BaseSelector):
       
   428         """ Epoll-based selector """
       
   429         def __init__(self):
       
   430             super(EpollSelector, self).__init__()
       
   431             self._epoll = select.epoll()
       
   432 
       
   433         def fileno(self):
       
   434             return self._epoll.fileno()
       
   435 
       
   436         def register(self, fileobj, events, data=None):
       
   437             key = super(EpollSelector, self).register(fileobj, events, data)
       
   438             events_mask = 0
       
   439             if events & EVENT_READ:
       
   440                 events_mask |= select.EPOLLIN
       
   441             if events & EVENT_WRITE:
       
   442                 events_mask |= select.EPOLLOUT
       
   443             _syscall_wrapper(self._epoll.register, False, key.fd, events_mask)
       
   444             return key
       
   445 
       
   446         def unregister(self, fileobj):
       
   447             key = super(EpollSelector, self).unregister(fileobj)
       
   448             try:
       
   449                 _syscall_wrapper(self._epoll.unregister, False, key.fd)
       
   450             except _ERROR_TYPES:
       
   451                 # This can occur when the fd was closed since registry.
       
   452                 pass
       
   453             return key
       
   454 
       
   455         def select(self, timeout=None):
       
   456             if timeout is not None:
       
   457                 if timeout <= 0:
       
   458                     timeout = 0.0
       
   459                 else:
       
   460                     # select.epoll.poll() has a resolution of 1 millisecond
       
   461                     # but luckily takes seconds so we don't need a wrapper
       
   462                     # like PollSelector. Just for better rounding.
       
   463                     timeout = math.ceil(timeout * 1000) * 0.001
       
   464                 timeout = float(timeout)
       
   465             else:
       
   466                 timeout = -1.0  # epoll.poll() must have a float.
       
   467 
       
   468             # We always want at least 1 to ensure that select can be called
       
   469             # with no file descriptors registered. Otherwise will fail.
       
   470             max_events = max(len(self._fd_to_key), 1)
       
   471 
       
   472             ready = []
       
   473             fd_events = _syscall_wrapper(self._epoll.poll, True,
       
   474                                          timeout=timeout,
       
   475                                          maxevents=max_events)
       
   476             for fd, event_mask in fd_events:
       
   477                 events = 0
       
   478                 if event_mask & ~select.EPOLLIN:
       
   479                     events |= EVENT_WRITE
       
   480                 if event_mask & ~select.EPOLLOUT:
       
   481                     events |= EVENT_READ
       
   482 
       
   483                 key = self._key_from_fd(fd)
       
   484                 if key:
       
   485                     ready.append((key, events & key.events))
       
   486             return ready
       
   487 
       
   488         def close(self):
       
   489             self._epoll.close()
       
   490             super(EpollSelector, self).close()
       
   491 
       
   492     __all__.append('EpollSelector')
       
   493 
       
   494 
       
   495 if hasattr(select, "devpoll"):
       
   496     class DevpollSelector(BaseSelector):
       
   497         """Solaris /dev/poll selector."""
       
   498 
       
   499         def __init__(self):
       
   500             super(DevpollSelector, self).__init__()
       
   501             self._devpoll = select.devpoll()
       
   502 
       
   503         def fileno(self):
       
   504             return self._devpoll.fileno()
       
   505 
       
   506         def register(self, fileobj, events, data=None):
       
   507             key = super(DevpollSelector, self).register(fileobj, events, data)
       
   508             poll_events = 0
       
   509             if events & EVENT_READ:
       
   510                 poll_events |= select.POLLIN
       
   511             if events & EVENT_WRITE:
       
   512                 poll_events |= select.POLLOUT
       
   513             self._devpoll.register(key.fd, poll_events)
       
   514             return key
       
   515 
       
   516         def unregister(self, fileobj):
       
   517             key = super(DevpollSelector, self).unregister(fileobj)
       
   518             self._devpoll.unregister(key.fd)
       
   519             return key
       
   520 
       
   521         def _wrap_poll(self, timeout=None):
       
   522             """ Wrapper function for select.poll.poll() so that
       
   523             _syscall_wrapper can work with only seconds. """
       
   524             if timeout is not None:
       
   525                 if timeout <= 0:
       
   526                     timeout = 0
       
   527                 else:
       
   528                     # select.devpoll.poll() has a resolution of 1 millisecond,
       
   529                     # round away from zero to wait *at least* timeout seconds.
       
   530                     timeout = math.ceil(timeout * 1000)
       
   531 
       
   532             result = self._devpoll.poll(timeout)
       
   533             return result
       
   534 
       
   535         def select(self, timeout=None):
       
   536             ready = []
       
   537             fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
       
   538             for fd, event_mask in fd_events:
       
   539                 events = 0
       
   540                 if event_mask & ~select.POLLIN:
       
   541                     events |= EVENT_WRITE
       
   542                 if event_mask & ~select.POLLOUT:
       
   543                     events |= EVENT_READ
       
   544 
       
   545                 key = self._key_from_fd(fd)
       
   546                 if key:
       
   547                     ready.append((key, events & key.events))
       
   548 
       
   549             return ready
       
   550 
       
   551         def close(self):
       
   552             self._devpoll.close()
       
   553             super(DevpollSelector, self).close()
       
   554 
       
   555     __all__.append('DevpollSelector')
       
   556 
       
   557 
       
   558 if hasattr(select, "kqueue"):
       
   559     class KqueueSelector(BaseSelector):
       
   560         """ Kqueue / Kevent-based selector """
       
   561         def __init__(self):
       
   562             super(KqueueSelector, self).__init__()
       
   563             self._kqueue = select.kqueue()
       
   564 
       
   565         def fileno(self):
       
   566             return self._kqueue.fileno()
       
   567 
       
   568         def register(self, fileobj, events, data=None):
       
   569             key = super(KqueueSelector, self).register(fileobj, events, data)
       
   570             if events & EVENT_READ:
       
   571                 kevent = select.kevent(key.fd,
       
   572                                        select.KQ_FILTER_READ,
       
   573                                        select.KQ_EV_ADD)
       
   574 
       
   575                 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
       
   576 
       
   577             if events & EVENT_WRITE:
       
   578                 kevent = select.kevent(key.fd,
       
   579                                        select.KQ_FILTER_WRITE,
       
   580                                        select.KQ_EV_ADD)
       
   581 
       
   582                 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
       
   583 
       
   584             return key
       
   585 
       
   586         def unregister(self, fileobj):
       
   587             key = super(KqueueSelector, self).unregister(fileobj)
       
   588             if key.events & EVENT_READ:
       
   589                 kevent = select.kevent(key.fd,
       
   590                                        select.KQ_FILTER_READ,
       
   591                                        select.KQ_EV_DELETE)
       
   592                 try:
       
   593                     _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
       
   594                 except _ERROR_TYPES:
       
   595                     pass
       
   596             if key.events & EVENT_WRITE:
       
   597                 kevent = select.kevent(key.fd,
       
   598                                        select.KQ_FILTER_WRITE,
       
   599                                        select.KQ_EV_DELETE)
       
   600                 try:
       
   601                     _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
       
   602                 except _ERROR_TYPES:
       
   603                     pass
       
   604 
       
   605             return key
       
   606 
       
   607         def select(self, timeout=None):
       
   608             if timeout is not None:
       
   609                 timeout = max(timeout, 0)
       
   610 
       
   611             max_events = len(self._fd_to_key) * 2
       
   612             ready_fds = {}
       
   613 
       
   614             kevent_list = _syscall_wrapper(self._kqueue.control, True,
       
   615                                            None, max_events, timeout)
       
   616 
       
   617             for kevent in kevent_list:
       
   618                 fd = kevent.ident
       
   619                 event_mask = kevent.filter
       
   620                 events = 0
       
   621                 if event_mask == select.KQ_FILTER_READ:
       
   622                     events |= EVENT_READ
       
   623                 if event_mask == select.KQ_FILTER_WRITE:
       
   624                     events |= EVENT_WRITE
       
   625 
       
   626                 key = self._key_from_fd(fd)
       
   627                 if key:
       
   628                     if key.fd not in ready_fds:
       
   629                         ready_fds[key.fd] = (key, events & key.events)
       
   630                     else:
       
   631                         old_events = ready_fds[key.fd][1]
       
   632                         ready_fds[key.fd] = (key, (events | old_events) & key.events)
       
   633 
       
   634             return list(ready_fds.values())
       
   635 
       
   636         def close(self):
       
   637             self._kqueue.close()
       
   638             super(KqueueSelector, self).close()
       
   639 
       
   640     __all__.append('KqueueSelector')
       
   641 
       
   642 
       
   643 def _can_allocate(struct):
       
   644     """ Checks that select structs can be allocated by the underlying
       
   645     operating system, not just advertised by the select module. We don't
       
   646     check select() because we'll be hopeful that most platforms that
       
   647     don't have it available will not advertise it. (ie: GAE) """
       
   648     try:
       
   649         # select.poll() objects won't fail until used.
       
   650         if struct == 'poll':
       
   651             p = select.poll()
       
   652             p.poll(0)
       
   653 
       
   654         # All others will fail on allocation.
       
   655         else:
       
   656             getattr(select, struct)().close()
       
   657         return True
       
   658     except (OSError, AttributeError):
       
   659         return False
       
   660 
       
   661 
       
   662 # Python 3.5 uses a more direct route to wrap system calls to increase speed.
       
   663 if sys.version_info >= (3, 5):
       
   664     def _syscall_wrapper(func, _, *args, **kwargs):
       
   665         """ This is the short-circuit version of the below logic
       
   666         because in Python 3.5+ all selectors restart system calls. """
       
   667         return func(*args, **kwargs)
       
   668 else:
       
   669     def _syscall_wrapper(func, recalc_timeout, *args, **kwargs):
       
   670         """ Wrapper function for syscalls that could fail due to EINTR.
       
   671         All functions should be retried if there is time left in the timeout
       
   672         in accordance with PEP 475. """
       
   673         timeout = kwargs.get("timeout", None)
       
   674         if timeout is None:
       
   675             expires = None
       
   676             recalc_timeout = False
       
   677         else:
       
   678             timeout = float(timeout)
       
   679             if timeout < 0.0:  # Timeout less than 0 treated as no timeout.
       
   680                 expires = None
       
   681             else:
       
   682                 expires = monotonic() + timeout
       
   683 
       
   684         args = list(args)
       
   685         if recalc_timeout and "timeout" not in kwargs:
       
   686             raise ValueError(
       
   687                 "Timeout must be in args or kwargs to be recalculated")
       
   688 
       
   689         result = _SYSCALL_SENTINEL
       
   690         while result is _SYSCALL_SENTINEL:
       
   691             try:
       
   692                 result = func(*args, **kwargs)
       
   693             # OSError is thrown by select.select
       
   694             # IOError is thrown by select.epoll.poll
       
   695             # select.error is thrown by select.poll.poll
       
   696             # Aren't we thankful for Python 3.x rework for exceptions?
       
   697             except (OSError, IOError, select.error) as e:
       
   698                 # select.error wasn't a subclass of OSError in the past.
       
   699                 errcode = None
       
   700                 if hasattr(e, "errno"):
       
   701                     errcode = e.errno
       
   702                 elif hasattr(e, "args"):
       
   703                     errcode = e.args[0]
       
   704 
       
   705                 # Also test for the Windows equivalent of EINTR.
       
   706                 is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and
       
   707                                                            errcode == errno.WSAEINTR))
       
   708 
       
   709                 if is_interrupt:
       
   710                     if expires is not None:
       
   711                         current_time = monotonic()
       
   712                         if current_time > expires:
       
   713                             raise OSError(errno=errno.ETIMEDOUT)
       
   714                         if recalc_timeout:
       
   715                             if "timeout" in kwargs:
       
   716                                 kwargs["timeout"] = expires - current_time
       
   717                     continue
       
   718                 raise
       
   719         return result
       
   720 
       
   721 
       
   722 # Choose the best implementation, roughly:
       
   723 # kqueue == devpoll == epoll > poll > select
       
   724 # select() also can't accept a FD > FD_SETSIZE (usually around 1024)
       
   725 def DefaultSelector():
       
   726     """ This function serves as a first call for DefaultSelector to
       
   727     detect if the select module is being monkey-patched incorrectly
       
   728     by eventlet, greenlet, and preserve proper behavior. """
       
   729     global _DEFAULT_SELECTOR
       
   730     if _DEFAULT_SELECTOR is None:
       
   731         if pycompat.isjython:
       
   732             _DEFAULT_SELECTOR = JythonSelectSelector
       
   733         elif _can_allocate('kqueue'):
       
   734             _DEFAULT_SELECTOR = KqueueSelector
       
   735         elif _can_allocate('devpoll'):
       
   736             _DEFAULT_SELECTOR = DevpollSelector
       
   737         elif _can_allocate('epoll'):
       
   738             _DEFAULT_SELECTOR = EpollSelector
       
   739         elif _can_allocate('poll'):
       
   740             _DEFAULT_SELECTOR = PollSelector
       
   741         elif hasattr(select, 'select'):
       
   742             _DEFAULT_SELECTOR = SelectSelector
       
   743         else:  # Platform-specific: AppEngine
       
   744             raise RuntimeError('Platform does not have a selector.')
       
   745     return _DEFAULT_SELECTOR()