A prior commit, aa1ff990, switched away from using get_event_loop *by
default*, but this is not good enough to avoid deprecation warnings as
`asyncio.get_event_loop_policy().get_event_loop()` is *also*
deprecated. Replace this mechanism with explicit calls to
asyncio.get_new_loop() and revise the cleanup mechanisms in __del__ to
match.

Reported-by: Richard W.M. Jones <rjo...@redhat.com>
Reported-by: Daniel P. Berrangé <berra...@redhat.com>
Signed-off-by: John Snow <js...@redhat.com>
cherry picked from commit 21ce2ee4f2df87efe84a27b9c5112487f4670622
Signed-off-by: John Snow <js...@redhat.com>
---
 python/qemu/qmp/legacy.py  | 47 +++++++++++++++++++++++++++-----------
 python/qemu/qmp/qmp_tui.py | 10 ++++++--
 2 files changed, 42 insertions(+), 15 deletions(-)

diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py
index 735d42971e9..775b1fdd3b3 100644
--- a/python/qemu/qmp/legacy.py
+++ b/python/qemu/qmp/legacy.py
@@ -86,13 +86,15 @@ def __init__(self,
                 "server argument should be False when passing a socket")
 
         self._qmp = QMPClient(nickname)
+        self._created_loop = False
 
         try:
             self._aloop = asyncio.get_running_loop()
         except RuntimeError:
-            # No running loop; since this is a sync shim likely to be
-            # used in fully sync programs, create one if neccessary.
-            self._aloop = asyncio.get_event_loop_policy().get_event_loop()
+            # No running loop; since this is a sync shim likely to be used
+            # in sync programs without any event loop at all, create one.
+            self._aloop = asyncio.new_event_loop()
+            self._created_loop = True
 
         self._address = address
         self._timeout: Optional[float] = None
@@ -313,17 +315,36 @@ def send_fd_scm(self, fd: int) -> None:
         self._qmp.send_fd_scm(fd)
 
     def __del__(self) -> None:
-        if self._qmp.runstate == Runstate.IDLE:
-            return
+        if self._qmp.runstate != Runstate.IDLE:
+            self._qmp.logger.warning(
+                "QEMUMonitorProtocol object garbage collected without a prior "
+                "call to close()"
+            )
 
         if not self._aloop.is_running():
-            self.close()
-        else:
-            # Garbage collection ran while the event loop was running.
-            # Nothing we can do about it now, but if we don't raise our
-            # own error, the user will be treated to a lot of traceback
-            # they might not understand.
+            if self._qmp.runstate != Runstate.IDLE:
+                # If the user neglected to close the QMP session and we
+                # are not currently running in an asyncio context, we
+                # have the opportunity to close the QMP session. If we
+                # do not do this, the error messages presented over
+                # dangling async resources may not make any sense to the
+                # user.
+                self.close()
+
+            # If we created our own loop (and we are not running inside
+            # of it), we must close it to avoid warnings and error
+            # messages upon program exit.
+            if self._created_loop:
+                self._aloop.close()
+
+        if self._qmp.runstate != Runstate.IDLE:
+            # If QMP is still not quiesced, it means that the garbage
+            # collector ran from a context within the event loop and we
+            # are simply too late to take any corrective action. Raise
+            # our own error to give meaningful feedback to the user in
+            # order to prevent pages of asyncio stacktrace jargon.
             raise QMPError(
-                "QEMUMonitorProtocol.close()"
-                " was not called before object was garbage collected"
+                "QEMUMonitorProtocol.close() was not called before object was "
+                "garbage collected, and could not be closed due to GC running "
+                "in the event loop"
             )
diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py
index 12bdc17c99e..d5526338f22 100644
--- a/python/qemu/qmp/qmp_tui.py
+++ b/python/qemu/qmp/qmp_tui.py
@@ -161,6 +161,7 @@ def __init__(self, address: Union[str, Tuple[str, int]], 
num_retries: int,
         self.retry_delay = retry_delay if retry_delay else 2
         self.retry: bool = False
         self.exiting: bool = False
+        self._created_loop = False
         super().__init__()
 
     def add_to_history(self, msg: str, level: Optional[str] = None) -> None:
@@ -391,8 +392,9 @@ def run(self, debug: bool = False) -> None:
         try:
             self.aloop = asyncio.get_running_loop()
         except RuntimeError:
-            # No running asyncio event loop. Create one if necessary.
-            self.aloop = asyncio.get_event_loop_policy().get_event_loop()
+            # No running asyncio event loop. Create one.
+            self.aloop = asyncio.new_event_loop()
+            self._created_loop = True
 
         self.aloop.set_debug(debug)
 
@@ -416,6 +418,10 @@ def run(self, debug: bool = False) -> None:
             logging.error('%s\n%s\n', str(err), pretty_traceback())
             raise err
 
+    def __del__(self) -> None:
+        if self._created_loop and self.aloop:
+            self.aloop.close()
+
 
 class StatusBar(urwid.Text):
     """
-- 
2.50.1


Reply via email to