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):
|
||||
super().__init__()
|
||||
self._selector = select.kqueue()
|
||||
self._max_events = 0
|
||||
|
||||
def fileno(self):
|
||||
return self._selector.fileno()
|
||||
|
@ -502,10 +503,12 @@ if hasattr(select, 'kqueue'):
|
|||
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
||||
select.KQ_EV_ADD)
|
||||
self._selector.control([kev], 0, 0)
|
||||
self._max_events += 1
|
||||
if events & EVENT_WRITE:
|
||||
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
||||
select.KQ_EV_ADD)
|
||||
self._selector.control([kev], 0, 0)
|
||||
self._max_events += 1
|
||||
except:
|
||||
super().unregister(fileobj)
|
||||
raise
|
||||
|
@ -516,6 +519,7 @@ if hasattr(select, 'kqueue'):
|
|||
if key.events & EVENT_READ:
|
||||
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
||||
select.KQ_EV_DELETE)
|
||||
self._max_events -= 1
|
||||
try:
|
||||
self._selector.control([kev], 0, 0)
|
||||
except OSError:
|
||||
|
@ -525,6 +529,7 @@ if hasattr(select, 'kqueue'):
|
|||
if key.events & EVENT_WRITE:
|
||||
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
||||
select.KQ_EV_DELETE)
|
||||
self._max_events -= 1
|
||||
try:
|
||||
self._selector.control([kev], 0, 0)
|
||||
except OSError:
|
||||
|
@ -537,7 +542,7 @@ if hasattr(select, 'kqueue'):
|
|||
# If max_ev is 0, kqueue will ignore the timeout. For consistent
|
||||
# behavior with the other selector classes, we prevent that here
|
||||
# (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 = []
|
||||
try:
|
||||
kev_list = self._selector.control(None, max_ev, timeout)
|
||||
|
|
|
@ -285,6 +285,35 @@ class BaseSelectorTestCase:
|
|||
|
||||
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):
|
||||
s = self.SELECTOR()
|
||||
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