From bf6958cdbe2a901bf115f79336aad66e036398f3 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Fri, 5 Nov 2021 10:43:14 -0400 Subject: [PATCH] Handle cancelled-before-proc-created spawn case It's definitely possible to have a nursery spawn task be cancelled before a `trio.Process` handle is ever returned; we now handle this case as a cancelled-during-spawn scenario. Zombie collection logic also is bypassed in this case. --- tractor/_spawn.py | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tractor/_spawn.py b/tractor/_spawn.py index 6ede566..6671c27 100644 --- a/tractor/_spawn.py +++ b/tractor/_spawn.py @@ -230,17 +230,19 @@ async def new_proc( ] cancelled_during_spawn: bool = False + proc: Optional[trio.Process] = None try: - proc = await trio.open_process(spawn_cmd) - - log.runtime(f"Started {proc}") - - # wait for actor to spawn and connect back to us - # channel should have handshake completed by the - # local actor by the time we get a ref to it try: + proc = await trio.open_process(spawn_cmd) + + log.runtime(f"Started {proc}") + + # wait for actor to spawn and connect back to us + # channel should have handshake completed by the + # local actor by the time we get a ref to it event, chan = await actor_nursery._actor.wait_for_peer( subactor.uid) + except trio.Cancelled: cancelled_during_spawn = True # we may cancel before the child connects back in which @@ -320,23 +322,26 @@ async def new_proc( # killing the process too early. log.cancel(f'Hard reap sequence starting for {uid}') - with trio.CancelScope(shield=True): + if proc: + with trio.CancelScope(shield=True): - # don't clobber an ongoing pdb - if cancelled_during_spawn: - # Try again to avoid TTY clobbering. - async with acquire_debug_lock(uid): - with trio.move_on_after(0.5): - await proc.wait() + # don't clobber an ongoing pdb + if cancelled_during_spawn: + # Try again to avoid TTY clobbering. + async with acquire_debug_lock(uid): + with trio.move_on_after(0.5): + await proc.wait() - if is_root_process(): - await maybe_wait_for_debugger() + if is_root_process(): + await maybe_wait_for_debugger() - if proc.poll() is None: - log.cancel(f"Attempting to hard kill {proc}") - await do_hard_kill(proc) + if proc.poll() is None: + log.cancel(f"Attempting to hard kill {proc}") + await do_hard_kill(proc) - log.debug(f"Joined {proc}") + log.debug(f"Joined {proc}") + else: + log.warning(f'Nursery cancelled before sub-proc started') if not cancelled_during_spawn: # pop child entry to indicate we no longer managing this