Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] AsynchronousTask: disable event loop recursion (bug 653856)
Date: Sat, 28 Apr 2018 14:42:59
Message-Id: 20180428144106.20487-1-zmedico@gentoo.org
1 Make the wait() and _async_wait() methods raise InvalidStateError
2 when the event loop is running and the returncode is not available,
3 since these cases would trigger event loop recursion. There are no
4 known remaining cases that cause event loop recursion via wait()
5 and _async_wait(), and this patch protects against changes that would
6 accidentally re-introduce event loop recursion.
7
8 Since the wait() method now raises InvalidStateError in cases where
9 it previously would have called the _wait() method, this patch makes
10 it possible to remove the _wait() method implementations from all
11 subclasses of AsynchronousTask.
12
13 Bug: https://bugs.gentoo.org/653856
14 ---
15 pym/_emerge/AbstractEbuildProcess.py | 3 +++
16 pym/_emerge/AsynchronousTask.py | 15 +++++++--------
17 2 files changed, 10 insertions(+), 8 deletions(-)
18
19 diff --git a/pym/_emerge/AbstractEbuildProcess.py b/pym/_emerge/AbstractEbuildProcess.py
20 index d481e6046..606c909e8 100644
21 --- a/pym/_emerge/AbstractEbuildProcess.py
22 +++ b/pym/_emerge/AbstractEbuildProcess.py
23 @@ -18,6 +18,7 @@ from portage.localization import _
24 from portage.package.ebuild._ipc.ExitCommand import ExitCommand
25 from portage.package.ebuild._ipc.QueryCommand import QueryCommand
26 from portage import shutil, os
27 +from portage.util.futures import asyncio
28 from portage.util._pty import _create_pty_or_pipe
29 from portage.util import apply_secpass_permissions
30
31 @@ -420,6 +421,8 @@ class AbstractEbuildProcess(SpawnProcess):
32 if self._build_dir is None:
33 SpawnProcess._async_wait(self)
34 elif self._build_dir_unlock is None:
35 + if self.returncode is None:
36 + raise asyncio.InvalidStateError('Result is not ready.')
37 self._async_unlock_builddir(returncode=self.returncode)
38
39 def _async_unlock_builddir(self, returncode=None):
40 diff --git a/pym/_emerge/AsynchronousTask.py b/pym/_emerge/AsynchronousTask.py
41 index 7d2e6253b..5cc6d3b7d 100644
42 --- a/pym/_emerge/AsynchronousTask.py
43 +++ b/pym/_emerge/AsynchronousTask.py
44 @@ -4,6 +4,7 @@
45 import signal
46
47 from portage import os
48 +from portage.util.futures import asyncio
49 from portage.util.SlotObject import SlotObject
50
51 class AsynchronousTask(SlotObject):
52 @@ -17,8 +18,7 @@ class AsynchronousTask(SlotObject):
53 """
54
55 __slots__ = ("background", "cancelled", "returncode", "scheduler") + \
56 - ("_exit_listeners", "_exit_listener_stack", "_start_listeners",
57 - "_waiting")
58 + ("_exit_listeners", "_exit_listener_stack", "_start_listeners")
59
60 _cancelled_returncode = - signal.SIGINT
61
62 @@ -71,12 +71,9 @@ class AsynchronousTask(SlotObject):
63 Deprecated. Use async_wait() instead.
64 """
65 if self.returncode is None:
66 - if not self._waiting:
67 - self._waiting = True
68 - try:
69 - self._wait()
70 - finally:
71 - self._waiting = False
72 + if self.scheduler.is_running():
73 + raise asyncio.InvalidStateError('Result is not ready.')
74 + self.scheduler.run_until_complete(self.async_wait())
75 self._wait_hook()
76 return self.returncode
77
78 @@ -91,6 +88,8 @@ class AsynchronousTask(SlotObject):
79 loop recursion (or stack overflow) that synchronous calling of
80 exit listeners can cause. This method is thread-safe.
81 """
82 + if self.returncode is None:
83 + raise asyncio.InvalidStateError('Result is not ready.')
84 self.scheduler.idle_add(self._async_wait_cb)
85
86 def _async_wait_cb(self):
87 --
88 2.13.6