Make the wait() and _async_wait() methods raise InvalidStateError
when the event loop is running and the returncode is not available,
since these cases would trigger event loop recursion. There are no
known remaining cases that cause event loop recursion via wait()
and _async_wait(), and this patch protects against changes that would
accidentally re-introduce event loop recursion.

Since the wait() method now raises InvalidStateError in cases where
it previously would have called the _wait() method, this patch makes
it possible to remove the _wait() method implementations from all
subclasses of AsynchronousTask.

Bug: https://bugs.gentoo.org/653856
---
 pym/_emerge/AbstractEbuildProcess.py |  3 +++
 pym/_emerge/AsynchronousTask.py      | 15 +++++++--------
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/pym/_emerge/AbstractEbuildProcess.py 
b/pym/_emerge/AbstractEbuildProcess.py
index d481e6046..606c909e8 100644
--- a/pym/_emerge/AbstractEbuildProcess.py
+++ b/pym/_emerge/AbstractEbuildProcess.py
@@ -18,6 +18,7 @@ from portage.localization import _
 from portage.package.ebuild._ipc.ExitCommand import ExitCommand
 from portage.package.ebuild._ipc.QueryCommand import QueryCommand
 from portage import shutil, os
+from portage.util.futures import asyncio
 from portage.util._pty import _create_pty_or_pipe
 from portage.util import apply_secpass_permissions
 
@@ -420,6 +421,8 @@ class AbstractEbuildProcess(SpawnProcess):
                if self._build_dir is None:
                        SpawnProcess._async_wait(self)
                elif self._build_dir_unlock is None:
+                       if self.returncode is None:
+                               raise asyncio.InvalidStateError('Result is not 
ready.')
                        self._async_unlock_builddir(returncode=self.returncode)
 
        def _async_unlock_builddir(self, returncode=None):
diff --git a/pym/_emerge/AsynchronousTask.py b/pym/_emerge/AsynchronousTask.py
index 7d2e6253b..5cc6d3b7d 100644
--- a/pym/_emerge/AsynchronousTask.py
+++ b/pym/_emerge/AsynchronousTask.py
@@ -4,6 +4,7 @@
 import signal
 
 from portage import os
+from portage.util.futures import asyncio
 from portage.util.SlotObject import SlotObject
 
 class AsynchronousTask(SlotObject):
@@ -17,8 +18,7 @@ class AsynchronousTask(SlotObject):
        """
 
        __slots__ = ("background", "cancelled", "returncode", "scheduler") + \
-               ("_exit_listeners", "_exit_listener_stack", "_start_listeners",
-               "_waiting")
+               ("_exit_listeners", "_exit_listener_stack", "_start_listeners")
 
        _cancelled_returncode = - signal.SIGINT
 
@@ -71,12 +71,9 @@ class AsynchronousTask(SlotObject):
                Deprecated. Use async_wait() instead.
                """
                if self.returncode is None:
-                       if not self._waiting:
-                               self._waiting = True
-                               try:
-                                       self._wait()
-                               finally:
-                                       self._waiting = False
+                       if self.scheduler.is_running():
+                               raise asyncio.InvalidStateError('Result is not 
ready.')
+                       self.scheduler.run_until_complete(self.async_wait())
                self._wait_hook()
                return self.returncode
 
@@ -91,6 +88,8 @@ class AsynchronousTask(SlotObject):
                loop recursion (or stack overflow) that synchronous calling of
                exit listeners can cause. This method is thread-safe.
                """
+               if self.returncode is None:
+                       raise asyncio.InvalidStateError('Result is not ready.')
                self.scheduler.idle_add(self._async_wait_cb)
 
        def _async_wait_cb(self):
-- 
2.13.6


Reply via email to