bpo-30014: make poll-like selector's modify() method faster (#1030)
* #30014: make selectors.DefaultSelector.modify() faster by relying on selector's modify() method instead of un/register()ing the fd * #30014: add unit test * speedup poll/epoll/devpoll modify() method by using internal modify() call * update doc * address PR comments * update NEWS entries * use != instead of 'is not'
This commit is contained in:
parent
894a654a9c
commit
fbfaa6fd57
|
@ -234,6 +234,9 @@ Optimizations
|
|||
expressions <re>`. Searching some patterns can now be up to 20 times faster.
|
||||
(Contributed by Serhiy Storchaka in :issue:`30285`.)
|
||||
|
||||
* :meth:`selectors.EpollSelector.modify`, :meth:`selectors.PollSelector.modify`
|
||||
and :meth:`selectors.DevpollSelector.modify` may be around 10% faster under
|
||||
heavy loads. (Contributed by Giampaolo Rodola' in :issue:`30014`)
|
||||
|
||||
Build and C API Changes
|
||||
=======================
|
||||
|
|
|
@ -252,7 +252,6 @@ class _BaseSelectorImpl(BaseSelector):
|
|||
return key
|
||||
|
||||
def modify(self, fileobj, events, data=None):
|
||||
# TODO: Subclasses can probably optimize this even further.
|
||||
try:
|
||||
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
|
||||
except KeyError:
|
||||
|
@ -342,6 +341,8 @@ class SelectSelector(_BaseSelectorImpl):
|
|||
class _PollLikeSelector(_BaseSelectorImpl):
|
||||
"""Base class shared between poll, epoll and devpoll selectors."""
|
||||
_selector_cls = None
|
||||
_EVENT_READ = None
|
||||
_EVENT_WRITE = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
@ -371,6 +372,33 @@ class _PollLikeSelector(_BaseSelectorImpl):
|
|||
pass
|
||||
return key
|
||||
|
||||
def modify(self, fileobj, events, data=None):
|
||||
try:
|
||||
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
|
||||
except KeyError:
|
||||
raise KeyError(f"{fileobj!r} is not registered") from None
|
||||
|
||||
changed = False
|
||||
if events != key.events:
|
||||
selector_events = 0
|
||||
if events & EVENT_READ:
|
||||
selector_events |= self._EVENT_READ
|
||||
if events & EVENT_WRITE:
|
||||
selector_events |= self._EVENT_WRITE
|
||||
try:
|
||||
self._selector.modify(key.fd, selector_events)
|
||||
except Exception:
|
||||
super().unregister(fileobj)
|
||||
raise
|
||||
changed = True
|
||||
if data != key.data:
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
key = key._replace(events=events, data=data)
|
||||
self._fd_to_key[key.fd] = key
|
||||
return key
|
||||
|
||||
def select(self, timeout=None):
|
||||
# This is shared between poll() and epoll().
|
||||
# epoll() has a different signature and handling of timeout parameter.
|
||||
|
|
|
@ -175,6 +175,33 @@ class BaseSelectorTestCase(unittest.TestCase):
|
|||
self.assertFalse(s.register.called)
|
||||
self.assertFalse(s.unregister.called)
|
||||
|
||||
def test_modify_unregister(self):
|
||||
# Make sure the fd is unregister()ed in case of error on
|
||||
# modify(): http://bugs.python.org/issue30014
|
||||
if self.SELECTOR.__name__ == 'EpollSelector':
|
||||
patch = unittest.mock.patch(
|
||||
'selectors.EpollSelector._selector_cls')
|
||||
elif self.SELECTOR.__name__ == 'PollSelector':
|
||||
patch = unittest.mock.patch(
|
||||
'selectors.PollSelector._selector_cls')
|
||||
elif self.SELECTOR.__name__ == 'DevpollSelector':
|
||||
patch = unittest.mock.patch(
|
||||
'selectors.DevpollSelector._selector_cls')
|
||||
else:
|
||||
raise self.skipTest("")
|
||||
|
||||
with patch as m:
|
||||
m.return_value.modify = unittest.mock.Mock(
|
||||
side_effect=ZeroDivisionError)
|
||||
s = self.SELECTOR()
|
||||
self.addCleanup(s.close)
|
||||
rd, wr = self.make_socketpair()
|
||||
s.register(rd, selectors.EVENT_READ)
|
||||
self.assertEqual(len(s._map), 1)
|
||||
with self.assertRaises(ZeroDivisionError):
|
||||
s.modify(rd, selectors.EVENT_WRITE)
|
||||
self.assertEqual(len(s._map), 0)
|
||||
|
||||
def test_close(self):
|
||||
s = self.SELECTOR()
|
||||
self.addCleanup(s.close)
|
||||
|
|
|
@ -350,6 +350,9 @@ Extension Modules
|
|||
Library
|
||||
-------
|
||||
|
||||
- bpo-30014: modify() method of poll(), epoll() and devpoll() based classes of
|
||||
selectors module is around 10% faster. Patch by Giampaolo Rodola'.
|
||||
|
||||
- bpo-30418: On Windows, subprocess.Popen.communicate() now also ignore EINVAL
|
||||
on stdin.write() if the child process is still running but closed the pipe.
|
||||
|
||||
|
|
Loading…
Reference in New Issue