Since the AsynchronousTask.wait() method is prone to event loop
recursion, deprecate it, and add an async_wait() method method to
replace it. Instead of using task.wait() in order to implicitly run
the event loop, now loop.run_until_complete(task.async_wait()) will
be used to explicitly run the event loop. This explicit approach will
make it more obvious when code will trigger event loop recursion
which would not be compatible with asyncio's default event loop.

Bug: https://bugs.gentoo.org/653856
---
 pym/_emerge/AsynchronousTask.py             | 23 +++++++++++++++++++++++
 pym/portage/tests/ebuild/test_ipc_daemon.py |  2 +-
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/pym/_emerge/AsynchronousTask.py b/pym/_emerge/AsynchronousTask.py
index e29324440..a2f1798fe 100644
--- a/pym/_emerge/AsynchronousTask.py
+++ b/pym/_emerge/AsynchronousTask.py
@@ -29,6 +29,26 @@ class AsynchronousTask(SlotObject):
                self._start_hook()
                self._start()
 
+       def async_wait(self):
+               """
+               Wait for returncode asynchronously. Notification is available
+               via the add_done_callback method of the returned Future 
instance.
+
+               @returns: Future, result is self.returncode
+               """
+               waiter = self.scheduler.create_future()
+               exit_listener = lambda self: waiter.set_result(self.returncode)
+               self.addExitListener(exit_listener)
+               waiter.add_done_callback(lambda waiter:
+                       self.removeExitListener(exit_listener) if 
waiter.cancelled() else None)
+               if self.returncode is not None:
+                       # If the returncode is None, it means the exit event 
has already
+                       # happened, so use _async_wait() to guarantee that the 
exit_listener
+                       # is called. This does not do any harm because a given 
exit
+                       # listener is never called more than once.
+                       self._async_wait()
+               return waiter
+
        def _start(self):
                self.returncode = os.EX_OK
                self.wait()
@@ -47,6 +67,9 @@ class AsynchronousTask(SlotObject):
                return self.returncode
 
        def wait(self):
+               """
+               Deprecated. Use async_wait() instead.
+               """
                if self.returncode is None:
                        if not self._waiting:
                                self._waiting = True
diff --git a/pym/portage/tests/ebuild/test_ipc_daemon.py 
b/pym/portage/tests/ebuild/test_ipc_daemon.py
index bc18cdf64..e6da51a76 100644
--- a/pym/portage/tests/ebuild/test_ipc_daemon.py
+++ b/pym/portage/tests/ebuild/test_ipc_daemon.py
@@ -157,6 +157,6 @@ class IpcDaemonTestCase(TestCase):
                try:
                        task_scheduler.start()
                        event_loop.run_until_complete(self._run_done)
-                       task_scheduler.wait()
+                       
event_loop.run_until_complete(task_scheduler.async_wait())
                finally:
                        timeout_handle.cancel()
-- 
2.13.6


Reply via email to