GeezerVison is showing github.com — gh-110038: KqueueSelector must count all read/write events (#110039) · Glyphack/cpython@4b73ee7
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Lib/selectors.py +6-1Lines changed: 6 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -491,6 +491,7 @@ class KqueueSelector(_BaseSelectorImpl): | 491 | 491 | def __init__(self): | 492 | 492 | super().__init__() | 493 | 493 | self._selector = select.kqueue() | | 494 | + self._max_events = 0 | 494 | 495 |
| 495 | 496 | def fileno(self): | 496 | 497 | return self._selector.fileno() | @@ -502,10 +503,12 @@ def register(self, fileobj, events, data=None): | 502 | 503 | kev = select.kevent(key.fd, select.KQ_FILTER_READ, | 503 | 504 | select.KQ_EV_ADD) | 504 | 505 | self._selector.control([kev], 0, 0) | | 506 | + self._max_events += 1 | 505 | 507 | if events & EVENT_WRITE: | 506 | 508 | kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, | 507 | 509 | select.KQ_EV_ADD) | 508 | 510 | self._selector.control([kev], 0, 0) | | 511 | + self._max_events += 1 | 509 | 512 | except: | 510 | 513 | super().unregister(fileobj) | 511 | 514 | raise | @@ -516,6 +519,7 @@ def unregister(self, fileobj): | 516 | 519 | if key.events & EVENT_READ: | 517 | 520 | kev = select.kevent(key.fd, select.KQ_FILTER_READ, | 518 | 521 | select.KQ_EV_DELETE) | | 522 | + self._max_events -= 1 | 519 | 523 | try: | 520 | 524 | self._selector.control([kev], 0, 0) | 521 | 525 | except OSError: | @@ -525,6 +529,7 @@ def unregister(self, fileobj): | 525 | 529 | if key.events & EVENT_WRITE: | 526 | 530 | kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, | 527 | 531 | select.KQ_EV_DELETE) | | 532 | + self._max_events -= 1 | 528 | 533 | try: | 529 | 534 | self._selector.control([kev], 0, 0) | 530 | 535 | except OSError: | @@ -537,7 +542,7 @@ def select(self, timeout=None): | 537 | 542 | # If max_ev is 0, kqueue will ignore the timeout. For consistent | 538 | 543 | # behavior with the other selector classes, we prevent that here | 539 | 544 | # (using max). See https://bugs.python.org/issue29255 | 540 | | - max_ev = len(self._fd_to_key) or 1 | | 545 | + max_ev = self._max_events or 1 | 541 | 546 | ready = [] | 542 | 547 | try: | 543 | 548 | kev_list = self._selector.control(None, max_ev, timeout) | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_selectors.py +29Lines changed: 29 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -285,6 +285,35 @@ def test_select(self): | 285 | 285 |
| 286 | 286 | self.assertEqual([(wr_key, selectors.EVENT_WRITE)], result) | 287 | 287 |
| | 288 | + def test_select_read_write(self): | | 289 | + # gh-110038: when a file descriptor is registered for both read and | | 290 | + # write, the two events must be seen on a single call to select(). | | 291 | + s = self.SELECTOR() | | 292 | + self.addCleanup(s.close) | | 293 | + | | 294 | + sock1, sock2 = self.make_socketpair() | | 295 | + sock2.send(b"foo") | | 296 | + my_key = s.register(sock1, selectors.EVENT_READ | selectors.EVENT_WRITE) | | 297 | + | | 298 | + seen_read, seen_write = False, False | | 299 | + result = s.select() | | 300 | + # We get the read and write either in the same result entry or in two | | 301 | + # distinct entries with the same key. | | 302 | + self.assertLessEqual(len(result), 2) | | 303 | + for key, events in result: | | 304 | + self.assertTrue(isinstance(key, selectors.SelectorKey)) | | 305 | + self.assertEqual(key, my_key) | | 306 | + self.assertFalse(events & ~(selectors.EVENT_READ | | | 307 | + selectors.EVENT_WRITE)) | | 308 | + if events & selectors.EVENT_READ: | | 309 | + self.assertFalse(seen_read) | | 310 | + seen_read = True | | 311 | + if events & selectors.EVENT_WRITE: | | 312 | + self.assertFalse(seen_write) | | 313 | + seen_write = True | | 314 | + self.assertTrue(seen_read) | | 315 | + self.assertTrue(seen_write) | | 316 | + | 288 | 317 | def test_context_manager(self): | 289 | 318 | s = self.SELECTOR() | 290 | 319 | self.addCleanup(s.close) | |
Collapse file Copy file name to clipboard +3Lines changed: 3 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +Fixed an issue that caused :meth:`KqueueSelector.select` to not return all | | 2 | +the ready events in some cases when a file descriptor is registered for both | | 3 | +read and write. |
|