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