cpython: eb42adc53923 (original) (raw)
Mercurial > cpython
changeset 87088:eb42adc53923
asyncio: Fix from Anthony Baire for CPython issue 19566 (replaces earlier fix). [#19566]
Guido van Rossum guido@dropbox.com | |
---|---|
date | Wed, 13 Nov 2013 15:50:08 -0800 |
parents | ca909a3728d3 |
children | 99ba1772c469 |
files | Lib/asyncio/unix_events.py Lib/test/test_asyncio/test_events.py Lib/test/test_asyncio/test_unix_events.py |
diffstat | 3 files changed, 60 insertions(+), 42 deletions(-)[+] [-] Lib/asyncio/unix_events.py 69 Lib/test/test_asyncio/test_events.py 4 Lib/test/test_asyncio/test_unix_events.py 29 |
line wrap: on
line diff
--- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -440,10 +440,13 @@ class AbstractChildWatcher: raise NotImplementedError()
Note: loop may be None[](#l1.12)
If the watcher was previously attached to an event loop, then it is[](#l1.13)
first detached before attaching to the new loop.[](#l1.14)
Note: loop may be None.[](#l1.16) """[](#l1.17) raise NotImplementedError()[](#l1.18)
@@ -467,15 +470,11 @@ class AbstractChildWatcher: class BaseChildWatcher(AbstractChildWatcher):
self._callbacks = {}[](#l1.27)
self.set_loop(loop)[](#l1.29)
self.set_loop(None)[](#l1.32)
self._callbacks.clear()[](#l1.33)
self.attach_loop(None)[](#l1.34)
def _do_waitpid(self, expected_pid): raise NotImplementedError() @@ -483,7 +482,7 @@ class BaseChildWatcher(AbstractChildWatc def _do_waitpid_all(self): raise NotImplementedError()
if self._loop is not None: @@ -497,13 +496,6 @@ class BaseChildWatcher(AbstractChildWatc # during the switch. self._do_waitpid_all()
- def remove_child_handler(self, pid):
try:[](#l1.52)
del self._callbacks[pid][](#l1.53)
return True[](#l1.54)
except KeyError:[](#l1.55)
return False[](#l1.56)
- def _sig_chld(self): try: self._do_waitpid_all() @@ -535,6 +527,14 @@ class SafeChildWatcher(BaseChildWatcher) big number of children (O(n) each time SIGCHLD is raised) """
+ def enter(self): return self @@ -547,6 +547,13 @@ class SafeChildWatcher(BaseChildWatcher) # Prevent a race condition in case the child is already terminated. self._do_waitpid(pid)
- def remove_child_handler(self, pid):
try:[](#l1.81)
del self._callbacks[pid][](#l1.82)
return True[](#l1.83)
except KeyError:[](#l1.84)
return False[](#l1.85)
+ def _do_waitpid_all(self): for pid in list(self._callbacks): @@ -592,17 +599,17 @@ class FastChildWatcher(BaseChildWatcher) There is no noticeable overhead when handling a big number of children (O(1) each time a child terminates). """
- def init(self):
super().__init__()[](#l1.96)
self._callbacks = {}[](#l1.97) self._lock = threading.Lock()[](#l1.98) self._zombies = {}[](#l1.99) self._forks = 0[](#l1.100)
# Call base class constructor last because it calls back into[](#l1.101)
# the subclass (set_loop() calls _do_waitpid()).[](#l1.102)
super().__init__(loop)[](#l1.103)
self._callbacks.clear()[](#l1.106)
self._zombies.clear()[](#l1.107) super().close()[](#l1.108)
self._zombies.clear()[](#l1.109)
def enter(self): with self._lock: @@ -643,6 +650,13 @@ class FastChildWatcher(BaseChildWatcher) else: callback(pid, returncode, *args)
- def remove_child_handler(self, pid):
try:[](#l1.118)
del self._callbacks[pid][](#l1.119)
return True[](#l1.120)
except KeyError:[](#l1.121)
return False[](#l1.122)
+ def _do_waitpid_all(self): # Because of signal coalescing, we must keep calling waitpid() as # long as we're able to reap a child. @@ -687,25 +701,24 @@ class _UnixDefaultEventLoopPolicy(events def _init_watcher(self): with events._lock: if self._watcher is None: # pragma: no branch
self._watcher = SafeChildWatcher()[](#l1.131) if isinstance(threading.current_thread(),[](#l1.132) threading._MainThread):[](#l1.133)
self._watcher = SafeChildWatcher(self._local._loop)[](#l1.134)
else:[](#l1.135)
self._watcher = SafeChildWatcher(None)[](#l1.136)
self._watcher.attach_loop(self._local._loop)[](#l1.137)
def set_event_loop(self, loop): """Set the event loop. As a side effect, if a child watcher was set before, then calling
.set_event_loop() from the main thread will call .set_loop(loop) on the[](#l1.143)
child watcher.[](#l1.144)
.set_event_loop() from the main thread will call .attach_loop(loop) on[](#l1.145)
the child watcher.[](#l1.146) """[](#l1.147)
super().set_event_loop(loop) if self._watcher is not None and [](#l1.151) isinstance(threading.current_thread(), threading._MainThread):
self._watcher.set_loop(loop)[](#l1.153)
self._watcher.attach_loop(loop)[](#l1.154)
def get_child_watcher(self): """Get the child watcher
--- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1311,7 +1311,9 @@ else: class UnixEventLoopTestsMixin(EventLoopTestsMixin): def setUp(self): super().setUp()
events.set_child_watcher(unix_events.SafeChildWatcher(self.loop))[](#l2.7)
watcher = unix_events.SafeChildWatcher()[](#l2.8)
watcher.attach_loop(self.loop)[](#l2.9)
events.set_child_watcher(watcher)[](#l2.10)
def tearDown(self): events.set_child_watcher(None)
--- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -687,7 +687,7 @@ class AbstractChildWatcherTests(unittest self.assertRaises( NotImplementedError, watcher.remove_child_handler, f) self.assertRaises(
NotImplementedError, watcher.set_loop, f)[](#l3.7)
NotImplementedError, watcher.attach_loop, f)[](#l3.8) self.assertRaises([](#l3.9) NotImplementedError, watcher.close)[](#l3.10) self.assertRaises([](#l3.11)
@@ -700,7 +700,7 @@ class BaseChildWatcherTests(unittest.Tes def test_not_implemented(self): f = unittest.mock.Mock()
watcher = unix_events.BaseChildWatcher(None)[](#l3.16)
watcher = unix_events.BaseChildWatcher()[](#l3.17) self.assertRaises([](#l3.18) NotImplementedError, watcher._do_waitpid, f)[](#l3.19)
@@ -720,10 +720,13 @@ class ChildWatcherTestsMixin: with unittest.mock.patch.object( self.loop, "add_signal_handler") as self.m_add_signal_handler:
self.watcher = self.create_watcher(self.loop)[](#l3.25)
self.watcher = self.create_watcher()[](#l3.26)
self.watcher.attach_loop(self.loop)[](#l3.27)
def cleanup():[](#l3.31)
ChildWatcherTestsMixin.instance = None[](#l3.32)
self.addCleanup(cleanup)[](#l3.34)
def waitpid(pid, flags): self = ChildWatcherTestsMixin.instance @@ -1334,7 +1337,7 @@ class ChildWatcherTestsMixin: self.loop, "add_signal_handler") as m_new_add_signal_handler:
self.watcher.set_loop(self.loop)[](#l3.42)
self.watcher.attach_loop(self.loop)[](#l3.43)
m_old_remove_signal_handler.assert_called_once_with( signal.SIGCHLD) @@ -1375,7 +1378,7 @@ class ChildWatcherTestsMixin: with unittest.mock.patch.object( old_loop, "remove_signal_handler") as m_remove_signal_handler:
self.watcher.set_loop(None)[](#l3.51)
self.watcher.attach_loop(None)[](#l3.52)
m_remove_signal_handler.assert_called_once_with( signal.SIGCHLD) @@ -1395,7 +1398,7 @@ class ChildWatcherTestsMixin: with unittest.mock.patch.object( self.loop, "add_signal_handler") as m_add_signal_handler:
self.watcher.set_loop(self.loop)[](#l3.60)
self.watcher.attach_loop(self.loop)[](#l3.61)
m_add_signal_handler.assert_called_once_with( signal.SIGCHLD, self.watcher._sig_chld) @@ -1457,13 +1460,13 @@ class ChildWatcherTestsMixin: class SafeChildWatcherTests (ChildWatcherTestsMixin, unittest.TestCase):
class FastChildWatcherTests (ChildWatcherTestsMixin, unittest.TestCase):
class PolicyTests(unittest.TestCase): @@ -1485,7 +1488,7 @@ class PolicyTests(unittest.TestCase): def test_get_child_watcher_after_set(self): policy = self.create_policy()
watcher = unix_events.FastChildWatcher(None)[](#l3.87)
watcher = unix_events.FastChildWatcher()[](#l3.88)
policy.set_child_watcher(watcher) self.assertIs(policy._watcher, watcher)