Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: pym/_emerge/
Date: Mon, 27 Mar 2017 21:41:23
Message-Id: 1490650838.eaf22a6d88ad8e0b7a3a1e21f3234c6b7037018a.zmedico@gentoo
1 commit: eaf22a6d88ad8e0b7a3a1e21f3234c6b7037018a
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Mon Mar 27 06:44:02 2017 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Mon Mar 27 21:40:38 2017 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=eaf22a6d
7
8 SpawnProcess: fix event loop recursion in _pipe_logger_exit (bug 613990)
9
10 Fix SpawnProcess._pipe_logger_exit to wait for process exit status
11 asynchronously, in order to avoid event loop recursion. This is
12 required for asyncio compatibility, and also protects emerge from
13 exceeding the maximum recursion depth limit like in bug 402335.
14
15 X-Gentoo-bug: 613990
16 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=613990
17 Acked-by: Brian Dolbec <dolsen <AT> gentoo.org>
18
19 pym/_emerge/SpawnProcess.py | 3 +--
20 pym/_emerge/SubProcess.py | 23 ++++++++++++++++++++++-
21 2 files changed, 23 insertions(+), 3 deletions(-)
22
23 diff --git a/pym/_emerge/SpawnProcess.py b/pym/_emerge/SpawnProcess.py
24 index e046640ea..7326254ca 100644
25 --- a/pym/_emerge/SpawnProcess.py
26 +++ b/pym/_emerge/SpawnProcess.py
27 @@ -170,8 +170,7 @@ class SpawnProcess(SubProcess):
28
29 def _pipe_logger_exit(self, pipe_logger):
30 self._pipe_logger = None
31 - self._unregister()
32 - self.wait()
33 + self._async_waitpid()
34
35 def _waitpid_loop(self):
36 SubProcess._waitpid_loop(self)
37
38 diff --git a/pym/_emerge/SubProcess.py b/pym/_emerge/SubProcess.py
39 index 13d938297..b81cfd5f6 100644
40 --- a/pym/_emerge/SubProcess.py
41 +++ b/pym/_emerge/SubProcess.py
42 @@ -12,7 +12,7 @@ import errno
43 class SubProcess(AbstractPollTask):
44
45 __slots__ = ("pid",) + \
46 - ("_dummy_pipe_fd", "_files", "_reg_id")
47 + ("_dummy_pipe_fd", "_files", "_reg_id", "_waitpid_id")
48
49 # This is how much time we allow for waitpid to succeed after
50 # we've sent a kill signal to our subprocess.
51 @@ -101,6 +101,23 @@ class SubProcess(AbstractPollTask):
52
53 return self.returncode
54
55 + def _async_waitpid(self):
56 + """
57 + Wait for exit status of self.pid asynchronously, and then
58 + set the returncode and notify exit listeners. This is
59 + prefered over _waitpid_loop, since the synchronous nature
60 + of _waitpid_loop can cause event loop recursion.
61 + """
62 + if self._waitpid_id is None:
63 + self._waitpid_id = self.scheduler.child_watch_add(
64 + self.pid, self._async_waitpid_cb)
65 +
66 + def _async_waitpid_cb(self, pid, condition, user_data=None):
67 + if pid != self.pid:
68 + raise AssertionError("expected pid %s, got %s" % (self.pid, pid))
69 + self._set_returncode((pid, condition))
70 + self.wait()
71 +
72 def _waitpid_loop(self):
73 source_id = self.scheduler.child_watch_add(
74 self.pid, self._waitpid_cb)
75 @@ -129,6 +146,10 @@ class SubProcess(AbstractPollTask):
76 self.scheduler.source_remove(self._reg_id)
77 self._reg_id = None
78
79 + if self._waitpid_id is not None:
80 + self.scheduler.source_remove(self._waitpid_id)
81 + self._waitpid_id = None
82 +
83 if self._files is not None:
84 for f in self._files.values():
85 if isinstance(f, int):