Compare commits

..

7 Commits

Author SHA1 Message Date
Tyler Goodlet 9010c9cf04 Solve another OoB cancellation case, the bg task one
Such that we are able to (finally) detect when we should
`Context._scope.cancel()` specifically when the `.parent_task` is
**not** blocking on receiving from the underlying `._rx_chan`, since if
the task is blocking on `.receive()` it will call `.cancel()`
implicitly.

This is a lot to explain with very little code actually needed for the
implementation (are we like `trio` yet anyone?? XD) but the main jist is
that `Context._maybe_cancel_and_set_remote_error()` needed the
additional case of calling `._scope.cancel()` whenever we know that
a remote-error/ctxc won't be immediately handled, bc user code is doing
non `Context`-API things, and result in a similar outcome as if that
task was waiting on `Context.wait_for_result()` or `.__aexite__()`.

Impl details,
- add a new `._is_blocked_on_rx_chan()` method which predicates whether
  the (new) `.parent_task` is blocking on `._rx_chan.receive()`.
  * see various stipulations about the current impl and how we might
    need to adjust for the future given `trio`'s commitment to the
    `Task.custom_sleep_data` attr..
- add `.parent_task`, a pub wrapper for `._task`.
- check for `not ._is_blocked_on_rx_chan()` before manually cancelling
  the local `.parent_task`
- minimize the surrounding branch case expressions.

Other,
- tweak a couple logs.
- add a new `.cancel()` pre-started msg.
- mask the `.cancel_called` setter, it's only (been) used for tracing.
- todos around maybe moving the `._nursery` allocation "around" the
  `.start_remote_task()` call and various subsequent tweaks therein.
2025-09-10 21:09:40 -04:00
Tyler Goodlet 54658016f1 Check off REPL-ing todo add masked usage in `drain_to_final_msg()` 2025-09-09 18:13:28 -04:00
Tyler Goodlet 1975d5bd86 Timeout on `test_peer_spawns_and_cancels_service_subactor`
While working on a fix to the hang case found from
`test_cancel_ctx_with_parent_side_entered_in_bg_task` an initial
solution caused this test to hang indefinitely; solve it with a small
wrapping `_main()` + `trio.fail_after()` entrypoint.

Further suite refinements,
- move the top-most `try:`->`else:` block
- toss in a masked base-exc block for tracing unexpected
  `ctx.wait_for_result()` outcomes.
- tweak the `raise_sub_spawn_error_after` to be an optional `float`
  which scales the `rng_seed: int = 50` msg counter to
  `tell_little_bro()` so that the abs value to the `range()` can be
  changed.
2025-09-09 17:33:20 -04:00
Tyler Goodlet b83d8bb91d Rename var for and hide the `_open_and_supervise_one_cancels_all_nursery` frame 2025-09-08 18:15:00 -04:00
Tyler Goodlet 81ba90492b Add timeout around `test_peer_spawns_and_cancels_service_subactor` suite 2025-09-08 17:58:02 -04:00
Tyler Goodlet 16bfc35c09 Parametrize with `Portal.cancel_actor()` only case
Such that when `maybe_context.cancel()` is not called (explicitly) and
only the subactor is cancelled by its parent we expect to see a ctxc
raised both from any call to `Context.wait_for_result()` and out of
the `Portal.open_context()` scope, up to the `trio.run()`.

Deats,
- obvi call-n-catch the ctxc (in scope) for the oob-only
  subactor-cancelled case.
- add branches around `trio.run()` entry to match.
2025-09-08 17:39:54 -04:00
Tyler Goodlet 3772b70e54 Add the minimal OoB cancel edge case from #391
Discovered while writing a `@context` sanity test to verify unmasker
ignore-cases support. Masked code is due to the process of finding the
minimal example causing the original hang discovered in the original
examples script. Details are in the test-fn doc strings and surrounding
comments; more refinement and cleanup coming obviously.

Also moved over the self-cancel todos from the inter-peer tests module.
2025-09-08 11:54:28 -04:00
2 changed files with 3 additions and 4 deletions

View File

@ -279,7 +279,7 @@ def test_unmask_aclose_as_checkpoint_on_aexit(
), ),
] ]
) )
def test_cancelled_lockacquire_in_ipctx_not_unmasked( def test_cancelled_lockacquire_in_ipctx_not_unmaskeed(
ignore_special_cases: bool, ignore_special_cases: bool,
loglevel: str, loglevel: str,
debug_mode: bool, debug_mode: bool,

View File

@ -72,7 +72,7 @@ _mask_cases: dict[
dict[ dict[
int, # inner-frame index into `inspect.getinnerframes()` int, # inner-frame index into `inspect.getinnerframes()`
# `FrameInfo.function/filename: str`s to match # `FrameInfo.function/filename: str`s to match
dict[str, str], tuple[str, str],
], ],
] = { ] = {
trio.WouldBlock: { trio.WouldBlock: {
@ -275,8 +275,7 @@ async def maybe_raise_from_masking_exc(
)) ))
): ):
log.warning( log.warning(
f'Ignoring already-known, non-ideal-but-valid ' f'Ignoring already-known/non-ideally-valid masker code @\n'
f'masker code @\n'
f'{masker_frame}\n' f'{masker_frame}\n'
f'\n' f'\n'
f'NOT raising {exc_ctx} from masker {exc_match!r}\n' f'NOT raising {exc_ctx} from masker {exc_match!r}\n'