forked from goodboy/tractor
				
			Fix hard kill in debug mode; only do it when debug lock is empty
							parent
							
								
									5b3894827f
								
							
						
					
					
						commit
						ef89ed947a
					
				| 
						 | 
				
			
			@ -28,6 +28,7 @@ from ._state import (
 | 
			
		|||
    is_root_process,
 | 
			
		||||
    _runtime_vars,
 | 
			
		||||
)
 | 
			
		||||
from ._debug import _global_actor_in_debug
 | 
			
		||||
 | 
			
		||||
from .log import get_logger
 | 
			
		||||
from ._portal import Portal
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +155,26 @@ async def cancel_on_completion(
 | 
			
		|||
        # cancel the process now that we have a final result
 | 
			
		||||
        await portal.cancel_actor()
 | 
			
		||||
 | 
			
		||||
async def do_hard_kill(
 | 
			
		||||
    proc: trio.Process,
 | 
			
		||||
) -> None:
 | 
			
		||||
    # NOTE: this timeout used to do nothing since we were shielding
 | 
			
		||||
    # the ``.wait()`` inside ``new_proc()`` which will pretty much
 | 
			
		||||
    # never release until the process exits, now it acts as
 | 
			
		||||
    # a hard-kill time ultimatum.
 | 
			
		||||
    with trio.move_on_after(3) as cs:
 | 
			
		||||
 | 
			
		||||
        # NOTE: This ``__aexit__()`` shields internally.
 | 
			
		||||
        async with proc:  # calls ``trio.Process.aclose()``
 | 
			
		||||
            log.debug(f"Terminating {proc}")
 | 
			
		||||
 | 
			
		||||
    if cs.cancelled_caught:
 | 
			
		||||
        # XXX: should pretty much never get here unless we have
 | 
			
		||||
        # to move the bits from ``proc.__aexit__()`` out and
 | 
			
		||||
        # into here.
 | 
			
		||||
        log.critical(f"HARD KILLING {proc}")
 | 
			
		||||
        proc.kill()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def do_hard_kill(
 | 
			
		||||
    proc: trio.Process,
 | 
			
		||||
| 
						 | 
				
			
			@ -222,33 +243,31 @@ async def spawn_subactor(
 | 
			
		|||
            # root-processe's ``trio.Process.aclose()`` can clobber
 | 
			
		||||
            # any existing debugger session so we avoid
 | 
			
		||||
            and _runtime_vars['_debug_mode']
 | 
			
		||||
            and _global_actor_in_debug is not None
 | 
			
		||||
        ):
 | 
			
		||||
            # XXX: this is ``trio.Process.aclose()`` minus
 | 
			
		||||
            # the std-streams pre-closing steps and ``Process.kill()``
 | 
			
		||||
            # calls.
 | 
			
		||||
            # XXX: this is ``trio.Process.aclose()`` MINUS the
 | 
			
		||||
            # std-streams pre-closing steps inside ``proc.__aexit__()``
 | 
			
		||||
            # (see below) which incluses a ``Process.kill()`` call
 | 
			
		||||
 | 
			
		||||
            log.critical(
 | 
			
		||||
                "Root process tty is locked in debug mode by "
 | 
			
		||||
                f"{_global_actor_in_debug}. If the console is hanging, you "
 | 
			
		||||
                "may need to trigger a KBI to kill any "
 | 
			
		||||
                "not-fully-initialized" " subprocesses and allow errors "
 | 
			
		||||
                "from `trio` to propagate"
 | 
			
		||||
            )
 | 
			
		||||
            try:
 | 
			
		||||
                # one more graceful wait try can can be cancelled by KBI
 | 
			
		||||
                # sent by user.
 | 
			
		||||
                await proc.wait()
 | 
			
		||||
 | 
			
		||||
            finally:
 | 
			
		||||
                if proc.returncode is None:
 | 
			
		||||
                    # XXX: skip this when in debug and a session might
 | 
			
		||||
                    # still be live
 | 
			
		||||
                    # proc.kill()
 | 
			
		||||
                    with trio.CancelScope(shield=True):
 | 
			
		||||
                        await proc.wait()
 | 
			
		||||
                    # with trio.CancelScope(shield=True):
 | 
			
		||||
                    await do_hard_kill(proc)
 | 
			
		||||
        else:
 | 
			
		||||
            # NOTE: this timeout used to do nothing since we were shielding
 | 
			
		||||
            # the ``.wait()`` inside ``new_proc()`` which will pretty much
 | 
			
		||||
            # never release until the process exits, now it acts as
 | 
			
		||||
            # a hard-kill time ultimatum.
 | 
			
		||||
            with trio.move_on_after(3) as cs:
 | 
			
		||||
 | 
			
		||||
                # NOTE: This ``__aexit__()`` shields internally.
 | 
			
		||||
                async with proc:  # calls ``trio.Process.aclose()``
 | 
			
		||||
                    log.debug(f"Terminating {proc}")
 | 
			
		||||
 | 
			
		||||
            if cs.cancelled_caught:
 | 
			
		||||
                log.critical(f"HARD KILLING {proc}")
 | 
			
		||||
                proc.kill()
 | 
			
		||||
            # with trio.CancelScope(shield=True):
 | 
			
		||||
            await do_hard_kill(proc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def new_proc(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue