Handle stopped main event loops in async_to_sync#558
Conversation
|
@andersk Can I get you to look at this example? — I think it's the same issue as #528 but with a better example. I had similar experimenting: def test_async_to_sync_with_stopped_main_loop():
"""
A wrapper built while a loop is running captures that loop. If the loop is
later stopped but not closed (e.g. a shared pytest-asyncio loop between
tests), calling the wrapper from a sync thread must fall back to a new loop
rather than deadlock.
"""
holder = {}
async def inner():
return 42
async def build():
holder["fn"] = async_to_sync(inner)
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(build())
assert not loop.is_running() and not loop.is_closed()
result = {}
def worker():
result["value"] = holder["fn"]()
thread = threading.Thread(target=worker, daemon=True)
thread.start()
thread.join(timeout=5)
assert not thread.is_alive(), "async_to_sync deadlocked on a stopped loop"
assert result["value"] == 42
finally:
loop.close()It looks like the claim in #507 that the In particular, pytest-asyncio reuses the same loop between tests with multiple WDYT? Thanks! |
|
I wonder if the real problem there is that that we capture the loop in If we remove that capture, your example passes, and the only failure in the test suite is the final Lines 278 to 279 in 4d233dd Indeed, this is the only place in the test suite where a captured loop makes it through the top of Perhaps this test’s expectation is wrong? |
|
I drafted #562 for that proposal. I’d be curious if it also prevents the pytest-asyncio deadlocks that people say they’re seeing (I mean I don’t think they’re making it up, but I haven’t seen a real reproduction posted yet). |
Summary
main_event_loopwhen it is still runningA stopped loop can still accept
call_soon_threadsafe()without ever running the scheduled task, which leavesasync_to_sync()blocked waiting for a result.Fixes #545.
Testing
python -m pytest -qpython -m mypy asgiref tests setup.py docs/conf.py