forked from goodboy/tractor
1
0
Fork 0

Factor lock-state release logic into helper

The common logic to both remove our custom SIGINT handler as well
as signal the actor global event that pdb is complete. Call this
whenever we exit a post mortem call and thus any time some rpc task
get's debugged inside `._actor._invoke()`.

Further, we have to manually print the REPL prompt on 3.9 for some wack
reason, so stick a version guard in the sigint handler for that..
signint_saviour
Tyler Goodlet 2022-07-28 13:45:17 -04:00
parent bd362a05f0
commit b01daa5319
1 changed files with 31 additions and 37 deletions

View File

@ -97,34 +97,23 @@ class MultiActorPdb(pdbpp.Pdb):
# override the pdbpp config with our coolio one # override the pdbpp config with our coolio one
DefaultConfig = TractorConfig DefaultConfig = TractorConfig
# def preloop(self):
# print('IN PRELOOP')
# super().preloop()
# TODO: figure out how to disallow recursive .set_trace() entry # TODO: figure out how to disallow recursive .set_trace() entry
# since that'll cause deadlock for us. # since that'll cause deadlock for us.
def set_continue(self): def set_continue(self):
try: try:
super().set_continue() super().set_continue()
finally: finally:
global _local_task_in_debug, _pdb_release_hook maybe_release()
_local_task_in_debug = None
if _pdb_release_hook:
_pdb_release_hook()
def set_quit(self): def set_quit(self):
try: try:
super().set_quit() super().set_quit()
finally: finally:
global _local_task_in_debug, _pdb_release_hook maybe_release()
_local_task_in_debug = None
if _pdb_release_hook:
_pdb_release_hook()
def set_next(self, frame):
try:
super().set_next(frame)
finally:
global _local_task_in_debug, _pdb_release_hook
_local_task_in_debug = None
if _pdb_release_hook:
_pdb_release_hook()
# TODO: will be needed whenever we get to true remote debugging. # TODO: will be needed whenever we get to true remote debugging.
@ -163,6 +152,13 @@ class MultiActorPdb(pdbpp.Pdb):
# log.info("Closing stdin hijack") # log.info("Closing stdin hijack")
# break # break
# TODO: make this method on a global lock type!
def maybe_release():
global _local_task_in_debug, _pdb_release_hook
_local_task_in_debug = None
if _pdb_release_hook:
_pdb_release_hook()
@acm @acm
async def _acquire_debug_lock( async def _acquire_debug_lock(
@ -392,10 +388,10 @@ async def wait_for_parent_stdin_hijack(
log.warning('Root actor cancelled debug lock') log.warning('Root actor cancelled debug lock')
finally: finally:
log.debug(f"Exiting debugger for actor {actor_uid}") log.pdb(f"Exiting debugger for actor {actor_uid}")
global _local_task_in_debug global _local_task_in_debug
_local_task_in_debug = None _local_task_in_debug = None
log.debug(f"Child {actor_uid} released parent stdio lock") log.pdb(f"Child {actor_uid} released parent stdio lock")
def mk_mpdb() -> tuple[MultiActorPdb, Callable]: def mk_mpdb() -> tuple[MultiActorPdb, Callable]:
@ -477,7 +473,7 @@ async def _breakpoint(
# entries/requests to the root process # entries/requests to the root process
_local_task_in_debug = task_name _local_task_in_debug = task_name
def child_release_hook(): def child_release():
try: try:
# sometimes the ``trio`` might already be termianated in # sometimes the ``trio`` might already be termianated in
# which case this call will raise. # which case this call will raise.
@ -489,8 +485,7 @@ async def _breakpoint(
# _local_task_in_debug = None # _local_task_in_debug = None
# assign unlock callback for debugger teardown hooks # assign unlock callback for debugger teardown hooks
# _pdb_release_hook = _local_pdb_complete.set _pdb_release_hook = child_release
_pdb_release_hook = child_release_hook
# 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
@ -507,7 +502,7 @@ async def _breakpoint(
actor.uid, actor.uid,
) )
except RuntimeError: except RuntimeError:
child_release_hook() _pdb_release_hook()
raise raise
elif is_root_process(): elif is_root_process():
@ -541,7 +536,7 @@ async def _breakpoint(
_local_task_in_debug = task_name _local_task_in_debug = task_name
# the lock must be released on pdb completion # the lock must be released on pdb completion
def teardown(): def root_release():
global _local_pdb_complete, _debug_lock global _local_pdb_complete, _debug_lock
global _global_actor_in_debug, _local_task_in_debug global _global_actor_in_debug, _local_task_in_debug
@ -566,11 +561,7 @@ async def _breakpoint(
# restore original sigint handler # restore original sigint handler
undo_sigint() undo_sigint()
_pdb_release_hook = teardown _pdb_release_hook = root_release
# frame = sys._getframe()
# last_f = frame.f_back
# last_f.f_globals['__tracebackhide__'] = True
try: try:
# block here one (at the appropriate frame *up*) where # block here one (at the appropriate frame *up*) where
@ -579,15 +570,13 @@ async def _breakpoint(
debug_func(actor, pdb) debug_func(actor, pdb)
except bdb.BdbQuit: except bdb.BdbQuit:
if _pdb_release_hook: maybe_release()
_pdb_release_hook()
raise raise
# XXX: apparently we can't do this without showing this frame # XXX: apparently we can't do this without showing this frame
# in the backtrace on first entry to the REPL? Seems like an odd # in the backtrace on first entry to the REPL? Seems like an odd
# behaviour that should have been fixed by now. This is also why # behaviour that should have been fixed by now. This is also why
# we scrapped all the @cm approaches that were tried previously. # we scrapped all the @cm approaches that were tried previously.
# finally: # finally:
# __tracebackhide__ = True # __tracebackhide__ = True
# # frame = sys._getframe() # # frame = sys._getframe()
@ -711,8 +700,10 @@ def shield_sigint(
# will be repeated by default. # will be repeated by default.
# TODO: maybe redraw/print last REPL output to console # TODO: maybe redraw/print last REPL output to console
# if pdb_obj: if (
pdb_obj
and sys.version_info <= (3, 10)
):
# TODO: make this work like sticky mode where if there is output # TODO: make this work like sticky mode where if there is output
# detected as written to the tty we redraw this part underneath # detected as written to the tty we redraw this part underneath
# and erase the past draw of this same bit above? # and erase the past draw of this same bit above?
@ -732,7 +723,7 @@ def shield_sigint(
# pdb_obj.do_longlist(None) # pdb_obj.do_longlist(None)
# XXX: we were doing this but it shouldn't be required.. # XXX: we were doing this but it shouldn't be required..
# print(pdb_obj.prompt, end='', flush=True) print(pdb_obj.prompt, end='', flush=True)
def _set_trace( def _set_trace(
@ -838,6 +829,7 @@ async def _maybe_enter_pm(err):
): ):
log.debug("Actor crashed, entering debug mode") log.debug("Actor crashed, entering debug mode")
await post_mortem() await post_mortem()
maybe_release()
return True return True
else: else:
@ -897,9 +889,11 @@ async def maybe_wait_for_debugger(
if _global_actor_in_debug: if _global_actor_in_debug:
sub_in_debug = tuple(_global_actor_in_debug) sub_in_debug = tuple(_global_actor_in_debug)
# alive = tractor.current_actor().child_alive(sub_in_debug)
# if not alive:
# break
log.debug( log.debug('Root polling for debug')
'Root polling for debug')
with trio.CancelScope(shield=True): with trio.CancelScope(shield=True):
await trio.sleep(poll_delay) await trio.sleep(poll_delay)