''' Verify we can dump a `stackscope` tree on a hang. ''' import os import platform import signal import trio import tractor @tractor.context async def start_n_shield_hang( ctx: tractor.Context, ): # actor: tractor.Actor = tractor.current_actor() # sync to parent-side task await ctx.started(os.getpid()) print('Entering shield sleep..') with trio.CancelScope(shield=True): await trio.sleep_forever() # in subactor # XXX NOTE ^^^ since this shields, we expect # the zombie reaper (aka T800) to engage on # SIGINT from the user and eventually hard-kill # this subprocess! async def main( from_test: bool = False, ) -> None: if platform.system() != 'Darwin': tpt = 'uds' else: # XXX, precisely we can't use pytest's tmp-path generation # for tests.. apparently because: # # > The OSError: AF_UNIX path too long in macOS Python occurs # > because the path to the Unix domain socket exceeds the # > operating system's maximum path length limit (around 104 # # WHICH IS just, wtf hillarious XD tpt = 'tcp' async with ( tractor.open_nursery( debug_mode=True, enable_stack_on_sig=True, # maybe_enable_greenback=False, loglevel='devx', enable_transports=[tpt], ) as an, ): ptl: tractor.Portal = await an.start_actor( 'hanger', enable_modules=[__name__], debug_mode=True, ) async with ptl.open_context( start_n_shield_hang, ) as (ctx, cpid): _, proc, _ = an._children[ptl.chan.uid] assert cpid == proc.pid print( 'Yo my child hanging..?\n' # "i'm a user who wants to see a `stackscope` tree!\n" ) # XXX simulate the wrapping test's "user actions" # (i.e. if a human didn't run this manually but wants to # know what they should do to reproduce test behaviour) if from_test: print( f'Sending SIGUSR1 to {cpid!r}!\n' ) os.kill( cpid, signal.SIGUSR1, ) # simulate user cancelling program await trio.sleep(0.5) os.kill( os.getpid(), signal.SIGINT, ) else: # actually let user send the ctl-c await trio.sleep_forever() # in root if __name__ == '__main__': trio.run(main)