aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2020-03-06 00:11:05 -0800
committerZac Medico <zmedico@gentoo.org>2020-03-06 00:55:17 -0800
commit5d476c4e500248929d6b042de302b1e7c923dc60 (patch)
treea7d0d91eb757411c4550171a4b16daf1f8182a53 /lib
parentScheduler: replace add_done_callback with addExitListener (diff)
downloadportage-5d476c4e500248929d6b042de302b1e7c923dc60.tar.gz
portage-5d476c4e500248929d6b042de302b1e7c923dc60.tar.bz2
portage-5d476c4e500248929d6b042de302b1e7c923dc60.zip
AsynchronousTask: handle addExistListener after exit
When addExistListener is called after the task has already exited with a returncode, immediately schedule the listener to be invoked via call_soon. This behavior is similar to the Future add_done_callback method. Signed-off-by: Zac Medico <zmedico@gentoo.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/_emerge/AsynchronousTask.py2
-rw-r--r--lib/portage/tests/util/futures/test_done_callback_after_exit.py40
2 files changed, 42 insertions, 0 deletions
diff --git a/lib/_emerge/AsynchronousTask.py b/lib/_emerge/AsynchronousTask.py
index 799e66a4a..97db02587 100644
--- a/lib/_emerge/AsynchronousTask.py
+++ b/lib/_emerge/AsynchronousTask.py
@@ -176,6 +176,8 @@ class AsynchronousTask(SlotObject):
if self._exit_listeners is None:
self._exit_listeners = []
self._exit_listeners.append(f)
+ if self.returncode is not None:
+ self._wait_hook()
def removeExitListener(self, f):
if self._exit_listeners is not None:
diff --git a/lib/portage/tests/util/futures/test_done_callback_after_exit.py b/lib/portage/tests/util/futures/test_done_callback_after_exit.py
new file mode 100644
index 000000000..46a51c271
--- /dev/null
+++ b/lib/portage/tests/util/futures/test_done_callback_after_exit.py
@@ -0,0 +1,40 @@
+# Copyright 2020 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from _emerge.AsynchronousTask import AsynchronousTask
+from portage.tests import TestCase
+from portage.util.futures import asyncio
+
+
+class DoneCallbackAfterExitTestCase(TestCase):
+
+ def test_done_callback_after_exit(self):
+ """
+ Test that callbacks can be registered via the Future
+ add_done_callback method even after the future is done, and
+ verify that the callbacks are called.
+ """
+ loop = asyncio._wrap_loop()
+ future = loop.create_future()
+ future.set_result(None)
+
+ for i in range(3):
+ event = loop.create_future()
+ future.add_done_callback(lambda future: event.set_result(None))
+ loop.run_until_complete(event)
+
+ def test_exit_listener_after_exit(self):
+ """
+ Test that callbacks can be registered via the AsynchronousTask
+ addExitListener method even after the task is done, and
+ verify that the callbacks are called.
+ """
+ loop = asyncio._wrap_loop()
+ task = AsynchronousTask(scheduler=loop)
+ loop.run_until_complete(task.async_start())
+ loop.run_until_complete(task.async_wait())
+
+ for i in range(3):
+ event = loop.create_future()
+ task.addExitListener(lambda task: event.set_result(None))
+ loop.run_until_complete(event)