commit:     690ac6e78c4099e83b84ef11c0b4064b077a8ef0
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Thu Oct  5 06:27:27 2023 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Thu Oct  5 06:57:11 2023 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=690ac6e7

Eliminate deprecated asyncio.get_child_watcher() usage

Use PidfdChildWatcher if os.pidfd_open is available and works, and
otherwise use ThreadedChildWatcher which should work in any case.

The _ChildWatcherThreadSafetyWrapper class is not needed because
both PidfdChildWatcher and ThreadedChildWatcher use the running
event loop to invoke the child handler callback, and it is safe
to assume that the current AsyncioEventLoop instance is the
running loop when it is used to obtain a child watcher instance.

Bug: https://bugs.gentoo.org/914873
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>

 lib/portage/util/_eventloop/asyncio_event_loop.py | 51 +++++++++++------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/lib/portage/util/_eventloop/asyncio_event_loop.py 
b/lib/portage/util/_eventloop/asyncio_event_loop.py
index 37b5b2706c..fe941b420a 100644
--- a/lib/portage/util/_eventloop/asyncio_event_loop.py
+++ b/lib/portage/util/_eventloop/asyncio_event_loop.py
@@ -6,6 +6,12 @@ import signal
 
 import asyncio as _real_asyncio
 from asyncio.events import AbstractEventLoop as _AbstractEventLoop
+from asyncio.unix_events import ThreadedChildWatcher
+
+try:
+    from asyncio.unix_events import PidfdChildWatcher
+except ImportError:
+    PidfdChildWatcher = None
 
 import portage
 
@@ -90,9 +96,24 @@ class AsyncioEventLoop(_AbstractEventLoop):
         @return: the internal event loop's AbstractChildWatcher interface
         """
         if self._child_watcher is None:
-            self._child_watcher = _ChildWatcherThreadSafetyWrapper(
-                self, _real_asyncio.get_child_watcher()
-            )
+            pidfd_works = False
+            if PidfdChildWatcher is not None and hasattr(os, "pidfd_open"):
+                try:
+                    fd = os.pidfd_open(portage.getpid())
+                except Exception:
+                    pass
+                else:
+                    os.close(fd)
+                    pidfd_works = True
+
+            if pidfd_works:
+                watcher = PidfdChildWatcher()
+            else:
+                watcher = ThreadedChildWatcher()
+
+            watcher.attach_loop(self._loop)
+            self._child_watcher = watcher
+
         return self._child_watcher
 
     @property
@@ -132,27 +153,3 @@ class AsyncioEventLoop(_AbstractEventLoop):
             except ValueError:
                 # This is intended to fail when not called in the main thread.
                 pass
-
-
-class _ChildWatcherThreadSafetyWrapper:
-    def __init__(self, loop, real_watcher):
-        self._loop = loop
-        self._real_watcher = real_watcher
-
-    def close(self):
-        pass
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, a, b, c):
-        pass
-
-    def _child_exit(self, pid, status, callback, *args):
-        self._loop.call_soon_threadsafe(callback, pid, status, *args)
-
-    def add_child_handler(self, pid, callback, *args):
-        self._real_watcher.add_child_handler(pid, self._child_exit, callback, 
*args)
-
-    def remove_child_handler(self, pid):
-        return self._real_watcher.remove_child_handler(pid)

Reply via email to