Always release debug request from `._post_mortem()`
Since obviously the thread is likely expected to halt and raise after the REPL session exits; this was a regression from the prior impl. The main reason for this is that otherwise the request task will never unblock if the user steps through the crashed task using 'next' since the `.do_next()` handler doesn't by default release the request since in the `.pause()` case this would end the session too early. Other, - toss in draft `Pdb.user_exception()`, though doesn't seem to ever trigger? - only release `Lock._debug_lock` when already locked.runtime_to_msgspec
parent
236083b6e4
commit
31de5f6648
|
@ -249,7 +249,10 @@ class Lock:
|
||||||
message: str = 'TTY lock not held by any child\n'
|
message: str = 'TTY lock not held by any child\n'
|
||||||
|
|
||||||
except RuntimeError as rte:
|
except RuntimeError as rte:
|
||||||
message: str = 'TTY lock FAILED to release for child??\n'
|
message: str = (
|
||||||
|
'TTY lock FAILED to release for child??\n'
|
||||||
|
f'{current_task()}\n'
|
||||||
|
)
|
||||||
log.exception(message)
|
log.exception(message)
|
||||||
|
|
||||||
# uhhh makes no sense but been seeing the non-owner
|
# uhhh makes no sense but been seeing the non-owner
|
||||||
|
@ -755,6 +758,16 @@ class PdbREPL(pdbp.Pdb):
|
||||||
|
|
||||||
status = DebugStatus
|
status = DebugStatus
|
||||||
|
|
||||||
|
# NOTE: see details in stdlib's `bdb.py`
|
||||||
|
def user_exception(self, frame, exc_info):
|
||||||
|
'''
|
||||||
|
Called when we stop on an exception.
|
||||||
|
'''
|
||||||
|
log.warning(
|
||||||
|
'Exception during REPL sesh\n\n'
|
||||||
|
f'{frame}\n\n'
|
||||||
|
f'{exc_info}\n\n'
|
||||||
|
)
|
||||||
|
|
||||||
# def preloop(self):
|
# def preloop(self):
|
||||||
# print('IN PRELOOP')
|
# print('IN PRELOOP')
|
||||||
|
@ -780,7 +793,11 @@ class PdbREPL(pdbp.Pdb):
|
||||||
# NOTE: for subactors the stdio lock is released via the
|
# NOTE: for subactors the stdio lock is released via the
|
||||||
# allocated RPC locker task, so for root we have to do it
|
# allocated RPC locker task, so for root we have to do it
|
||||||
# manually.
|
# manually.
|
||||||
if is_root_process():
|
if (
|
||||||
|
is_root_process()
|
||||||
|
and
|
||||||
|
Lock._debug_lock.locked()
|
||||||
|
):
|
||||||
Lock.release()
|
Lock.release()
|
||||||
|
|
||||||
def set_quit(self):
|
def set_quit(self):
|
||||||
|
@ -791,7 +808,11 @@ class PdbREPL(pdbp.Pdb):
|
||||||
cancel_req_task=False,
|
cancel_req_task=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_root_process():
|
if (
|
||||||
|
is_root_process()
|
||||||
|
and
|
||||||
|
Lock._debug_lock.locked()
|
||||||
|
):
|
||||||
Lock.release()
|
Lock.release()
|
||||||
|
|
||||||
# TODO: special handling where we just want the next LOC and
|
# TODO: special handling where we just want the next LOC and
|
||||||
|
@ -803,7 +824,7 @@ class PdbREPL(pdbp.Pdb):
|
||||||
# try:
|
# try:
|
||||||
# super().set_next(frame)
|
# super().set_next(frame)
|
||||||
# finally:
|
# finally:
|
||||||
# Lock.release()
|
# pdbp.set_trace()
|
||||||
|
|
||||||
# XXX NOTE: we only override this because apparently the stdlib pdb
|
# XXX NOTE: we only override this because apparently the stdlib pdb
|
||||||
# bois likes to touch the SIGINT handler as much as i like to touch
|
# bois likes to touch the SIGINT handler as much as i like to touch
|
||||||
|
@ -1251,7 +1272,7 @@ def shield_sigint_handler(
|
||||||
|
|
||||||
# child actor that has locked the debugger
|
# child actor that has locked the debugger
|
||||||
elif not is_root_process():
|
elif not is_root_process():
|
||||||
log.warning(
|
log.debug(
|
||||||
f'Subactor {actor.uid} handling SIGINT\n\n'
|
f'Subactor {actor.uid} handling SIGINT\n\n'
|
||||||
f'{Lock.repr()}\n'
|
f'{Lock.repr()}\n'
|
||||||
)
|
)
|
||||||
|
@ -1484,8 +1505,11 @@ async def _pause(
|
||||||
):
|
):
|
||||||
# re-entrant root process already has it: noop.
|
# re-entrant root process already has it: noop.
|
||||||
log.warning(
|
log.warning(
|
||||||
f'{task.name}@{actor.uid} already has TTY lock\n'
|
f'This root actor task is already within an active REPL session\n'
|
||||||
f'ignoring..'
|
f'Ignoring this re-entered `tractor.pause()`\n'
|
||||||
|
f'task: {task.name}\n'
|
||||||
|
f'REPL: {Lock.repl}\n'
|
||||||
|
# TODO: use `._frame_stack` scanner to find the @api_frame
|
||||||
)
|
)
|
||||||
await trio.lowlevel.checkpoint()
|
await trio.lowlevel.checkpoint()
|
||||||
return
|
return
|
||||||
|
@ -1609,6 +1633,7 @@ async def _pause(
|
||||||
log.exception(
|
log.exception(
|
||||||
'Failed to engage debugger via `_pause()` ??\n'
|
'Failed to engage debugger via `_pause()` ??\n'
|
||||||
)
|
)
|
||||||
|
mk_pdb().set_trace()
|
||||||
|
|
||||||
DebugStatus.release()
|
DebugStatus.release()
|
||||||
# sanity checks for ^ on request/status teardown
|
# sanity checks for ^ on request/status teardown
|
||||||
|
@ -1926,6 +1951,10 @@ def _post_mortem(
|
||||||
# frame=None,
|
# frame=None,
|
||||||
traceback=tb,
|
traceback=tb,
|
||||||
)
|
)
|
||||||
|
# Since we presume the post-mortem was enaged to a task-ending
|
||||||
|
# error, we MUST release the local REPL request so that not other
|
||||||
|
# local task nor the root remains blocked!
|
||||||
|
DebugStatus.release()
|
||||||
|
|
||||||
|
|
||||||
async def post_mortem(
|
async def post_mortem(
|
||||||
|
|
Loading…
Reference in New Issue