Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/portage/util/_eventloop/asyncio_event_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,8 @@ def _run_until_complete(self, future):
try:
return self._loop.run_until_complete(future)
finally:
self._wakeup_fd = signal.set_wakeup_fd(-1)
try:
self._wakeup_fd = signal.set_wakeup_fd(-1)
except ValueError:
# This is intended to fail when not called in the main thread.
pass
26 changes: 19 additions & 7 deletions lib/portage/util/futures/_asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
portage.proxy.lazyimport.lazyimport(globals(),
'portage.util.futures.unix_events:_PortageEventLoopPolicy',
'portage.util.futures:compat_coroutine@_compat_coroutine',
'portage.util._eventloop.EventLoop:EventLoop@_EventLoop',
)
from portage.util._eventloop.asyncio_event_loop import AsyncioEventLoop as _AsyncioEventLoop
from portage.util._eventloop.global_event_loop import (
Expand Down Expand Up @@ -246,14 +245,27 @@ def _wrap_loop(loop=None):
def _safe_loop():
"""
Return an event loop that's safe to use within the current context.
For portage internal callers, this returns a globally shared event
loop instance. For external API consumers, this constructs a
temporary event loop instance that's safe to use in a non-main
thread (it does not override the global SIGCHLD handler).
For portage internal callers or external API consumers calling from
the main thread, this returns a globally shared event loop instance.

For external API consumers calling from a non-main thread, an
asyncio loop must be registered for the current thread, or else an
error will be raised like this:

RuntimeError: There is no current event loop in thread 'Thread-1'.

In order to avoid this RuntimeError, the external API consumer
is responsible for setting an event loop and managing its lifecycle.
For example, this code will set an event loop for the current thread:

asyncio.set_event_loop(asyncio.new_event_loop())

In order to avoid a ResourceWarning, the caller should also close the
corresponding loop before the current thread terminates.

@rtype: asyncio.AbstractEventLoop (or compatible)
@return: event loop instance
"""
if portage._internal_caller:
if portage._internal_caller or threading.current_thread() is threading.main_thread():
return _global_event_loop()
return _EventLoop(main=False)
return _AsyncioEventLoop()