From 07781e38cd5dd0b60b071b5d9ca6f68457ff38da Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sun, 7 Sep 2025 18:47:10 -0400 Subject: [PATCH] Reduce "ignore cases" script to `trio`-only Remove all the `tractor` usage (with IPC ctxs) and just get us a min-reproducing-example with a multi-task-single `trio.Lock`. The wrapping test suite runs the exact same with an ignore case and an `.xfail()` for when we let the `trio.WouldBlock` be unmasked. --- examples/trio/lockacquire_not_unmasked.py | 98 ++++++++++------------- 1 file changed, 43 insertions(+), 55 deletions(-) diff --git a/examples/trio/lockacquire_not_unmasked.py b/examples/trio/lockacquire_not_unmasked.py index 3e507133..2f979a00 100644 --- a/examples/trio/lockacquire_not_unmasked.py +++ b/examples/trio/lockacquire_not_unmasked.py @@ -1,3 +1,6 @@ +from contextlib import ( + asynccontextmanager as acm, +) from functools import partial import tractor @@ -8,87 +11,72 @@ log = tractor.log.get_logger( name=__name__ ) +_lock: trio.Lock|None = None + +@acm async def acquire_singleton_lock( - _lock = trio.Lock(), ) -> None: + global _lock + if _lock is None: + log.info('Allocating LOCK') + _lock = trio.Lock() + log.info('TRYING TO LOCK ACQUIRE') - await _lock.acquire() - log.info('ACQUIRED') + async with _lock: + log.info('ACQUIRED') + yield _lock + + log.info('RELEASED') -@tractor.context -async def acquire_actor_global_lock( - ctx: tractor.Context, - ignore_special_cases: bool, +async def hold_lock_forever( + task_status=trio.TASK_STATUS_IGNORED ): - 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 with ( + tractor.trionics.maybe_raise_from_masking_exc(), + acquire_singleton_lock() as lock, + ): + task_status.started(lock) + 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) + async with ( + trio.open_nursery() as tn, - 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__], + # tractor.trionics.maybe_raise_from_masking_exc() + # ^^^ XXX NOTE, interestingly putting the unmasker + # here does not exhibit the same behaviour ?? + ): + if not ignore_special_cases: + from tractor.trionics import _taskc + _taskc._mask_cases.clear() + + _lock = await tn.start( + hold_lock_forever, + ) + with trio.move_on_after(0.2): + await tn.start( + hold_lock_forever, ) - 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() + tn.cancel_scope.cancel() # XXX, manual test as script if __name__ == '__main__': tractor.log.get_console_log(level='info') - for case in [False, True]: + for case in [True, False]: log.info( f'\n' f'------ RUNNING SCRIPT TRIAL ------\n' - f'child_errors_midstream: {case!r}\n' + f'ignore_special_cases: {case!r}\n' ) trio.run(partial( main,