issue #415, #477: Poller must handle POLLHUP too.

Linux will fire poll() with simply the POLLHUP bit set even though it
was not requested, resulting in an infinite loop.
issue510
David Wilson 6 years ago
parent 07f1b9bdd0
commit 19b708e141

@ -1900,8 +1900,10 @@ class Poller(object):
""" """
pass pass
_readmask = select.POLLIN | select.POLLHUP
def _update(self, fd): def _update(self, fd):
mask = (((fd in self._rfds) and select.POLLIN) | mask = (((fd in self._rfds) and self._readmask) |
((fd in self._wfds) and select.POLLOUT)) ((fd in self._wfds) and select.POLLOUT))
if mask: if mask:
self._pollobj.register(fd, mask) self._pollobj.register(fd, mask)
@ -1951,8 +1953,8 @@ class Poller(object):
events, _ = io_op(self._pollobj.poll, timeout) events, _ = io_op(self._pollobj.poll, timeout)
for fd, event in events: for fd, event in events:
if event & select.POLLIN: if event & self._readmask:
_vv and IOLOG.debug('%r: POLLIN for %r', self, fd) _vv and IOLOG.debug('%r: POLLIN|POLLHUP for %r', self, fd)
data, gen = self._rfds.get(fd, (None, None)) data, gen = self._rfds.get(fd, (None, None))
if gen and gen < self._generation: if gen and gen < self._generation:
yield data yield data

@ -13,6 +13,12 @@ import mitogen.parent
import testlib import testlib
try:
next
except NameError:
# Python 2.4
from mitogen.core import next
class SockMixin(object): class SockMixin(object):
def tearDown(self): def tearDown(self):
@ -345,6 +351,22 @@ class FileClosedMixin(PollerMixin, SockMixin):
pass pass
class TtyHangupMixin(PollerMixin):
def test_tty_hangup_detected(self):
# bug in initial select.poll() implementation failed to detect POLLHUP.
master_fd, slave_fd = mitogen.parent.openpty()
try:
self.p.start_receive(master_fd)
self.assertEquals([], list(self.p.poll(0)))
os.close(slave_fd)
slave_fd = None
self.assertEquals([master_fd], list(self.p.poll(0)))
finally:
if slave_fd is not None:
os.close(slave_fd)
os.close(master_fd)
class DistinctDataMixin(PollerMixin, SockMixin): class DistinctDataMixin(PollerMixin, SockMixin):
# Verify different data is yielded for the same FD according to the event # Verify different data is yielded for the same FD according to the event
# being raised. # being raised.
@ -368,29 +390,39 @@ class AllMixin(ReceiveStateMixin,
FileClosedMixin, FileClosedMixin,
DistinctDataMixin, DistinctDataMixin,
PollMixin, PollMixin,
TtyHangupMixin,
CloseMixin): CloseMixin):
""" """
Helper to avoid cutpasting mixin names below. Helper to avoid cutpasting mixin names below.
""" """
@unittest2.skipIf(condition=not hasattr(select, 'select'),
reason='select.select() not supported')
class SelectTest(AllMixin, testlib.TestCase): class SelectTest(AllMixin, testlib.TestCase):
klass = mitogen.core.Poller klass = mitogen.core.Poller
SelectTest = unittest2.skipIf(
condition=not hasattr(select, 'select'),
reason='select.select() not supported'
)(SelectTest)
@unittest2.skipIf(condition=not hasattr(select, 'kqueue'),
reason='select.kqueue() not supported')
class KqueueTest(AllMixin, testlib.TestCase): class KqueueTest(AllMixin, testlib.TestCase):
klass = mitogen.parent.KqueuePoller klass = mitogen.parent.KqueuePoller
KqueueTest = unittest2.skipIf(
condition=not hasattr(select, 'kqueue'),
reason='select.kqueue() not supported'
)(KqueueTest)
@unittest2.skipIf(condition=not hasattr(select, 'epoll'),
reason='select.epoll() not supported')
class EpollTest(AllMixin, testlib.TestCase): class EpollTest(AllMixin, testlib.TestCase):
klass = mitogen.parent.EpollPoller klass = mitogen.parent.EpollPoller
EpollTest = unittest2.skipIf(
condition=not hasattr(select, 'epoll'),
reason='select.epoll() not supported'
)(EpollTest)
if __name__ == '__main__': if __name__ == '__main__':
unittest2.main() unittest2.main()

Loading…
Cancel
Save