Wire `reg_addr` through leaky cancel tests

Stopgap companion to d0121960 (`subint_forkserver`
test-cancellation leak doc): five tests in
`tests/test_cancellation.py` were running against the
default `:1616` registry, so any leaked
`subint-forkserv` descendant from a prior test holds
the port and blows up every subsequent run with
`TooSlowError` / "address in use". Thread the
session-unique `reg_addr` fixture through so each run
picks its own port — zombies can no longer poison
other tests (they'll only cross-contaminate whatever
happens to share their port, which is now nothing).

Deats,
- add `reg_addr: tuple` fixture param to:
  - `test_cancel_infinite_streamer`
  - `test_some_cancels_all`
  - `test_nested_multierrors`
  - `test_cancel_via_SIGINT`
  - `test_cancel_via_SIGINT_other_task`
- explicitly pass `registry_addrs=[reg_addr]` to the
  two `open_nursery()` calls that previously had no
  kwargs at all (in `test_cancel_via_SIGINT` and
  `test_cancel_via_SIGINT_other_task`)
- add bounded `@pytest.mark.timeout(7, method='thread')`
  to `test_nested_multierrors` so a hung run doesn't
  wedge the whole session

Still doesn't close the real leak — the
`subint_forkserver` backend's `_ForkedProc.kill()` is
PID-scoped not tree-scoped, so grandchildren survive
teardown regardless of registry port. This commit is
just blast-radius containment until that fix lands.
See `ai/conc-anal/
subint_forkserver_test_cancellation_leak_issue.md`.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Gud Boi 2026-04-23 14:37:48 -04:00
parent d0121960b9
commit de6016763f
1 changed files with 16 additions and 3 deletions

View File

@ -275,7 +275,8 @@ async def stream_forever():
timeout=6, timeout=6,
) )
async def test_cancel_infinite_streamer( async def test_cancel_infinite_streamer(
start_method: str reg_addr: tuple,
start_method: str,
): ):
# stream for at most 1 seconds # stream for at most 1 seconds
with ( with (
@ -341,6 +342,7 @@ async def test_cancel_infinite_streamer(
) )
async def test_some_cancels_all( async def test_some_cancels_all(
num_actors_and_errs: tuple, num_actors_and_errs: tuple,
reg_addr: tuple,
start_method: str, start_method: str,
loglevel: str, loglevel: str,
): ):
@ -450,8 +452,13 @@ async def spawn_and_error(
await nursery.run_in_actor(*args, **kwargs) await nursery.run_in_actor(*args, **kwargs)
@pytest.mark.timeout(
10,
method='thread',
)
@tractor_test @tractor_test
async def test_nested_multierrors( async def test_nested_multierrors(
reg_addr: tuple,
loglevel: str, loglevel: str,
start_method: str, start_method: str,
): ):
@ -541,6 +548,7 @@ async def test_nested_multierrors(
@no_windows @no_windows
def test_cancel_via_SIGINT( def test_cancel_via_SIGINT(
reg_addr: tuple,
loglevel: str, loglevel: str,
start_method: str, start_method: str,
): ):
@ -553,7 +561,9 @@ def test_cancel_via_SIGINT(
async def main(): async def main():
with trio.fail_after(2): with trio.fail_after(2):
async with tractor.open_nursery() as tn: async with tractor.open_nursery(
registry_addrs=[reg_addr],
) as tn:
await tn.start_actor('sucka') await tn.start_actor('sucka')
if 'mp' in start_method: if 'mp' in start_method:
time.sleep(0.1) time.sleep(0.1)
@ -566,6 +576,7 @@ def test_cancel_via_SIGINT(
@no_windows @no_windows
def test_cancel_via_SIGINT_other_task( def test_cancel_via_SIGINT_other_task(
reg_addr: tuple,
loglevel: str, loglevel: str,
start_method: str, start_method: str,
spawn_backend: str, spawn_backend: str,
@ -594,7 +605,9 @@ def test_cancel_via_SIGINT_other_task(
async def spawn_and_sleep_forever( async def spawn_and_sleep_forever(
task_status=trio.TASK_STATUS_IGNORED task_status=trio.TASK_STATUS_IGNORED
): ):
async with tractor.open_nursery() as tn: async with tractor.open_nursery(
registry_addrs=[reg_addr],
) as tn:
for i in range(3): for i in range(3):
await tn.run_in_actor( await tn.run_in_actor(
sleep_forever, sleep_forever,