Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH 2/2] AsynchronousTask: add async_wait() method (bug 653856)
Date: Tue, 24 Apr 2018 03:21:24
Message-Id: 20180424032109.16691-3-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH 0/2] AsynchronousTask: add async_wait() method (bug 653856) by Zac Medico
1 Since the AsynchronousTask.wait() method is prone to event loop
2 recursion, deprecate it, and add an async_wait() method method to
3 replace it. Instead of using task.wait() in order to implicitly run
4 the event loop, now loop.run_until_complete(task.async_wait()) will
5 be used to explicitly run the event loop. This explicit approach will
6 make it more obvious when code will trigger event loop recursion
7 which would not be compatible with asyncio's default event loop.
8
9 Bug: https://bugs.gentoo.org/653856
10 ---
11 pym/_emerge/AsynchronousTask.py | 23 +++++++++++++++++++++++
12 pym/portage/tests/ebuild/test_ipc_daemon.py | 2 +-
13 2 files changed, 24 insertions(+), 1 deletion(-)
14
15 diff --git a/pym/_emerge/AsynchronousTask.py b/pym/_emerge/AsynchronousTask.py
16 index e29324440..a2f1798fe 100644
17 --- a/pym/_emerge/AsynchronousTask.py
18 +++ b/pym/_emerge/AsynchronousTask.py
19 @@ -29,6 +29,26 @@ class AsynchronousTask(SlotObject):
20 self._start_hook()
21 self._start()
22
23 + def async_wait(self):
24 + """
25 + Wait for returncode asynchronously. Notification is available
26 + via the add_done_callback method of the returned Future instance.
27 +
28 + @returns: Future, result is self.returncode
29 + """
30 + waiter = self.scheduler.create_future()
31 + exit_listener = lambda self: waiter.set_result(self.returncode)
32 + self.addExitListener(exit_listener)
33 + waiter.add_done_callback(lambda waiter:
34 + self.removeExitListener(exit_listener) if waiter.cancelled() else None)
35 + if self.returncode is not None:
36 + # If the returncode is None, it means the exit event has already
37 + # happened, so use _async_wait() to guarantee that the exit_listener
38 + # is called. This does not do any harm because a given exit
39 + # listener is never called more than once.
40 + self._async_wait()
41 + return waiter
42 +
43 def _start(self):
44 self.returncode = os.EX_OK
45 self.wait()
46 @@ -47,6 +67,9 @@ class AsynchronousTask(SlotObject):
47 return self.returncode
48
49 def wait(self):
50 + """
51 + Deprecated. Use async_wait() instead.
52 + """
53 if self.returncode is None:
54 if not self._waiting:
55 self._waiting = True
56 diff --git a/pym/portage/tests/ebuild/test_ipc_daemon.py b/pym/portage/tests/ebuild/test_ipc_daemon.py
57 index bc18cdf64..e6da51a76 100644
58 --- a/pym/portage/tests/ebuild/test_ipc_daemon.py
59 +++ b/pym/portage/tests/ebuild/test_ipc_daemon.py
60 @@ -157,6 +157,6 @@ class IpcDaemonTestCase(TestCase):
61 try:
62 task_scheduler.start()
63 event_loop.run_until_complete(self._run_done)
64 - task_scheduler.wait()
65 + event_loop.run_until_complete(task_scheduler.async_wait())
66 finally:
67 timeout_handle.cancel()
68 --
69 2.13.6