From 62c7d90b6446288be1a7906581befe3c211fad5f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 May 2017 11:34:44 +0200 Subject: [PATCH] #30014: refactor poll-related classes (#1035) * #30014: refactor poll-related classes so that poll(), epoll() and devpoll() share the same methods for register(), unregister(), close() and select() * remove unused attribute * use specific class attributes instead of select.* constants * have all classes except SelectSelector a _selector attribute * BaseException -> Exception * be explicit in defining a close() method only for selectors which have it * fix AttributeError --- Lib/selectors.py | 222 +++++++++++++++++++---------------------------- 1 file changed, 87 insertions(+), 135 deletions(-) diff --git a/Lib/selectors.py b/Lib/selectors.py index e7bc5177302..f29d11fc6a8 100644 --- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -339,92 +339,86 @@ class SelectSelector(_BaseSelectorImpl): return ready +class _PollLikeSelector(_BaseSelectorImpl): + """Base class shared between poll, epoll and devpoll selectors.""" + _selector_cls = None + + def __init__(self): + super().__init__() + self._selector = self._selector_cls() + + def register(self, fileobj, events, data=None): + key = super().register(fileobj, events, data) + poller_events = 0 + if events & EVENT_READ: + poller_events |= self._EVENT_READ + if events & EVENT_WRITE: + poller_events |= self._EVENT_WRITE + try: + self._selector.register(key.fd, poller_events) + except Exception: + super().unregister(fileobj) + raise + return key + + def unregister(self, fileobj): + key = super().unregister(fileobj) + try: + self._selector.unregister(key.fd) + except OSError: + # This can happen if the FD was closed since it + # was registered. + pass + return key + + def select(self, timeout=None): + # This is shared between poll() and epoll(). + # epoll() has a different signature and handling of timeout parameter. + if timeout is None: + timeout = None + elif timeout <= 0: + timeout = 0 + else: + # poll() has a resolution of 1 millisecond, round away from + # zero to wait *at least* timeout seconds. + timeout = math.ceil(timeout * 1e3) + ready = [] + try: + fd_event_list = self._selector.poll(timeout) + except InterruptedError: + return ready + for fd, event in fd_event_list: + events = 0 + if event & ~self._EVENT_READ: + events |= EVENT_WRITE + if event & ~self._EVENT_WRITE: + events |= EVENT_READ + + key = self._key_from_fd(fd) + if key: + ready.append((key, events & key.events)) + return ready + + if hasattr(select, 'poll'): - class PollSelector(_BaseSelectorImpl): + class PollSelector(_PollLikeSelector): """Poll-based selector.""" - - def __init__(self): - super().__init__() - self._poll = select.poll() - - def register(self, fileobj, events, data=None): - key = super().register(fileobj, events, data) - poll_events = 0 - if events & EVENT_READ: - poll_events |= select.POLLIN - if events & EVENT_WRITE: - poll_events |= select.POLLOUT - self._poll.register(key.fd, poll_events) - return key - - def unregister(self, fileobj): - key = super().unregister(fileobj) - self._poll.unregister(key.fd) - return key - - def select(self, timeout=None): - if timeout is None: - timeout = None - elif timeout <= 0: - timeout = 0 - else: - # poll() has a resolution of 1 millisecond, round away from - # zero to wait *at least* timeout seconds. - timeout = math.ceil(timeout * 1e3) - ready = [] - try: - fd_event_list = self._poll.poll(timeout) - except InterruptedError: - return ready - for fd, event in fd_event_list: - events = 0 - if event & ~select.POLLIN: - events |= EVENT_WRITE - if event & ~select.POLLOUT: - events |= EVENT_READ - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - return ready + _selector_cls = select.poll + _EVENT_READ = select.POLLIN + _EVENT_WRITE = select.POLLOUT if hasattr(select, 'epoll'): - class EpollSelector(_BaseSelectorImpl): + class EpollSelector(_PollLikeSelector): """Epoll-based selector.""" - - def __init__(self): - super().__init__() - self._epoll = select.epoll() + _selector_cls = select.epoll + _EVENT_READ = select.EPOLLIN + _EVENT_WRITE = select.EPOLLOUT def fileno(self): - return self._epoll.fileno() - - def register(self, fileobj, events, data=None): - key = super().register(fileobj, events, data) - epoll_events = 0 - if events & EVENT_READ: - epoll_events |= select.EPOLLIN - if events & EVENT_WRITE: - epoll_events |= select.EPOLLOUT - try: - self._epoll.register(key.fd, epoll_events) - except BaseException: - super().unregister(fileobj) - raise - return key - - def unregister(self, fileobj): - key = super().unregister(fileobj) - try: - self._epoll.unregister(key.fd) - except OSError: - # This can happen if the FD was closed since it - # was registered. - pass - return key + return self._selector.fileno() def select(self, timeout=None): if timeout is None: @@ -443,7 +437,7 @@ if hasattr(select, 'epoll'): ready = [] try: - fd_event_list = self._epoll.poll(timeout, max_ev) + fd_event_list = self._selector.poll(timeout, max_ev) except InterruptedError: return ready for fd, event in fd_event_list: @@ -459,65 +453,23 @@ if hasattr(select, 'epoll'): return ready def close(self): - self._epoll.close() + self._selector.close() super().close() if hasattr(select, 'devpoll'): - class DevpollSelector(_BaseSelectorImpl): + class DevpollSelector(_PollLikeSelector): """Solaris /dev/poll selector.""" - - def __init__(self): - super().__init__() - self._devpoll = select.devpoll() + _selector_cls = select.devpoll + _EVENT_READ = select.POLLIN + _EVENT_WRITE = select.POLLOUT def fileno(self): - return self._devpoll.fileno() - - def register(self, fileobj, events, data=None): - key = super().register(fileobj, events, data) - poll_events = 0 - if events & EVENT_READ: - poll_events |= select.POLLIN - if events & EVENT_WRITE: - poll_events |= select.POLLOUT - self._devpoll.register(key.fd, poll_events) - return key - - def unregister(self, fileobj): - key = super().unregister(fileobj) - self._devpoll.unregister(key.fd) - return key - - def select(self, timeout=None): - if timeout is None: - timeout = None - elif timeout <= 0: - timeout = 0 - else: - # devpoll() has a resolution of 1 millisecond, round away from - # zero to wait *at least* timeout seconds. - timeout = math.ceil(timeout * 1e3) - ready = [] - try: - fd_event_list = self._devpoll.poll(timeout) - except InterruptedError: - return ready - for fd, event in fd_event_list: - events = 0 - if event & ~select.POLLIN: - events |= EVENT_WRITE - if event & ~select.POLLOUT: - events |= EVENT_READ - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - return ready + return self._selector.fileno() def close(self): - self._devpoll.close() + self._selector.close() super().close() @@ -528,10 +480,10 @@ if hasattr(select, 'kqueue'): def __init__(self): super().__init__() - self._kqueue = select.kqueue() + self._selector = select.kqueue() def fileno(self): - return self._kqueue.fileno() + return self._selector.fileno() def register(self, fileobj, events, data=None): key = super().register(fileobj, events, data) @@ -539,12 +491,12 @@ if hasattr(select, 'kqueue'): if events & EVENT_READ: kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) + self._selector.control([kev], 0, 0) if events & EVENT_WRITE: kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_ADD) - self._kqueue.control([kev], 0, 0) - except BaseException: + self._selector.control([kev], 0, 0) + except Exception: super().unregister(fileobj) raise return key @@ -555,7 +507,7 @@ if hasattr(select, 'kqueue'): kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_DELETE) try: - self._kqueue.control([kev], 0, 0) + self._selector.control([kev], 0, 0) except OSError: # This can happen if the FD was closed since it # was registered. @@ -564,7 +516,7 @@ if hasattr(select, 'kqueue'): kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_DELETE) try: - self._kqueue.control([kev], 0, 0) + self._selector.control([kev], 0, 0) except OSError: # See comment above. pass @@ -575,7 +527,7 @@ if hasattr(select, 'kqueue'): max_ev = len(self._fd_to_key) ready = [] try: - kev_list = self._kqueue.control(None, max_ev, timeout) + kev_list = self._selector.control(None, max_ev, timeout) except InterruptedError: return ready for kev in kev_list: @@ -593,7 +545,7 @@ if hasattr(select, 'kqueue'): return ready def close(self): - self._kqueue.close() + self._selector.close() super().close()