Commit 0c6b4cb0 authored by Jason Madden's avatar Jason Madden

Fix libuv multiplex io watchers from polling too much when one event has been...

Fix libuv multiplex io watchers from polling too much when one event has been completely turned off. Fixes #1144.
parent 34d56d26
......@@ -38,6 +38,10 @@
- Hub objects now include the value of their ``name`` attribute in
their repr.
- Fix libuv io watchers polling for events that only stopped watchers
are interested in, reducing CPU usage. Reported in :issue:`1144` by
wwqgtxx.
1.3a2 (2018-03-06)
==================
......
......@@ -263,6 +263,8 @@ class io(_base.IoMixin, watcher):
assert self._handle is not None
self._watcher_start(self._watcher, self._events, self._watcher_callback)
events = property(_get_events, _set_events)
def _watcher_ffi_start(self):
_dbg("Starting watcher", self, "with events", self._events)
self._watcher_start(self._watcher, self._events, self._watcher_callback)
......@@ -326,8 +328,12 @@ class io(_base.IoMixin, watcher):
self.args = args
watcher = self._watcher_ref
if watcher is not None and not watcher.active:
if watcher is not None:
if not watcher.active:
watcher._io_start()
else:
# Make sure we're in the event mask
watcher._calc_and_update_events()
def stop(self):
_dbg("Stopping IO multiplex watcher for", self.fd,
......@@ -360,6 +366,8 @@ class io(_base.IoMixin, watcher):
lambda self, nv: self._watcher_ref._set_fd(nv))
def _io_maybe_stop(self):
_dbg("IO maybe stop on behalf of multiplex", self, "fd", self._fd, "events", self._events)
self._calc_and_update_events()
for w in self._multiplex_watchers:
if w.callback is not None:
# There's still a reference to it, and it's started,
......@@ -371,11 +379,14 @@ class io(_base.IoMixin, watcher):
def _io_start(self):
_dbg("IO start on behalf of multiplex", self, "fd", self._fd, "events", self._events)
self._calc_and_update_events()
self.start(self._io_callback, pass_events=True)
def _calc_and_update_events(self):
events = 0
for watcher in self._multiplex_watchers:
if watcher.callback is not None:
# Only ask for events that are active.
events |= watcher.events
self._set_events(events)
......
......@@ -69,6 +69,7 @@ from greentest.skipping import skipOnPyPy
from greentest.skipping import skipOnPyPyOnCI
from greentest.skipping import skipOnPyPy3
from greentest.skipping import skipIf
from greentest.skipping import skipOnLibev
from greentest.skipping import skipOnLibuv
from greentest.skipping import skipOnLibuvOnWin
from greentest.skipping import skipOnLibuvOnCI
......
......@@ -269,6 +269,15 @@ if LIBUV:
'test_socket.GeneralModuleTests.test_uknown_socket_family_repr',
]
if RUN_COVERAGE:
disabled_tests += [
# Starting with #1145 this test (actually
# TestTLS_FTPClassMixin) becomes sensitive to timings
# under coverage.
'test_ftplib.TestFTPClass.test_storlines',
]
if sys.platform.startswith('linux'):
disabled_tests += [
......
......@@ -49,6 +49,7 @@ skipOnLibuvOnCI = _do_not_skip
skipOnLibuvOnCIOnPyPy = _do_not_skip
skipOnLibuvOnPyPyOnWin = _do_not_skip
skipOnLibev = _do_not_skip
if sysinfo.WIN:
skipOnWindows = unittest.skip
......@@ -99,3 +100,5 @@ if sysinfo.LIBUV:
skipOnLibuvOnWin = unittest.skip
if sysinfo.PYPY:
skipOnLibuvOnPyPyOnWin = unittest.skip
else:
skipOnLibev = unittest.skip
......@@ -71,6 +71,41 @@ class TestWatchers(unittest.TestCase):
io.start(lambda: None)
io.close()
@greentest.skipOnLibev("libuv-specific")
@greentest.skipOnWindows("Destroying the loop somehow fails")
def test_io_multiplex_events(self):
# pylint:disable=no-member
import socket
sock = socket.socket()
fd = sock.fileno()
read = self.loop.io(fd, core.READ)
write = self.loop.io(fd, core.WRITE)
try:
real_watcher = read._watcher_ref
read.start(lambda: None)
self.assertEqual(real_watcher.events, core.READ)
write.start(lambda: None)
self.assertEqual(real_watcher.events, core.READ | core.WRITE)
write.stop()
self.assertEqual(real_watcher.events, core.READ)
write.start(lambda: None)
self.assertEqual(real_watcher.events, core.READ | core.WRITE)
read.stop()
self.assertEqual(real_watcher.events, core.WRITE)
write.stop()
self.assertEqual(real_watcher.events, 0)
finally:
read.close()
write.close()
sock.close()
def test_timer_constructor(self):
with self.assertRaises(ValueError):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment