summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavide Rizzo <sorcio@gmail.com>2023-10-02 15:42:55 (GMT)
committerGitHub <noreply@github.com>2023-10-02 15:42:55 (GMT)
commitc7941034171cd608a0235731832b7784200dec70 (patch)
treecd9ea92c6b466a54c77e73d67cbd0d84269cea18
parent4bdcb99469ed4d9e4ab2ef3586985a081a18b2bb (diff)
downloadcpython-c7941034171cd608a0235731832b7784200dec70.zip
cpython-c7941034171cd608a0235731832b7784200dec70.tar.gz
cpython-c7941034171cd608a0235731832b7784200dec70.tar.bz2
[3.12] gh-110038: KqueueSelector must count all read/write events (GH-110039) (#110043)
[3.12] gh-110038: KqueueSelector must count all read/write events (GH-110039). (cherry picked from commit b14f0ab51cb4851b25935279617e388456dcf716)
-rw-r--r--Lib/selectors.py7
-rw-r--r--Lib/test/test_selectors.py29
-rw-r--r--Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst3
3 files changed, 38 insertions, 1 deletions
diff --git a/Lib/selectors.py b/Lib/selectors.py
index af6a4f9..c3b065b 100644
--- a/Lib/selectors.py
+++ b/Lib/selectors.py
@@ -509,6 +509,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()
@@ -520,10 +521,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
@@ -534,6 +537,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:
@@ -543,6 +547,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:
@@ -555,7 +560,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 = max(len(self._fd_to_key), 1)
+ max_ev = self._max_events or 1
ready = []
try:
kev_list = self._selector.control(None, max_ev, timeout)
diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py
index 12ecc50..3175720 100644
--- a/Lib/test/test_selectors.py
+++ b/Lib/test/test_selectors.py
@@ -279,6 +279,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)
diff --git a/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst b/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst
new file mode 100644
index 0000000..6b2abd8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-09-28-18-50-33.gh-issue-110038.nx_gCu.rst
@@ -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.