Skip to content

Commit 6284134

Browse files
committed
Merge branch 'loop-exit' into cleanup-handles
2 parents e5a4d6d + b7f1798 commit 6284134

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

newsfragments/80.bugfix.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Previously, cancelling the context surrounding an :func:`open_loop`
2+
block might cause a deadlock in some cases. The ordering of operations
3+
during loop teardown has been improved, so this shouldn't happen
4+
anymore.

trio_asyncio/_base.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -634,10 +634,13 @@ async def _main_loop(self, task_status=trio.TASK_STATUS_IGNORED):
634634
# The shield here ensures that if the context surrounding
635635
# the loop is cancelled, we keep processing callbacks
636636
# until we reach the callback inserted by stop().
637-
# There's a call to stop() in the finally block of
638-
# open_loop(), and we're not shielding the body of the
639-
# open_loop() context, so this should be safe against
640-
# deadlocks.
637+
# That's important to maintain the asyncio invariant
638+
# that everything you schedule before stop() will run
639+
# before the loop stops. In order to be safe against
640+
# deadlocks, it's important that the surrounding
641+
# context ensure that stop() gets called upon a
642+
# cancellation. (open_loop() does this indirectly
643+
# by calling _main_loop_exit().)
641644
with trio.CancelScope(shield=True):
642645
while not self._stopped.is_set():
643646
await self._main_loop_one()
@@ -702,12 +705,19 @@ async def _main_loop_exit(self):
702705
return
703706

704707
with trio.CancelScope(shield=True):
708+
# wait_stopped() will return once _main_loop() exits.
709+
# stop() inserts a callback that will cause such, and
710+
# _main_loop() doesn't block except to wait for new
711+
# callbacks to be added, so this should be deadlock-proof.
705712
self.stop()
706713
await self.wait_stopped()
707714

708715
# Drain all remaining callbacks, even those after an initial
709-
# call to stop(). This avoids a deadlock if stop() was called
710-
# again during unwinding.
716+
# call to stop(). This avoids deadlocks in some cases if
717+
# work is submitted to the loop after the shutdown process
718+
# starts. TODO: figure out precisely what this helps with,
719+
# maybe find a better way. test_wrong_context_manager_order
720+
# deadlocks if we remove it for now.
711721
while True:
712722
try:
713723
await self._main_loop_one(no_wait=True)

0 commit comments

Comments
 (0)