8000 Fix from Anthony Baire for CPython issue 19566. · Python-Repository-Hub/asyncio@219dec2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 219dec2

Browse files
committed
Fix from Anthony Baire for CPython issue 19566.
1 parent 0e86c19 commit 219dec2

File tree

3 files changed

+60
-41
lines changed

3 files changed

+60
-41
lines changed

asyncio/unix_events.py

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -440,10 +440,13 @@ def remove_child_handler(self, pid):
440440

441441
raise NotImplementedError()
442442

443-
def set_loop(self, loop):
444-
"""Reattach the watcher to another event loop.
443+
def attach_loop(self, loop):
444+
"""Attach the watcher to an event loop.
445445
446-
Note: loop may be None
446+
If the watcher was previously attached to an event loop, then it is
447+
first detached before attaching to the new loop.
448+
449+
Note: loop may be None.
447450
"""
448451
raise NotImplementedError()
449452

@@ -467,23 +470,19 @@ def __exit__(self, a, b, c):
467470

468471
class BaseChildWatcher(AbstractChildWatcher):
469472

470-
def __init__(self, loop):
473+
def __init__(self):
471474
self._loop = None
472-
self._callbacks = {}
473-
474-
self.set_loop(loop)
475475

476476
def close(self):
477-
self.set_loop(None)
478-
self._callbacks.clear()
477+
self.attach_loop(None)
479478

480479
def _do_waitpid(self, expected_pid):
481480
raise NotImplementedError()
482481

483482
def _do_waitpid_all(self):
484483
raise NotImplementedError()
485484

486-
def set_loop(self, loop):
485+
def attach_loop(self, loop):
487486
assert loop is None or isinstance(loop, events.AbstractEventLoop)
488487

489488
if self._loop is not None:
@@ -497,13 +496,6 @@ def set_loop(self, loop):
497496
# during the switch.
498497
self._do_waitpid_all()
499498

500-
def remove_child_handler(self, pid):
501-
try:
502-
del self._callbacks[pid]
503-
return True
504-
except KeyError:
505-
return False
506-
507499
def _sig_chld(self):
508500
try:
509501
self._do_waitpid_all()
@@ -535,6 +527,14 @@ class SafeChildWatcher(BaseChildWatcher):
535527
big number of children (O(n) each time SIGCHLD is raised)
536528
"""
537529

530+
def __init__(self):
531+
super().__init__()
532+
self._callbacks = {}
533+
534+
def close(self):
535+
self._callbacks.clear()
536+
super().close()
537+
538538
def __enter__(self):
539539
return self
540540

@@ -547,6 +547,13 @@ def add_child_handler(self, pid, callback, *args):
547547
# Prevent a race condition in case the child is already terminated.
548548
self._do_waitpid(pid)
549549

550+
def remove_child_handler(self, pid):
551+
try:
552+
del self._callbacks[pid]
553+
return True
554+
except KeyError:
555+
return False
556+
550557
def _do_waitpid_all(self):
551558

552559
for pid in list(self._callbacks):
@@ -592,16 +599,17 @@ class FastChildWatcher(BaseChildWatcher):
592599
There is no noticeable overhead when handling a big number of children
593600
(O(1) each time a child terminates).
594601
"""
595-
def __init__(self, loop):
596-
super().__init__(loop)
597-
602+
def __init__(self):
603+
super().__init__()
604+
self._callbacks = {}
598605
self._lock = threading.Lock()
599606
self._zombies = {}
600607
self._forks = 0
601608

602609
def close(self):
603-
super().close()
610+
self._callbacks.clear()
604611
self._zombies.clear()
612+
super().close()
605613

606614
def __enter__(self):
607615
with self._lock:
@@ -642,6 +650,13 @@ def add_child_handler(self, pid, callback, *args):
642650
else:
643651
callback(pid, returncode, *args)
644652

653+
def remove_child_handler(self, pid):
654+
try:
655+
del self._callbacks[pid]
656+
return True
657+
except KeyError:
658+
return False
659+
645660
def _do_waitpid_all(self):
646661
# Because of signal coalescing, we must keep calling waitpid() as
647662
# long as we're able to reap a child.
@@ -686,25 +701,24 @@ def __init__(self):
686701
def _init_watcher(self):
687702
with events._lock:
688703
if self._watcher is None: # pragma: no branch
704+
self._watcher = SafeChildWatcher()
689705
if isinstance(threading.current_thread(),
690706
threading._MainThread):
691-
self._watcher = SafeChildWatcher(self._local._loop)
692-
else:
693-
self._watcher = F438 SafeChildWatcher(None)
707+
self._watcher.attach_loop(self._local._loop)
694708

695709
def set_event_loop(self, loop):
696710
"""Set the event loop.
697711
698712
As a side effect, if a child watcher was set before, then calling
699-
.set_event_loop() from the main thread will call .set_loop(loop) on the
700-
child watcher.
713+
.set_event_loop() from the main thread will call .attach_loop(loop) on
714+
the child watcher.
701715
"""
702716

703717
super().set_event_loop(loop)
704718

705719
if self._watcher is not None and \
706720
isinstance(threading.current_thread(), threading._MainThread):
707-
self._watcher.set_loop(loop)
721+
self._watcher.attach_loop(loop)
708722

709723
def get_child_watcher(self):
710724
"""Get the child watcher

