1 |
On 12/6/20 2:14 PM, Zac Medico wrote: |
2 |
> Make the _safe_loop function return an AsyncioEventLoop instance, |
3 |
> so that the default asyncio event loop implementation will be used |
4 |
> in API consumer threads. This is possible because the underlying |
5 |
> asyncio.get_event_loop() function returns a new event loop for |
6 |
> each thread. The AsyncioEventLoop _run_until_complete method will |
7 |
> now appropriately handle a ValueError from signal.set_wakeup_fd(-1) |
8 |
> if it is not called in the main thread. |
9 |
> |
10 |
> Bug: https://bugs.gentoo.org/758755 |
11 |
> Signed-off-by: Zac Medico <zmedico@g.o> |
12 |
> --- |
13 |
> [PATCH v3] fixed AsyncioEventLoop _run_until_complete method to |
14 |
> handle ValueError from signal.set_wakeup_fd(-1) |
15 |
> |
16 |
> lib/portage/util/_eventloop/asyncio_event_loop.py | 6 +++++- |
17 |
> lib/portage/util/futures/_asyncio/__init__.py | 3 +-- |
18 |
> 2 files changed, 6 insertions(+), 3 deletions(-) |
19 |
> |
20 |
> diff --git a/lib/portage/util/_eventloop/asyncio_event_loop.py b/lib/portage/util/_eventloop/asyncio_event_loop.py |
21 |
> index 836f1c30a..4d7047ae8 100644 |
22 |
> --- a/lib/portage/util/_eventloop/asyncio_event_loop.py |
23 |
> +++ b/lib/portage/util/_eventloop/asyncio_event_loop.py |
24 |
> @@ -121,4 +121,8 @@ class AsyncioEventLoop(_AbstractEventLoop): |
25 |
> try: |
26 |
> return self._loop.run_until_complete(future) |
27 |
> finally: |
28 |
> - self._wakeup_fd = signal.set_wakeup_fd(-1) |
29 |
> + try: |
30 |
> + self._wakeup_fd = signal.set_wakeup_fd(-1) |
31 |
> + except ValueError: |
32 |
> + # This is intended to fail when not called in the main thread. |
33 |
> + pass |
34 |
> diff --git a/lib/portage/util/futures/_asyncio/__init__.py b/lib/portage/util/futures/_asyncio/__init__.py |
35 |
> index a902ad895..12013be00 100644 |
36 |
> --- a/lib/portage/util/futures/_asyncio/__init__.py |
37 |
> +++ b/lib/portage/util/futures/_asyncio/__init__.py |
38 |
> @@ -34,7 +34,6 @@ import portage |
39 |
> portage.proxy.lazyimport.lazyimport(globals(), |
40 |
> 'portage.util.futures.unix_events:_PortageEventLoopPolicy', |
41 |
> 'portage.util.futures:compat_coroutine@_compat_coroutine', |
42 |
> - 'portage.util._eventloop.EventLoop:EventLoop@_EventLoop', |
43 |
> ) |
44 |
> from portage.util._eventloop.asyncio_event_loop import AsyncioEventLoop as _AsyncioEventLoop |
45 |
> from portage.util._eventloop.global_event_loop import ( |
46 |
> @@ -256,4 +255,4 @@ def _safe_loop(): |
47 |
> """ |
48 |
> if portage._internal_caller: |
49 |
> return _global_event_loop() |
50 |
> - return _EventLoop(main=False) |
51 |
> + return _AsyncioEventLoop() |
52 |
> |
53 |
|
54 |
This fails if an event loop has not been created for the current thread: |
55 |
|
56 |
File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop |
57 |
raise RuntimeError('There is no current event loop in thread %r.' |
58 |
RuntimeError: There is no current event loop in thread 'Thread-1'. |
59 |
|
60 |
However, if we automatically instantiate a loop for the current thread |
61 |
then we will be responsible for closing it as well, or else we'll |
62 |
eventually see a ResourceWarning like this: |
63 |
|
64 |
/usr/lib/python3.8/asyncio/base_events.py:654: ResourceWarning: unclosed |
65 |
event loop <_UnixSelectorEventLoop running=False closed=False debug=False> |
66 |
_warn(f"unclosed event loop {self!r}", ResourceWarning, source=self) |
67 |
ResourceWarning: Enable tracemalloc to get the object allocation traceback |
68 |
|
69 |
So, I think it's probably best if we force the API consumer to manage |
70 |
the lifecycle of an asyncio loop for each thread that it uses to call |
71 |
the portage API. |
72 |
-- |
73 |
Thanks, |
74 |
Zac |