Facepalm: tty locking from root doesn't require an extra task
parent
31c1a32d58
commit
d43d367153
|
@ -1078,7 +1078,6 @@ async def _start_actor(
|
||||||
raise
|
raise
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
actor._service_n.cancel_scope.cancel()
|
|
||||||
await actor.cancel()
|
await actor.cancel()
|
||||||
|
|
||||||
# XXX: the actor is cancelled when this context is complete
|
# XXX: the actor is cancelled when this context is complete
|
||||||
|
|
|
@ -4,9 +4,9 @@ Multi-core debugging for da peeps!
|
||||||
import bdb
|
import bdb
|
||||||
import sys
|
import sys
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from contextlib import asynccontextmanager, contextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing import Awaitable, Tuple, Optional, Callable
|
from typing import Awaitable, Tuple, Optional, Callable
|
||||||
import signal
|
# import signal
|
||||||
|
|
||||||
from async_generator import aclosing
|
from async_generator import aclosing
|
||||||
import tractor
|
import tractor
|
||||||
|
@ -16,6 +16,7 @@ import trio
|
||||||
from .log import get_logger
|
from .log import get_logger
|
||||||
from . import _state
|
from . import _state
|
||||||
from ._discovery import get_root
|
from ._discovery import get_root
|
||||||
|
from ._state import is_root_process
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# wtf: only exported when installed in dev mode?
|
# wtf: only exported when installed in dev mode?
|
||||||
|
@ -35,7 +36,7 @@ __all__ = ['breakpoint', 'post_mortem']
|
||||||
# placeholder for function to set a ``trio.Event`` on debugger exit
|
# placeholder for function to set a ``trio.Event`` on debugger exit
|
||||||
_pdb_release_hook: Optional[Callable] = None
|
_pdb_release_hook: Optional[Callable] = None
|
||||||
|
|
||||||
# actor-wide flag
|
# actor-wide variable pointing to current task name using debugger
|
||||||
_in_debug = False
|
_in_debug = False
|
||||||
|
|
||||||
# lock in root actor preventing multi-access to local tty
|
# lock in root actor preventing multi-access to local tty
|
||||||
|
@ -120,14 +121,15 @@ async def _acquire_debug_lock():
|
||||||
"""Acquire a actor local FIFO lock meant to mutex entry to a local
|
"""Acquire a actor local FIFO lock meant to mutex entry to a local
|
||||||
debugger entry point to avoid tty clobbering by multiple processes.
|
debugger entry point to avoid tty clobbering by multiple processes.
|
||||||
"""
|
"""
|
||||||
|
task_name = trio.lowlevel.current_task()
|
||||||
try:
|
try:
|
||||||
log.error("TTY BEING ACQUIRED")
|
log.error(f"TTY BEING ACQUIRED by {task_name}")
|
||||||
await _debug_lock.acquire()
|
await _debug_lock.acquire()
|
||||||
log.error("TTY lock acquired")
|
log.error(f"TTY lock acquired by {task_name}")
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
_debug_lock.release()
|
_debug_lock.release()
|
||||||
log.error("TTY lock released")
|
log.error(f"TTY lock released by {task_name}")
|
||||||
|
|
||||||
|
|
||||||
def handler(signum, frame):
|
def handler(signum, frame):
|
||||||
|
@ -221,54 +223,46 @@ def _breakpoint(debug_func) -> Awaitable[None]:
|
||||||
"""Async breakpoint which schedules a parent stdio lock, and once complete
|
"""Async breakpoint which schedules a parent stdio lock, and once complete
|
||||||
enters the ``pdbpp`` debugging console.
|
enters the ``pdbpp`` debugging console.
|
||||||
"""
|
"""
|
||||||
task_name = trio.lowlevel.current_task()
|
task_name = trio.lowlevel.current_task().name
|
||||||
|
|
||||||
global _in_debug
|
global _in_debug
|
||||||
if _in_debug :
|
|
||||||
if _in_debug == task_name:
|
|
||||||
# this task already has the lock and is
|
|
||||||
# likely recurrently entering a breakpoint
|
|
||||||
return
|
|
||||||
|
|
||||||
# if **this** actor is already in debug mode block here
|
|
||||||
# waiting for the control to be released - this allows
|
|
||||||
# support for recursive entries to `tractor.breakpoint()`
|
|
||||||
log.warning(
|
|
||||||
f"Actor {actor.uid} already has a debug lock, waiting...")
|
|
||||||
await do_unlock.wait()
|
|
||||||
await trio.sleep(0.1)
|
|
||||||
|
|
||||||
# assign unlock callback for debugger teardown hooks
|
|
||||||
global _pdb_release_hook
|
|
||||||
_pdb_release_hook = do_unlock.set
|
|
||||||
|
|
||||||
# mark local actor as "in debug mode" to avoid recurrent
|
|
||||||
# entries/requests to the root process
|
|
||||||
_in_debug = task_name
|
|
||||||
|
|
||||||
# TODO: need a more robust check for the "root" actor
|
# TODO: need a more robust check for the "root" actor
|
||||||
if actor._parent_chan:
|
if actor._parent_chan and not is_root_process():
|
||||||
|
if _in_debug:
|
||||||
|
if _in_debug == task_name:
|
||||||
|
# this task already has the lock and is
|
||||||
|
# likely recurrently entering a breakpoint
|
||||||
|
return
|
||||||
|
|
||||||
|
# if **this** actor is already in debug mode block here
|
||||||
|
# waiting for the control to be released - this allows
|
||||||
|
# support for recursive entries to `tractor.breakpoint()`
|
||||||
|
log.warning(
|
||||||
|
f"Actor {actor.uid} already has a debug lock, waiting...")
|
||||||
|
await do_unlock.wait()
|
||||||
|
await trio.sleep(0.1)
|
||||||
|
|
||||||
|
# assign unlock callback for debugger teardown hooks
|
||||||
|
global _pdb_release_hook
|
||||||
|
_pdb_release_hook = do_unlock.set
|
||||||
|
|
||||||
|
# mark local actor as "in debug mode" to avoid recurrent
|
||||||
|
# entries/requests to the root process
|
||||||
|
_in_debug = task_name
|
||||||
|
|
||||||
# this **must** be awaited by the caller and is done using the
|
# this **must** be awaited by the caller and is done using the
|
||||||
# root nursery so that the debugger can continue to run without
|
# root nursery so that the debugger can continue to run without
|
||||||
# being restricted by the scope of a new task nursery.
|
# being restricted by the scope of a new task nursery.
|
||||||
await actor._service_n.start(wait_for_parent_stdin_hijack)
|
await actor._service_n.start(wait_for_parent_stdin_hijack)
|
||||||
|
|
||||||
else:
|
elif is_root_process():
|
||||||
# we also wait in the root-parent for any child that
|
# we also wait in the root-parent for any child that
|
||||||
# may have the tty locked prior
|
# may have the tty locked prior
|
||||||
async def _lock(
|
if _debug_lock.locked(): # root process already has it; ignore
|
||||||
task_status=trio.TASK_STATUS_IGNORED
|
return
|
||||||
):
|
await _debug_lock.acquire()
|
||||||
try:
|
_pdb_release_hook = _debug_lock.release
|
||||||
async with _acquire_debug_lock():
|
|
||||||
task_status.started()
|
|
||||||
await do_unlock.wait()
|
|
||||||
finally:
|
|
||||||
global _in_debug
|
|
||||||
_in_debug = False
|
|
||||||
log.debug(f"{actor} released tty lock")
|
|
||||||
|
|
||||||
await actor._service_n.start(_lock)
|
|
||||||
|
|
||||||
# block here one (at the appropriate frame *up* where
|
# block here one (at the appropriate frame *up* where
|
||||||
# ``breakpoint()`` was awaited and begin handling stdio
|
# ``breakpoint()`` was awaited and begin handling stdio
|
||||||
|
|
|
@ -139,7 +139,7 @@ class ActorNursery:
|
||||||
"""
|
"""
|
||||||
self.cancelled = True
|
self.cancelled = True
|
||||||
|
|
||||||
log.critical("Cancelling nursery")
|
log.warning(f"Cancelling nursery in {self._actor.uid}")
|
||||||
with trio.move_on_after(3) as cs:
|
with trio.move_on_after(3) as cs:
|
||||||
async with trio.open_nursery() as nursery:
|
async with trio.open_nursery() as nursery:
|
||||||
for subactor, proc, portal in self._children.values():
|
for subactor, proc, portal in self._children.values():
|
||||||
|
|
Loading…
Reference in New Issue