1 |
commit: bca4f6a58512471cdf1caf644cee9858ca3bd1eb |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Thu Apr 26 09:51:43 2018 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Sun Apr 29 04:21:56 2018 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=bca4f6a5 |
7 |
|
8 |
AsynchronousTask: disable event loop recursion (bug 653856) |
9 |
|
10 |
Make the wait() and _async_wait() methods raise InvalidStateError |
11 |
when the event loop is running and the returncode is not available, |
12 |
since these cases would trigger event loop recursion. There are no |
13 |
known remaining cases that cause event loop recursion via wait() |
14 |
and _async_wait(), and this patch protects against changes that would |
15 |
accidentally re-introduce event loop recursion. |
16 |
|
17 |
Since the wait() method now raises InvalidStateError in cases where |
18 |
it previously would have called the _wait() method, this patch makes |
19 |
it possible to remove the _wait() method implementations from all |
20 |
subclasses of AsynchronousTask. Subclasses that used the _wait() |
21 |
method to perform cleanup now use the _async_wait() method instead. |
22 |
|
23 |
Bug: https://bugs.gentoo.org/653856 |
24 |
|
25 |
pym/_emerge/AbstractEbuildProcess.py | 5 ++++- |
26 |
pym/_emerge/AsynchronousTask.py | 24 ++++++++++++++---------- |
27 |
2 files changed, 18 insertions(+), 11 deletions(-) |
28 |
|
29 |
diff --git a/pym/_emerge/AbstractEbuildProcess.py b/pym/_emerge/AbstractEbuildProcess.py |
30 |
index 1012ce166..b10aa4bfa 100644 |
31 |
--- a/pym/_emerge/AbstractEbuildProcess.py |
32 |
+++ b/pym/_emerge/AbstractEbuildProcess.py |
33 |
@@ -1,4 +1,4 @@ |
34 |
-# Copyright 1999-2012 Gentoo Foundation |
35 |
+# Copyright 1999-2018 Gentoo Foundation |
36 |
# Distributed under the terms of the GNU General Public License v2 |
37 |
|
38 |
import errno |
39 |
@@ -18,6 +18,7 @@ from portage.localization import _ |
40 |
from portage.package.ebuild._ipc.ExitCommand import ExitCommand |
41 |
from portage.package.ebuild._ipc.QueryCommand import QueryCommand |
42 |
from portage import shutil, os |
43 |
+from portage.util.futures import asyncio |
44 |
from portage.util._pty import _create_pty_or_pipe |
45 |
from portage.util import apply_secpass_permissions |
46 |
|
47 |
@@ -420,6 +421,8 @@ class AbstractEbuildProcess(SpawnProcess): |
48 |
if self._build_dir is None: |
49 |
SpawnProcess._async_wait(self) |
50 |
elif self._build_dir_unlock is None: |
51 |
+ if self.returncode is None: |
52 |
+ raise asyncio.InvalidStateError('Result is not ready.') |
53 |
self._async_unlock_builddir(returncode=self.returncode) |
54 |
|
55 |
def _async_unlock_builddir(self, returncode=None): |
56 |
|
57 |
diff --git a/pym/_emerge/AsynchronousTask.py b/pym/_emerge/AsynchronousTask.py |
58 |
index 9d8df7f5e..246895d71 100644 |
59 |
--- a/pym/_emerge/AsynchronousTask.py |
60 |
+++ b/pym/_emerge/AsynchronousTask.py |
61 |
@@ -1,9 +1,10 @@ |
62 |
-# Copyright 1999-2012 Gentoo Foundation |
63 |
+# Copyright 1999-2018 Gentoo Foundation |
64 |
# Distributed under the terms of the GNU General Public License v2 |
65 |
|
66 |
import signal |
67 |
|
68 |
from portage import os |
69 |
+from portage.util.futures import asyncio |
70 |
from portage.util.SlotObject import SlotObject |
71 |
|
72 |
class AsynchronousTask(SlotObject): |
73 |
@@ -17,8 +18,7 @@ class AsynchronousTask(SlotObject): |
74 |
""" |
75 |
|
76 |
__slots__ = ("background", "cancelled", "returncode", "scheduler") + \ |
77 |
- ("_exit_listeners", "_exit_listener_stack", "_start_listeners", |
78 |
- "_waiting") |
79 |
+ ("_exit_listeners", "_exit_listener_stack", "_start_listeners") |
80 |
|
81 |
_cancelled_returncode = - signal.SIGINT |
82 |
|
83 |
@@ -68,15 +68,19 @@ class AsynchronousTask(SlotObject): |
84 |
|
85 |
def wait(self): |
86 |
""" |
87 |
- Deprecated. Use async_wait() instead. |
88 |
+ Wait for the returncode attribute to become ready, and return |
89 |
+ it. If the returncode is not ready and the event loop is already |
90 |
+ running, then the async_wait() method should be used instead of |
91 |
+ wait(), because wait() will raise asyncio.InvalidStateError in |
92 |
+ this case. |
93 |
+ |
94 |
+ @rtype: int |
95 |
+ @returns: the value of self.returncode |
96 |
""" |
97 |
if self.returncode is None: |
98 |
- if not self._waiting: |
99 |
- self._waiting = True |
100 |
- try: |
101 |
- self._wait() |
102 |
- finally: |
103 |
- self._waiting = False |
104 |
+ if self.scheduler.is_running(): |
105 |
+ raise asyncio.InvalidStateError('Result is not ready.') |
106 |
+ self.scheduler.run_until_complete(self.async_wait()) |
107 |
self._wait_hook() |
108 |
return self.returncode |