commit:     bca4f6a58512471cdf1caf644cee9858ca3bd1eb
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Thu Apr 26 09:51:43 2018 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Sun Apr 29 04:21:56 2018 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=bca4f6a5

AsynchronousTask: disable event loop recursion (bug 653856)

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. Subclasses that used the _wait()
method to perform cleanup now use the _async_wait() method instead.

Bug: https://bugs.gentoo.org/653856

 pym/_emerge/AbstractEbuildProcess.py |  5 ++++-
 pym/_emerge/AsynchronousTask.py      | 24 ++++++++++++++----------
 2 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/pym/_emerge/AbstractEbuildProcess.py 
b/pym/_emerge/AbstractEbuildProcess.py
index 1012ce166..b10aa4bfa 100644
--- a/pym/_emerge/AbstractEbuildProcess.py
+++ b/pym/_emerge/AbstractEbuildProcess.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2012 Gentoo Foundation
+# Copyright 1999-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
@@ -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 9d8df7f5e..246895d71 100644
--- a/pym/_emerge/AsynchronousTask.py
+++ b/pym/_emerge/AsynchronousTask.py
@@ -1,9 +1,10 @@
-# Copyright 1999-2012 Gentoo Foundation
+# Copyright 1999-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 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
 
@@ -68,15 +68,19 @@ class AsynchronousTask(SlotObject):
 
        def wait(self):
                """
-               Deprecated. Use async_wait() instead.
+               Wait for the returncode attribute to become ready, and return
+               it. If the returncode is not ready and the event loop is already
+               running, then the async_wait() method should be used instead of
+               wait(), because wait() will raise asyncio.InvalidStateError in
+               this case.
+
+               @rtype: int
+               @returns: the value of self.returncode
                """
                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
 

Reply via email to