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] AsyncioEventLoop: wrap child watcher for thread safety (bug 764905)
Date: Mon, 11 Jan 2021 07:19:53
Message-Id: 20210111071933.231869-1-zmedico@gentoo.org
1 Use a child watcher wrapper to deliver the callbacks via the
2 call_soon_threadsafe method, since documentation for the asycio
3 AbstractChildWatcher class says that callbacks must be thread
4 safe.
5
6 Bug: https://bugs.gentoo.org/764905
7 Signed-off-by: Zac Medico <zmedico@g.o>
8 ---
9 .../util/_eventloop/asyncio_event_loop.py | 30 ++++++++++++++++++-
10 1 file changed, 29 insertions(+), 1 deletion(-)
11
12 diff --git a/lib/portage/util/_eventloop/asyncio_event_loop.py b/lib/portage/util/_eventloop/asyncio_event_loop.py
13 index 4d7047ae8..b77728088 100644
14 --- a/lib/portage/util/_eventloop/asyncio_event_loop.py
15 +++ b/lib/portage/util/_eventloop/asyncio_event_loop.py
16 @@ -6,6 +6,7 @@ import signal
17
18 import asyncio as _real_asyncio
19 from asyncio.events import AbstractEventLoop as _AbstractEventLoop
20 +from asyncio.unix_events import AbstractChildWatcher as _AbstractChildWatcher
21
22 import portage
23
24 @@ -47,6 +48,7 @@ class AsyncioEventLoop(_AbstractEventLoop):
25 self.set_debug = loop.set_debug
26 self.get_debug = loop.get_debug
27 self._wakeup_fd = -1
28 + self._child_watcher = None
29
30 if portage._internal_caller:
31 loop.set_exception_handler(self._internal_caller_exception_handler)
32 @@ -87,7 +89,9 @@ class AsyncioEventLoop(_AbstractEventLoop):
33 @rtype: asyncio.AbstractChildWatcher
34 @return: the internal event loop's AbstractChildWatcher interface
35 """
36 - return _real_asyncio.get_child_watcher()
37 + if self._child_watcher is None:
38 + self._child_watcher = _ChildWatcherThreadSafetyWrapper(self, _real_asyncio.get_child_watcher())
39 + return self._child_watcher
40
41 @property
42 def _asyncio_wrapper(self):
43 @@ -126,3 +130,27 @@ class AsyncioEventLoop(_AbstractEventLoop):
44 except ValueError:
45 # This is intended to fail when not called in the main thread.
46 pass
47 +
48 +
49 +class _ChildWatcherThreadSafetyWrapper(_AbstractChildWatcher):
50 + def __init__(self, loop, real_watcher):
51 + self._loop = loop
52 + self._real_watcher = real_watcher
53 +
54 + def close(self):
55 + pass
56 +
57 + def __enter__(self):
58 + return self
59 +
60 + def __exit__(self, a, b, c):
61 + pass
62 +
63 + def _child_exit(self, pid, status, callback, *args):
64 + self._loop.call_soon_threadsafe(callback, pid, status, *args)
65 +
66 + def add_child_handler(self, pid, callback, *args):
67 + self._real_watcher.add_child_handler(pid, self._child_exit, callback, *args)
68 +
69 + def remove_child_handler(self, pid):
70 + return self._real_watcher.remove_child_handler(pid)
71 --
72 2.26.2