aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2018-04-23 19:47:11 -0700
committerZac Medico <zmedico@gentoo.org>2018-04-25 20:19:22 -0700
commit71a5a82313226f7be0d966d49392a53139a96f6b (patch)
tree46255606242047288c1c01d9f01d67950a829180
parentAsynchronousTask: add scheduler attribute (bug 653856) (diff)
downloadportage-71a5a82313226f7be0d966d49392a53139a96f6b.tar.gz
portage-71a5a82313226f7be0d966d49392a53139a96f6b.tar.bz2
portage-71a5a82313226f7be0d966d49392a53139a96f6b.zip
AsynchronousTask: add async_wait() method (bug 653856)
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
-rw-r--r--pym/_emerge/AsynchronousTask.py23
-rw-r--r--pym/portage/tests/ebuild/test_ipc_daemon.py2
2 files changed, 24 insertions, 1 deletions
diff --git a/pym/_emerge/AsynchronousTask.py b/pym/_emerge/AsynchronousTask.py
index e29324440..7d2e6253b 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 not 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()