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',
|
||||
|
||||
# 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
|
||||
# children inherit those fds — opaque deadlocks happen in
|
||||
# the pytest-capture-machinery ↔ fork-child stdio
|
||||
|
|
|
|||
|
|
@ -538,13 +538,13 @@ async def kill_transport(
|
|||
@pytest.mark.timeout(
|
||||
30,
|
||||
# NOTE should be a 2.1s happy path.
|
||||
# XXX for `subint_forkserver` this is SUPER SENSITIVE so keep it
|
||||
# higher to avoid flaky runs..
|
||||
# XXX for `main_thread_forkserver` this is SUPER SENSITIVE
|
||||
# so keep it higher to avoid flaky runs..
|
||||
method='thread',
|
||||
)
|
||||
@pytest.mark.skipon_spawn_backend(
|
||||
'subint',
|
||||
# 'subint_forkserver',
|
||||
# 'main_thread_forkserver',
|
||||
reason=(
|
||||
'XXX SUBINT HANGING TEST XXX\n'
|
||||
'See oustanding issue(s)\n'
|
||||
|
|
|
|||
|
|
@ -452,8 +452,12 @@ async def spawn_and_error(
|
|||
await nursery.run_in_actor(*args, **kwargs)
|
||||
|
||||
|
||||
# NOTE: subint_forkserver skip handled by file-level `pytestmark`
|
||||
# above (same pytest-capture-fd hang class as siblings).
|
||||
# NOTE: `main_thread_forkserver` capture-fd hang class is no
|
||||
# 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(
|
||||
10,
|
||||
method='thread',
|
||||
|
|
|
|||
|
|
@ -1113,7 +1113,7 @@ def test_sigint_closes_lifetime_stack(
|
|||
if (
|
||||
send_sigint_to == 'child'
|
||||
and
|
||||
start_method == 'subint_forkserver'
|
||||
start_method == 'main_thread_forkserver'
|
||||
):
|
||||
pytest.xfail(
|
||||
reason=(
|
||||
|
|
|
|||
|
|
@ -16,22 +16,16 @@ from tractor.ipc._shm import (
|
|||
|
||||
pytestmark = pytest.mark.skipon_spawn_backend(
|
||||
'subint',
|
||||
# 'subint_forkserver',
|
||||
# XXX we hack around this stdlib limitation by both,
|
||||
# - setting `ShareMemory(track=False)`
|
||||
# - overriding the `mp.ResourceTracker` nonsense in
|
||||
# `.ipc._mp_bs`.
|
||||
# NOTE, `main_thread_forkserver` works for these tests
|
||||
# via the `mp.SharedMemory(track=False)` +
|
||||
# `mp.resource_tracker` monkey-patch in `.ipc._mp_bs`.
|
||||
# Without that workaround the fork-inherited
|
||||
# `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=(
|
||||
'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,
|
||||
level: str,
|
||||
):
|
||||
if start_method in ('mp_forkserver', 'subint_forkserver'):
|
||||
if start_method in ('mp_forkserver', 'main_thread_forkserver'):
|
||||
pytest.skip(
|
||||
"a bug with `capfd` seems to make forkserver capture not work? "
|
||||
"(same class as the `mp_forkserver` pre-existing skip — fork-"
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ _DEBUG_COMPATIBLE_BACKENDS: tuple[str, ...] = (
|
|||
'trio',
|
||||
# forkserver children run `_trio_main` in their own OS
|
||||
# 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.
|
||||
|
||||
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
|
||||
this reap, they linger across sessions and compete
|
||||
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
|
||||
# indefinitely waiting for EOF that only arrives
|
||||
# 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.
|
||||
actor._parent_chan_cs = await root_tn.start(
|
||||
partial(
|
||||
|
|
|
|||
|
|
@ -122,8 +122,8 @@ class RuntimeVars(Struct):
|
|||
# `open_root_actor()` nor received a parent `SpawnSpec`. Kept
|
||||
# as a module-level constant so `get_runtime_vars(clear_values=
|
||||
# True)` can reset the live dict back to this baseline (see
|
||||
# `tractor.spawn._subint_forkserver` for the one current caller
|
||||
# that needs it).
|
||||
# `tractor.spawn._main_thread_forkserver` for the one current
|
||||
# caller that needs it).
|
||||
_RUNTIME_VARS_DEFAULTS: dict[str, Any] = {
|
||||
# root of actor-process tree info
|
||||
'_is_root': False, # bool
|
||||
|
|
@ -165,7 +165,7 @@ def get_runtime_vars(
|
|||
defaults (`_RUNTIME_VARS_DEFAULTS`) instead of the live
|
||||
dict. Useful in combination with `set_runtime_vars()` to
|
||||
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:
|
||||
|
||||
set_runtime_vars(get_runtime_vars(clear_values=True))
|
||||
|
|
|
|||
|
|
@ -257,7 +257,8 @@ async def subint_proc(
|
|||
# via a `nonlocal err` slot inspected after
|
||||
# `subint_exited.wait()` — see anyio's
|
||||
# `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
|
||||
# now: re-raise from the parent must coordinate with
|
||||
# the existing `trio.Cancelled` paths around the
|
||||
|
|
|
|||
Loading…
Reference in New Issue