commit: 5d476c4e500248929d6b042de302b1e7c923dc60
Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Fri Mar 6 08:11:05 2020 +0000
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Fri Mar 6 08:55:17 2020 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=5d476c4e
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 <AT> gentoo.org>
lib/_emerge/AsynchronousTask.py | 2 ++
.../util/futures/test_done_callback_after_exit.py | 40 ++++++++++++++++++++++
2 files changed, 42 insertions(+)
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)