diff --git a/examples/trio/lockacquire_not_unmasked.py b/examples/trio/lockacquire_not_unmasked.py new file mode 100644 index 00000000..3e507133 --- /dev/null +++ b/examples/trio/lockacquire_not_unmasked.py @@ -0,0 +1,97 @@ +from functools import partial + +import tractor +import trio + + +log = tractor.log.get_logger( + name=__name__ +) + + +async def acquire_singleton_lock( + _lock = trio.Lock(), +) -> None: + log.info('TRYING TO LOCK ACQUIRE') + await _lock.acquire() + log.info('ACQUIRED') + + +@tractor.context +async def acquire_actor_global_lock( + ctx: tractor.Context, + + ignore_special_cases: bool, +): + if not ignore_special_cases: + from tractor.trionics import _taskc + _taskc._mask_cases.clear() + + await acquire_singleton_lock() + await ctx.started('locked') + + # block til cancelled + await trio.sleep_forever() + + +async def main( + ignore_special_cases: bool, + loglevel: str = 'info', + debug_mode: bool = True, + + _fail_after: float = 2, +): + tractor.log.get_console_log(level=loglevel) + + with trio.fail_after(_fail_after): + async with ( + tractor.trionics.collapse_eg(), + tractor.open_nursery( + debug_mode=debug_mode, + loglevel=loglevel, + ) as an, + trio.open_nursery() as tn, + ): + ptl = await an.start_actor( + 'locker', + enable_modules=[__name__], + ) + + async def _open_ctx( + task_status=trio.TASK_STATUS_IGNORED, + ): + async with ptl.open_context( + acquire_actor_global_lock, + ignore_special_cases=ignore_special_cases, + ) as pair: + task_status.started(pair) + await trio.sleep_forever() + + first_ctx, first = await tn.start(_open_ctx,) + assert first == 'locked' + + with trio.move_on_after(0.5):# as cs: + await _open_ctx() + + # await tractor.pause() + print('cancelling first IPC ctx!') + await first_ctx.cancel() + + await ptl.cancel_actor() + # await tractor.pause() + + +# XXX, manual test as script +if __name__ == '__main__': + tractor.log.get_console_log(level='info') + for case in [False, True]: + log.info( + f'\n' + f'------ RUNNING SCRIPT TRIAL ------\n' + f'child_errors_midstream: {case!r}\n' + ) + trio.run(partial( + main, + ignore_special_cases=case, + loglevel='info', + )) diff --git a/tests/test_trioisms.py b/tests/test_trioisms.py index 18e0c80a..f68bc2e8 100644 --- a/tests/test_trioisms.py +++ b/tests/test_trioisms.py @@ -265,3 +265,38 @@ def test_unmask_aclose_as_checkpoint_on_aexit( raise_unmasked=raise_unmasked, child_errors_mid_stream=child_errors_mid_stream, )) + + + +@pytest.mark.parametrize( + 'ignore_special_cases', [ + True, + pytest.param( + False, + marks=pytest.mark.xfail( + reason="see examples/trio/lockacquire_not_umasked.py" + ) + ), + ] +) +def test_cancelled_lockacquire_in_ipctx_not_unmaskeed( + ignore_special_cases: bool, + loglevel: str, + debug_mode: bool, +): + mod: ModuleType = pathlib.import_path( + examples_dir() + / 'trio' + / 'lockacquire_not_unmasked.py', + root=examples_dir(), + consider_namespace_packages=False, + ) + async def _main(): + with trio.fail_after(2): + await mod.main( + ignore_special_cases=ignore_special_cases, + loglevel=loglevel, + debug_mode=debug_mode, + ) + + trio.run(_main)