@@ -391,20 +391,43 @@ async def async_main(*args):
391
391
"""
392
392
393
393
# 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 :
395
401
loop = TrioEventLoop (queue_len = queue_len )
396
402
old_loop = current_loop .set (loop )
397
403
try :
398
404
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
+ if not loop .is_closed ():
421
+ await loop .synchronize ()
422
+ sync_nursery .cancel_scope .cancel ()
423
+
424
+ await loop .wait_stopped ()
425
+ sync_nursery .cancel_scope .cancel ()
402
426
finally :
403
427
try :
404
428
await loop ._main_loop_exit ()
405
429
finally :
406
430
loop .close ()
407
- nursery .cancel_scope .cancel ()
408
431
current_loop .reset (old_loop )
409
432
410
433
0 commit comments