changeset: 94506:aa88d654d5ee tag: tip user: Victor Stinner date: Wed Feb 04 18:22:06 2015 +0100 files: Lib/selectors.py description: Issue #18932, selectors: Optimize the modify() method of selectors Optimize also register() and unregister() methods of KqueueSelector: only call kqueue.control() once. diff -r 017e7391ab58 -r aa88d654d5ee Lib/selectors.py --- a/Lib/selectors.py Wed Feb 04 08:37:02 2015 -0800 +++ b/Lib/selectors.py Wed Feb 04 18:22:06 2015 +0100 @@ -241,19 +241,26 @@ class _BaseSelectorImpl(BaseSelector): raise KeyError("{!r} is not registered".format(fileobj)) from None return key + @abstractmethod + def _modify(self, oldkey, key): + raise NotImplementedError + def modify(self, fileobj, events, data=None): - # TODO: Subclasses can probably optimize this even further. + fd = self._fileobj_lookup(fileobj) try: - key = self._fd_to_key[self._fileobj_lookup(fileobj)] + oldkey = self._fd_to_key[fd] except KeyError: raise KeyError("{!r} is not registered".format(fileobj)) from None - if events != key.events: - self.unregister(fileobj) - key = self.register(fileobj, events, data) - elif data != key.data: - # Use a shortcut to update the data. - key = key._replace(data=data) + if events != oldkey.events: + oldkey = _BaseSelectorImpl.unregister(self, fileobj) + key = _BaseSelectorImpl.register(self, fileobj, events, data) + self._modify(oldkey, key) + elif data != oldkey.data: + key = SelectorKey(fileobj, fd, events, data) self._fd_to_key[key.fd] = key + else: + # no change + key = oldkey return key def close(self): @@ -300,6 +307,17 @@ class SelectSelector(_BaseSelectorImpl): self._writers.discard(key.fd) return key + def _modify(self, oldkey, key): + fd = key.fd + if key.events & EVENT_READ: + self._readers.add(fd) + else: + self._readers.discard(fd) + if key.events & EVENT_WRITE: + self._writers.add(fd) + else: + self._writers.discard(fd) + if sys.platform == 'win32': def _select(self, r, w, _, timeout=None): r, w, x = select.select(r, w, w, timeout) @@ -353,6 +371,15 @@ if hasattr(select, 'poll'): self._poll.unregister(key.fd) return key + def _modify(self, oldkey, key): + poll_events = 0 + if key.events & EVENT_READ: + poll_events |= select.POLLIN + if key.events & EVENT_WRITE: + poll_events |= select.POLLOUT + self._poll.modify(key.fd, poll_events) + return key + def select(self, timeout=None): if timeout is None: timeout = None @@ -412,6 +439,15 @@ if hasattr(select, 'epoll'): pass return key + def _modify(self, oldkey, key): + epoll_events = 0 + if key.events & EVENT_READ: + epoll_events |= select.EPOLLIN + if key.events & EVENT_WRITE: + epoll_events |= select.EPOLLOUT + self._epoll.modify(key.fd, epoll_events) + return key + def select(self, timeout=None): if timeout is None: timeout = -1 @@ -476,6 +512,15 @@ if hasattr(select, 'devpoll'): self._devpoll.unregister(key.fd) return key + def _modify(self, oldkey, key): + poll_events = 0 + if key.events & EVENT_READ: + poll_events |= select.POLLIN + if key.events & EVENT_WRITE: + poll_events |= select.POLLOUT + self._devpoll.modify(key.fd, poll_events) + return key + def select(self, timeout=None): if timeout is None: timeout = None @@ -521,37 +566,54 @@ if hasattr(select, 'kqueue'): def register(self, fileobj, events, data=None): key = super().register(fileobj, events, data) + kevents = [] if events & EVENT_READ: - kev = select.kevent(key.fd, select.KQ_FILTER_READ, - select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) + kevents.append(select.kevent(key.fd, select.KQ_FILTER_READ, + select.KQ_EV_ADD)) if events & EVENT_WRITE: - kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, - select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) + kevents.append(select.kevent(key.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_ADD)) + if kevents: + self._kqueue.control(kevents, 0, 0) return key def unregister(self, fileobj): key = super().unregister(fileobj) + kevents = [] if key.events & EVENT_READ: - kev = select.kevent(key.fd, select.KQ_FILTER_READ, - select.KQ_EV_DELETE) + kevents.append(select.kevent(key.fd, select.KQ_FILTER_READ, + select.KQ_EV_DELETE)) + if key.events & EVENT_WRITE: + kevents.append(select.kevent(key.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_DELETE)) + if kevents: try: - self._kqueue.control([kev], 0, 0) + self._kqueue.control(kevents, 0, 0) except OSError: # This can happen if the FD was closed since it # was registered. pass + return key + + def _modify(self, oldkey, key): + kevents = [] + if key.events & EVENT_READ: + kevents.append(select.kevent(key.fd, select.KQ_FILTER_READ, + select.KQ_EV_ADD)) + elif oldkey.events & EVENT_READ: + kevents.append(select.kevent(oldkey.fd, select.KQ_FILTER_READ, + select.KQ_EV_DELETE)) if key.events & EVENT_WRITE: - kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, - select.KQ_EV_DELETE) - try: - self._kqueue.control([kev], 0, 0) - except OSError: - # See comment above. - pass + kevents.append(select.kevent(key.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_ADD)) + elif oldkey.events & EVENT_WRITE: + kevents.append(select.kevent(oldkey.fd, select.KQ_FILTER_WRITE, + select.KQ_EV_DELETE)) + if kevents: + self._kqueue.control(kevents, 0, 0) return key + def select(self, timeout=None): timeout = None if timeout is None else max(timeout, 0) max_ev = len(self._fd_to_key)