tests/test_events.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,9 @@ def test_create_datagram_endpoint(self):
13111311
class UnixEventLoopTestsMixin(EventLoopTestsMixin):
13121312
def setUp(self):
13131313
super().setUp()
1314-
events.set_child_watcher(unix_events.SafeChildWatcher(self.loop))
1314+
watcher = unix_events.SafeChildWatcher()
1315+
watcher.attach_loop(self.loop)
1316+
events.set_child_watcher(watcher)
13151317

13161318
def tearDown(self):
13171319
events.set_child_watcher(None)

tests/test_unix_events.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ def test_not_implemented(self):
687687
self.assertRaises(
688688
NotImplementedError, watcher.remove_child_handler, f)
689689
self.assertRaises(
690-
NotImplementedError, watcher.set_loop, f)
690+
NotImplementedError, watcher.attach_loop, f)
691691
self.assertRaises(
692692
NotImplementedError, watcher.close)
693693
self.assertRaises(
@@ -700,7 +700,7 @@ class BaseChildWatcherTests(unittest.TestCase):
700700

701701
def test_not_implemented(self):
702702
f = unittest.mock.Mock()
703-
watcher = unix_events.BaseChildWatcher(None)
703+
watcher = unix_events.BaseChildWatcher()
704704
self.assertRaises(
705705
NotImplementedError, watcher._do_waitpid, f)
706706

@@ -720,10 +720,13 @@ def setUp(self):
720720

721721
with unittest.mock.patch.object(
722722
self.loop, "add_signal_handler") as self.m_add_signal_handler:
723-
self.watcher = self.create_watcher(self.loop)
723+
self.watcher = self.create_watcher()
724+
self.watcher.attach_loop(self.loop)
724725

725-
def tearDown(self):
726-
ChildWatcherTestsMixin.instance = None
726+
def cleanup():
727+
ChildWatcherTestsMixin.instance = None
728+
729+
self.addCleanup(cleanup)
727730

728731
def waitpid(pid, flags):
729732
self = ChildWatcherTestsMixin.instance
@@ -1334,7 +1337,7 @@ def test_set_loop(
13341337
self.loop,
13351338
"add_signal_handler") as m_new_add_signal_handler:
13361339

1337-
self.watcher.set_loop(self.loop)
1340+
self.watcher.attach_loop(self.loop)
13381341

13391342
m_old_remove_signal_handler.assert_called_once_with(
13401343
signal.SIGCHLD)
@@ -1375,7 +1378,7 @@ def test_set_loop_race_condition(
13751378
with unittest.mock.patch.object(
13761379
old_loop, "remove_signal_handler") as m_remove_signal_handler:
13771380

1378-
self.watcher.set_loop(None)
1381+
self.watcher.attach_loop(None)
13791382

13801383
m_remove_signal_handler.assert_called_once_with(
13811384
signal.SIGCHLD)
@@ -1395,7 +1398,7 @@ def test_set_loop_race_condition(
13951398
with unittest.mock.patch.object(
13961399
self.loop, "add_signal_handler") as m_add_signal_handler:
13971400

1398-
self.watcher.set_loop(self.loop)
1401+
self.watcher.attach_loop(self.loop)
13991402

14001403
m_add_signal_handler.assert_called_once_with(
14011404
signal.SIGCHLD, self.watcher._sig_chld)
@@ -1457,13 +1460,13 @@ def test_close(
14571460

14581461

14591462
class SafeChildWatcherTests (ChildWatcherTestsMixin, unittest.TestCase):
1460-
def create_watcher(self, loop):
1461-
return unix_events.SafeChildWatcher(loop)
1463+
def create_watcher(self):
1464+
return unix_events.SafeChildWatcher()
14621465

14631466

14641467
class FastChildWatcherTests (ChildWatcherTestsMixin, unittest.TestCase):
1465-
def create_watcher(self, loop):
1466-
return unix_events.FastChildWatcher(loop)
1468+
def create_watcher(self):
1469+
return unix_events.FastChildWatcher()
14671470

14681471

14691472
class PolicyTests(unittest.TestCase):
@@ -1485,7 +1488,7 @@ def test_get_child_watcher(self):
14851488

14861489
def test_get_child_watcher_after_set(self):
14871490
policy = self.create_policy()
1488-
watcher = unix_events.FastChildWatcher(None)
1491+
watcher = unix_events.FastChildWatcher()
14891492

14901493
policy.set_child_watcher(watcher)
14911494
self.assertIs(policy._watcher, watcher)

0 commit comments

Comments
 (0)
0