mirror of https://github.com/python/cpython
gh-110038: KqueueSelector must count all read/write events (#110039)
This commit is contained in:
parent
7e0fbf5175
commit
b14f0ab51c
|
@ -491,6 +491,7 @@ if hasattr(select, 'kqueue'):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._selector = select.kqueue()
|
self._selector = select.kqueue()
|
||||||
|
self._max_events = 0
|
||||||
|
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return self._selector.fileno()
|
return self._selector.fileno()
|
||||||
|
@ -502,10 +503,12 @@ if hasattr(select, 'kqueue'):
|
||||||
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
||||||
select.KQ_EV_ADD)
|
select.KQ_EV_ADD)
|
||||||
self._selector.control([kev], 0, 0)
|
self._selector.control([kev], 0, 0)
|
||||||
|
self._max_events += 1
|
||||||
if events & EVENT_WRITE:
|
if events & EVENT_WRITE:
|
||||||
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
||||||
select.KQ_EV_ADD)
|
select.KQ_EV_ADD)
|
||||||
self._selector.control([kev], 0, 0)
|
self._selector.control([kev], 0, 0)
|
||||||
|
self._max_events += 1
|
||||||
except:
|
except:
|
||||||
super().unregister(fileobj)
|
super().unregister(fileobj)
|
||||||
raise
|
raise
|
||||||
|
@ -516,6 +519,7 @@ if hasattr(select, 'kqueue'):
|
||||||
if key.events & EVENT_READ:
|
if key.events & EVENT_READ:
|
||||||
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
||||||
select.KQ_EV_DELETE)
|
select.KQ_EV_DELETE)
|
||||||
|
self._max_events -= 1
|
||||||
try:
|
try:
|
||||||
self._selector.control([kev], 0, 0)
|
self._selector.control([kev], 0, 0)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -525,6 +529,7 @@ if hasattr(select, 'kqueue'):
|
||||||
if key.events & EVENT_WRITE:
|
if key.events & EVENT_WRITE:
|
||||||
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
||||||
select.KQ_EV_DELETE)
|
select.KQ_EV_DELETE)
|
||||||
|
self._max_events -= 1
|
||||||
try:
|
try:
|
||||||
self._selector.control([kev], 0, 0)
|
self._selector.control([kev], 0, 0)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -537,7 +542,7 @@ if hasattr(select, 'kqueue'):
|
||||||
# If max_ev is 0, kqueue will ignore the timeout. For consistent
|
# If max_ev is 0, kqueue will ignore the timeout. For consistent
|
||||||
# behavior with the other selector classes, we prevent that here
|
# behavior with the other selector classes, we prevent that here
|
||||||
# (using max). See https://bugs.python.org/issue29255
|
# (using max). See https://bugs.python.org/issue29255
|
||||||
max_ev = len(self._fd_to_key) or 1
|
max_ev = self._max_events or 1
|
||||||
ready = []
|
ready = []
|
||||||
try:
|
try:
|
||||||
kev_list = self._selector.control(None, max_ev, timeout)
|
kev_list = self._selector.control(None, max_ev, timeout)
|
||||||
|
|
|
@ -285,6 +285,35 @@ class BaseSelectorTestCase:
|
||||||
|
|
||||||
self.assertEqual([(wr_key, selectors.EVENT_WRITE)], result)
|
self.assertEqual([(wr_key, selectors.EVENT_WRITE)], result)
|
||||||
|
|
||||||
|
def test_select_read_write(self):
|
||||||
|
# gh-110038: when a file descriptor is registered for both read and
|
||||||
|
# write, the two events must be seen on a single call to select().
|
||||||
|
s = self.SELECTOR()
|
||||||
|
self.addCleanup(s.close)
|
||||||
|
|
||||||
|
sock1, sock2 = self.make_socketpair()
|
||||||
|
sock2.send(b"foo")
|
||||||
|
my_key = s.register(sock1, selectors.EVENT_READ | selectors.EVENT_WRITE)
|
||||||
|
|
||||||
|
seen_read, seen_write = False, False
|
||||||
|
result = s.select()
|
||||||
|
# We get the read and write either in the same result entry or in two
|
||||||
|
# distinct entries with the same key.
|
||||||
|
self.assertLessEqual(len(result), 2)
|
||||||
|
for key, events in result:
|
||||||
|
self.assertTrue(isinstance(key, selectors.SelectorKey))
|
||||||
|
self.assertEqual(key, my_key)
|
||||||
|
self.assertFalse(events & ~(selectors.EVENT_READ |
|
||||||
|
selectors.EVENT_WRITE))
|
||||||
|
if events & selectors.EVENT_READ:
|
||||||
|
self.assertFalse(seen_read)
|
||||||
|
seen_read = True
|
||||||
|
if events & selectors.EVENT_WRITE:
|
||||||
|
self.assertFalse(seen_write)
|
||||||
|
seen_write = True
|
||||||
|
self.assertTrue(seen_read)
|
||||||
|
self.assertTrue(seen_write)
|
||||||
|
|
||||||
def test_context_manager(self):
|
def test_context_manager(self):
|
||||||
s = self.SELECTOR()
|
s = self.SELECTOR()
|
||||||
self.addCleanup(s.close)
|
self.addCleanup(s.close)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fixed an issue that caused :meth:`KqueueSelector.select` to not return all
|
||||||
|
the ready events in some cases when a file descriptor is registered for both
|
||||||
|
read and write.
|
Loading…
Reference in New Issue