Sweep `subint_forkserver` → `main_thread_forkserver` in code
After the variant-1 / variant-2 backend split, update remaining string-match refs to the variant-1 backend so user-visible gates + skip-marks + comments name the working backend correctly: - `tractor._root._DEBUG_COMPATIBLE_BACKENDS`: include `main_thread_forkserver`, drop the stub-only `subint_forkserver` entry. - `tests/test_spawning.py::test_loglevel_propagated_to_subactor`: capfd-skip flips to `main_thread_forkserver`. - `tests/test_infected_asyncio.py::test_sigint_closes_lifetime_stack`: xfail-condition flips to `main_thread_forkserver`. - `tests/test_shm.py`: drop stale "broken on `main_thread_forkserver`" reason-text since the `mp.SharedMemory(track=False)` + resource-tracker monkey-patch in `.ipc._mp_bs` makes the tests pass; the skip-mark only fires on plain `subint` now. - Comment / docstring sweep: `runtime._state`, `runtime._runtime`, `_testing.pytest`, `_subint.py`, `pyproject.toml`, `test_cancellation.py`, `test_registrar.py` — refs to variant-1 backend updated. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codesubint_forkserver_backend
parent
9f0709eee2
commit
205382a39b
|
|
@ -217,7 +217,7 @@ addopts = [
|
||||||
'--show-capture=no',
|
'--show-capture=no',
|
||||||
|
|
||||||
# sys-level capture. REQUIRED for fork-based spawn
|
# sys-level capture. REQUIRED for fork-based spawn
|
||||||
# backends (e.g. `subint_forkserver`): default
|
# backends (e.g. `main_thread_forkserver`): default
|
||||||
# `--capture=fd` redirects fd 1,2 to temp files, and fork
|
# `--capture=fd` redirects fd 1,2 to temp files, and fork
|
||||||
# children inherit those fds — opaque deadlocks happen in
|
# children inherit those fds — opaque deadlocks happen in
|
||||||
# the pytest-capture-machinery ↔ fork-child stdio
|
# the pytest-capture-machinery ↔ fork-child stdio
|
||||||
|
|
|
||||||
|
|
@ -538,13 +538,13 @@ async def kill_transport(
|
||||||
@pytest.mark.timeout(
|
@pytest.mark.timeout(
|
||||||
30,
|
30,
|
||||||
# NOTE should be a 2.1s happy path.
|
# NOTE should be a 2.1s happy path.
|
||||||
# XXX for `subint_forkserver` this is SUPER SENSITIVE so keep it
|
# XXX for `main_thread_forkserver` this is SUPER SENSITIVE
|
||||||
# higher to avoid flaky runs..
|
# so keep it higher to avoid flaky runs..
|
||||||
method='thread',
|
method='thread',
|
||||||
)
|
)
|
||||||
@pytest.mark.skipon_spawn_backend(
|
@pytest.mark.skipon_spawn_backend(
|
||||||
'subint',
|
'subint',
|
||||||
# 'subint_forkserver',
|
# 'main_thread_forkserver',
|
||||||
reason=(
|
reason=(
|
||||||
'XXX SUBINT HANGING TEST XXX\n'
|
'XXX SUBINT HANGING TEST XXX\n'
|
||||||
'See oustanding issue(s)\n'
|
'See oustanding issue(s)\n'
|
||||||
|
|
|
||||||
|
|
@ -452,8 +452,12 @@ async def spawn_and_error(
|
||||||
await nursery.run_in_actor(*args, **kwargs)
|
await nursery.run_in_actor(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# NOTE: subint_forkserver skip handled by file-level `pytestmark`
|
# NOTE: `main_thread_forkserver` capture-fd hang class is no
|
||||||
# above (same pytest-capture-fd hang class as siblings).
|
# longer skipped here — `--capture=sys` (the new `pyproject.toml`
|
||||||
|
# default) sidesteps the pipe-buffer-fill deadlock for
|
||||||
|
# `test_nested_multierrors`. See
|
||||||
|
# `ai/conc-anal/subint_forkserver_test_cancellation_leak_issue.md`
|
||||||
|
# / #449 for the post-mortem.
|
||||||
@pytest.mark.timeout(
|
@pytest.mark.timeout(
|
||||||
10,
|
10,
|
||||||
method='thread',
|
method='thread',
|
||||||
|
|
|
||||||
|
|
@ -1113,7 +1113,7 @@ def test_sigint_closes_lifetime_stack(
|
||||||
if (
|
if (
|
||||||
send_sigint_to == 'child'
|
send_sigint_to == 'child'
|
||||||
and
|
and
|
||||||
start_method == 'subint_forkserver'
|
start_method == 'main_thread_forkserver'
|
||||||
):
|
):
|
||||||
pytest.xfail(
|
pytest.xfail(
|
||||||
reason=(
|
reason=(
|
||||||
|
|
|
||||||
|
|
@ -16,22 +16,16 @@ from tractor.ipc._shm import (
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipon_spawn_backend(
|
pytestmark = pytest.mark.skipon_spawn_backend(
|
||||||
'subint',
|
'subint',
|
||||||
# 'subint_forkserver',
|
# NOTE, `main_thread_forkserver` works for these tests
|
||||||
# XXX we hack around this stdlib limitation by both,
|
# via the `mp.SharedMemory(track=False)` +
|
||||||
# - setting `ShareMemory(track=False)`
|
# `mp.resource_tracker` monkey-patch in `.ipc._mp_bs`.
|
||||||
# - overriding the `mp.ResourceTracker` nonsense in
|
# Without that workaround the fork-inherited
|
||||||
# `.ipc._mp_bs`.
|
# `resource_tracker` fd would EBADF on first shm op +
|
||||||
|
# cascade into `FileExistsError` across parametrize
|
||||||
|
# variants. Tracker doc:
|
||||||
|
# `ai/conc-anal/subint_forkserver_mp_shared_memory_issue.md`.
|
||||||
reason=(
|
reason=(
|
||||||
'subint: GIL-contention hanging class.\n'
|
'subint: GIL-contention hanging class.\n'
|
||||||
'subint_forkserver: `multiprocessing.SharedMemory` '
|
|
||||||
'is fork-without-exec unsafe — child inherits parent\'s '
|
|
||||||
'`resource_tracker` fd → EBADF on first shm op '
|
|
||||||
'(`test_child_attaches_alot`); leaked `/shm_list` from '
|
|
||||||
'a "passing" run cascades into `FileExistsError` across '
|
|
||||||
'parametrize variants (`test_parent_writer_child_reader`). '
|
|
||||||
'Canonical CPython issue class, NOT a tractor bug; full '
|
|
||||||
'tracker doc:\n'
|
|
||||||
'ai/conc-anal/subint_forkserver_mp_shared_memory_issue.md'
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ def test_loglevel_propagated_to_subactor(
|
||||||
reg_addr: tuple,
|
reg_addr: tuple,
|
||||||
level: str,
|
level: str,
|
||||||
):
|
):
|
||||||
if start_method in ('mp_forkserver', 'subint_forkserver'):
|
if start_method in ('mp_forkserver', 'main_thread_forkserver'):
|
||||||
pytest.skip(
|
pytest.skip(
|
||||||
"a bug with `capfd` seems to make forkserver capture not work? "
|
"a bug with `capfd` seems to make forkserver capture not work? "
|
||||||
"(same class as the `mp_forkserver` pre-existing skip — fork-"
|
"(same class as the `mp_forkserver` pre-existing skip — fork-"
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ _DEBUG_COMPATIBLE_BACKENDS: tuple[str, ...] = (
|
||||||
'trio',
|
'trio',
|
||||||
# forkserver children run `_trio_main` in their own OS
|
# forkserver children run `_trio_main` in their own OS
|
||||||
# process — same child-side runtime shape as `trio_proc`.
|
# process — same child-side runtime shape as `trio_proc`.
|
||||||
'subint_forkserver',
|
'main_thread_forkserver',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ def _reap_orphaned_subactors():
|
||||||
grace window, then SIGKILL survivors.
|
grace window, then SIGKILL survivors.
|
||||||
|
|
||||||
Rationale: under fork-based spawn backends (notably
|
Rationale: under fork-based spawn backends (notably
|
||||||
`subint_forkserver`), a test that times out or bails
|
`main_thread_forkserver`), a test that times out or bails
|
||||||
mid-teardown can leave subactor forks alive. Without
|
mid-teardown can leave subactor forks alive. Without
|
||||||
this reap, they linger across sessions and compete
|
this reap, they linger across sessions and compete
|
||||||
for ports / inherit pytest's capture-pipe fds — which
|
for ports / inherit pytest's capture-pipe fds — which
|
||||||
|
|
|
||||||
|
|
@ -1763,7 +1763,7 @@ async def async_main(
|
||||||
# shielded loop would park on the parent chan
|
# shielded loop would park on the parent chan
|
||||||
# indefinitely waiting for EOF that only arrives
|
# indefinitely waiting for EOF that only arrives
|
||||||
# after the PARENT tears down, which under
|
# after the PARENT tears down, which under
|
||||||
# fork-based backends (e.g. `subint_forkserver`)
|
# fork-based backends (e.g. `main_thread_forkserver`)
|
||||||
# it waits on THIS actor's exit — deadlock.
|
# it waits on THIS actor's exit — deadlock.
|
||||||
actor._parent_chan_cs = await root_tn.start(
|
actor._parent_chan_cs = await root_tn.start(
|
||||||
partial(
|
partial(
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,8 @@ class RuntimeVars(Struct):
|
||||||
# `open_root_actor()` nor received a parent `SpawnSpec`. Kept
|
# `open_root_actor()` nor received a parent `SpawnSpec`. Kept
|
||||||
# as a module-level constant so `get_runtime_vars(clear_values=
|
# as a module-level constant so `get_runtime_vars(clear_values=
|
||||||
# True)` can reset the live dict back to this baseline (see
|
# True)` can reset the live dict back to this baseline (see
|
||||||
# `tractor.spawn._subint_forkserver` for the one current caller
|
# `tractor.spawn._main_thread_forkserver` for the one current
|
||||||
# that needs it).
|
# caller that needs it).
|
||||||
_RUNTIME_VARS_DEFAULTS: dict[str, Any] = {
|
_RUNTIME_VARS_DEFAULTS: dict[str, Any] = {
|
||||||
# root of actor-process tree info
|
# root of actor-process tree info
|
||||||
'_is_root': False, # bool
|
'_is_root': False, # bool
|
||||||
|
|
@ -165,7 +165,7 @@ def get_runtime_vars(
|
||||||
defaults (`_RUNTIME_VARS_DEFAULTS`) instead of the live
|
defaults (`_RUNTIME_VARS_DEFAULTS`) instead of the live
|
||||||
dict. Useful in combination with `set_runtime_vars()` to
|
dict. Useful in combination with `set_runtime_vars()` to
|
||||||
reset process-global state back to "cold" — the main caller
|
reset process-global state back to "cold" — the main caller
|
||||||
today is the `subint_forkserver` spawn backend's post-fork
|
today is the `main_thread_forkserver` spawn backend's post-fork
|
||||||
child prelude:
|
child prelude:
|
||||||
|
|
||||||
set_runtime_vars(get_runtime_vars(clear_values=True))
|
set_runtime_vars(get_runtime_vars(clear_values=True))
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,8 @@ async def subint_proc(
|
||||||
# via a `nonlocal err` slot inspected after
|
# via a `nonlocal err` slot inspected after
|
||||||
# `subint_exited.wait()` — see anyio's
|
# `subint_exited.wait()` — see anyio's
|
||||||
# `to_interpreter._interp_call` `(retval, is_exception)`
|
# `to_interpreter._interp_call` `(retval, is_exception)`
|
||||||
# tuple pattern + `_subint_forkserver.py:480-494`'s
|
# tuple pattern +
|
||||||
|
# `_subint_forkserver.run_subint_in_worker_thread._drive`'s
|
||||||
# equivalent which already does this. Skipped here for
|
# equivalent which already does this. Skipped here for
|
||||||
# now: re-raise from the parent must coordinate with
|
# now: re-raise from the parent must coordinate with
|
||||||
# the existing `trio.Cancelled` paths around the
|
# the existing `trio.Cancelled` paths around the
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue