Reset post-fork `_state` in forkserver child
`os.fork()` inherits the parent's entire memory image, including `tractor.runtime._state` globals that encode "this process is the root actor" — `_runtime_vars`'s `_is_root=True`, pre-populated `_root_mailbox` + `_registry_addrs`, and the parent's `_current_actor` singleton. A fresh `exec`-based child starts with those globals at their module-level defaults (all falsey/empty). The forkserver child needs to match that shape BEFORE calling `_actor_child_main()`, otherwise `Actor.__init__()` takes the `is_root_process() == True` branch and pre-populates `self.enable_modules`, which then trips `assert not self.enable_modules` at the top of `Actor._from_parent()` on the subsequent parent→child `SpawnSpec` handshake. Fix: at the start of `_child_target`, null `_state._current_actor` and overwrite `_runtime_vars` with a cold-root blank (`_is_root=False`, empty mailbox/addrs, `_debug_mode=False`) before `_actor_child_main()` runs. Found-via: `test_subint_forkserver_spawn_basic` hitting the `enable_modules` assert on child-side runtime boot. (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
26914fde75
commit
63ab7c986b
|
|
@ -548,6 +548,32 @@ async def subint_forkserver_proc(
|
|||
# every spawn — it's module-level in `_child` but
|
||||
# cheap enough to re-resolve here.
|
||||
from tractor._child import _actor_child_main
|
||||
# XXX, fork inherits the parent's entire memory
|
||||
# image — including `tractor.runtime._state` globals
|
||||
# that encode "this process is the root actor":
|
||||
#
|
||||
# - `_runtime_vars['_is_root']` → True in parent
|
||||
# - pre-populated `_root_mailbox`, `_registry_addrs`
|
||||
# - the parent's `_current_actor` singleton
|
||||
#
|
||||
# A fresh `exec`-based child would start with the
|
||||
# `_state` module's defaults (all falsey / empty).
|
||||
# Replicate that here so the new child-side `Actor`
|
||||
# sees a "cold" runtime — otherwise `Actor.__init__`
|
||||
# takes the `is_root_process() == True` branch and
|
||||
# pre-populates `self.enable_modules`, which then
|
||||
# trips the `assert not self.enable_modules` gate at
|
||||
# the top of `Actor._from_parent()` on the subsequent
|
||||
# parent→child `SpawnSpec` handshake.
|
||||
from tractor.runtime import _state
|
||||
_state._current_actor = None
|
||||
_state._runtime_vars.update({
|
||||
'_is_root': False,
|
||||
'_root_mailbox': (None, None),
|
||||
'_root_addrs': [],
|
||||
'_registry_addrs': [],
|
||||
'_debug_mode': False,
|
||||
})
|
||||
_actor_child_main(
|
||||
uid=uid,
|
||||
loglevel=loglevel,
|
||||
|
|
|
|||
Loading…
Reference in New Issue