@@ -391,20 +391,42 @@ async def async_main(*args):
391391 """
392392
393393 # TODO: make sure that there is no asyncio loop already running
394- async with trio .open_nursery () as nursery :
394+
395+ # The trio-asyncio loop can't shut down until all trio_as_aio tasks
396+ # (or others using run_trio) have exited. This is because the
397+ # termination of such a Trio task sets an asyncio future, which
398+ # uses call_soon(), which won't work if the loop is closed.
399+ # So, we use two nested nurseries.
400+ async with trio .open_nursery () as loop_nursery :
395401 loop = TrioEventLoop (queue_len = queue_len )
396402 old_loop = current_loop .set (loop )
397403 try :
398404 loop ._closed = False
399- await loop ._main_loop_init (nursery )
400- await nursery .start (loop ._main_loop )
401- yield loop
405+ async with trio .open_nursery () as tasks_nursery :
406+ await loop ._main_loop_init (tasks_nursery )
407+ await loop_nursery .start (loop ._main_loop )
408+ yield loop
409+ tasks_nursery .cancel_scope .cancel ()
410+
411+ # Allow all submitted run_trio() tasks calls a chance
412+ # to start before the tasks_nursery closes, unless the
413+ # loop stops (due to someone else calling stop())
414+ # before that:
415+ async with trio .open_nursery () as sync_nursery :
416+ sync_nursery .cancel_scope .shield = True
417+
418+ @sync_nursery .start_soon
419+ async def wait_for_sync ():
420+ await loop .synchronize ()
421+ sync_nursery .cancel_scope .cancel ()
422+
423+ await loop .wait_stopped ()
424+ sync_nursery .cancel_scope .cancel ()
402425 finally :
403426 try :
404427 await loop ._main_loop_exit ()
405428 finally :
406429 loop .close ()
407- nursery .cancel_scope .cancel ()
408430 current_loop .reset (old_loop )
409431
410432
0 commit comments