diff options
author | Zac Medico <zmedico@gentoo.org> | 2017-03-26 23:44:02 -0700 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2017-03-27 14:40:38 -0700 |
commit | eaf22a6d88ad8e0b7a3a1e21f3234c6b7037018a (patch) | |
tree | 363c50842ec462b6d31d8b023d23f536f2518fac | |
parent | emerge: use asyncio interfaces for spinner during owner lookup (bug 591760) (diff) | |
download | portage-eaf22a6d88ad8e0b7a3a1e21f3234c6b7037018a.tar.gz portage-eaf22a6d88ad8e0b7a3a1e21f3234c6b7037018a.tar.bz2 portage-eaf22a6d88ad8e0b7a3a1e21f3234c6b7037018a.zip |
SpawnProcess: fix event loop recursion in _pipe_logger_exit (bug 613990)
Fix SpawnProcess._pipe_logger_exit to wait for process exit status
asynchronously, in order to avoid event loop recursion. This is
required for asyncio compatibility, and also protects emerge from
exceeding the maximum recursion depth limit like in bug 402335.
X-Gentoo-bug: 613990
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=613990
Acked-by: Brian Dolbec <dolsen@gentoo.org>
-rw-r--r-- | pym/_emerge/SpawnProcess.py | 3 | ||||
-rw-r--r-- | pym/_emerge/SubProcess.py | 23 |
2 files changed, 23 insertions, 3 deletions
diff --git a/pym/_emerge/SpawnProcess.py b/pym/_emerge/SpawnProcess.py index e046640ea..7326254ca 100644 --- a/pym/_emerge/SpawnProcess.py +++ b/pym/_emerge/SpawnProcess.py @@ -170,8 +170,7 @@ class SpawnProcess(SubProcess): def _pipe_logger_exit(self, pipe_logger): self._pipe_logger = None - self._unregister() - self.wait() + self._async_waitpid() def _waitpid_loop(self): SubProcess._waitpid_loop(self) diff --git a/pym/_emerge/SubProcess.py b/pym/_emerge/SubProcess.py index 13d938297..b81cfd5f6 100644 --- a/pym/_emerge/SubProcess.py +++ b/pym/_emerge/SubProcess.py @@ -12,7 +12,7 @@ import errno class SubProcess(AbstractPollTask): __slots__ = ("pid",) + \ - ("_dummy_pipe_fd", "_files", "_reg_id") + ("_dummy_pipe_fd", "_files", "_reg_id", "_waitpid_id") # This is how much time we allow for waitpid to succeed after # we've sent a kill signal to our subprocess. @@ -101,6 +101,23 @@ class SubProcess(AbstractPollTask): return self.returncode + def _async_waitpid(self): + """ + Wait for exit status of self.pid asynchronously, and then + set the returncode and notify exit listeners. This is + prefered over _waitpid_loop, since the synchronous nature + of _waitpid_loop can cause event loop recursion. + """ + if self._waitpid_id is None: + self._waitpid_id = self.scheduler.child_watch_add( + self.pid, self._async_waitpid_cb) + + def _async_waitpid_cb(self, pid, condition, user_data=None): + if pid != self.pid: + raise AssertionError("expected pid %s, got %s" % (self.pid, pid)) + self._set_returncode((pid, condition)) + self.wait() + def _waitpid_loop(self): source_id = self.scheduler.child_watch_add( self.pid, self._waitpid_cb) @@ -129,6 +146,10 @@ class SubProcess(AbstractPollTask): self.scheduler.source_remove(self._reg_id) self._reg_id = None + if self._waitpid_id is not None: + self.scheduler.source_remove(self._waitpid_id) + self._waitpid_id = None + if self._files is not None: for f in self._files.values(): if isinstance(f, int): |