Refactor `_runtime_vars` into pure get/set API
Post-fork `_runtime_vars` reset in `subint_forkserver_proc`
was previously done via direct mutation of
`_state._runtime_vars` from an external module + an inline
default dict duplicating the `_state.py`-internal defaults.
Split the access surface into a pure getter + explicit
setter so the reset call site becomes a one-liner
composition.
Deats `tractor/runtime/_state.py`,
- extract initial values into a module-level
`_RUNTIME_VARS_DEFAULTS: dict[str, Any]` constant; the
live `_runtime_vars` is now initialised from
`dict(_RUNTIME_VARS_DEFAULTS)`
- `get_runtime_vars()` grows a `clear_values: bool = False`
kwarg. When True, returns a fresh copy of
`_RUNTIME_VARS_DEFAULTS` instead of the live dict —
still a **pure read**, never mutates anything
- new `set_runtime_vars(rtvars: dict | RuntimeVars)` —
atomic replacement of the live dict's contents via
`.clear()` + `.update()`, so existing references to the
same dict object remain valid. Accepts either the
historical dict form or the `RuntimeVars` struct
Deats `tractor/spawn/_subint_forkserver.py`,
- collapse the prior ad-hoc `.update({...})` block into
`set_runtime_vars(get_runtime_vars(clear_values=True))`
- drop the `_state._current_actor = None` line —
`_trio_main` unconditionally overwrites it downstream,
so no explicit reset needed (noted in the XXX comment)
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
subint_forkserver_backend
parent
63ab7c986b
commit
7804a9feac
|
|
@ -117,7 +117,14 @@ class RuntimeVars(Struct):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
_runtime_vars: dict[str, Any] = {
|
# The "fresh process" defaults — what `_runtime_vars` looks
|
||||||
|
# like in a just-booted Python process that hasn't yet entered
|
||||||
|
# `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).
|
||||||
|
_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
|
||||||
'_root_mailbox': (None, None), # tuple[str|None, str|None]
|
'_root_mailbox': (None, None), # tuple[str|None, str|None]
|
||||||
|
|
@ -138,10 +145,12 @@ _runtime_vars: dict[str, Any] = {
|
||||||
# infected-`asyncio`-mode: `trio` running as guest.
|
# infected-`asyncio`-mode: `trio` running as guest.
|
||||||
'_is_infected_aio': False,
|
'_is_infected_aio': False,
|
||||||
}
|
}
|
||||||
|
_runtime_vars: dict[str, Any] = dict(_RUNTIME_VARS_DEFAULTS)
|
||||||
|
|
||||||
|
|
||||||
def get_runtime_vars(
|
def get_runtime_vars(
|
||||||
as_dict: bool = True,
|
as_dict: bool = True,
|
||||||
|
clear_values: bool = False,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
'''
|
'''
|
||||||
Deliver a **copy** of the current `Actor`'s "runtime variables".
|
Deliver a **copy** of the current `Actor`'s "runtime variables".
|
||||||
|
|
@ -150,11 +159,62 @@ def get_runtime_vars(
|
||||||
form, but the `RuntimeVars` struct should be utilized as possible
|
form, but the `RuntimeVars` struct should be utilized as possible
|
||||||
for future calls.
|
for future calls.
|
||||||
|
|
||||||
'''
|
Pure read — **never mutates** the module-level `_runtime_vars`.
|
||||||
if as_dict:
|
|
||||||
return dict(_runtime_vars)
|
|
||||||
|
|
||||||
return RuntimeVars(**_runtime_vars)
|
If `clear_values=True`, return a copy of the fresh-process
|
||||||
|
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
|
||||||
|
child prelude:
|
||||||
|
|
||||||
|
set_runtime_vars(get_runtime_vars(clear_values=True))
|
||||||
|
|
||||||
|
`os.fork()` inherits the parent's full memory image, so the
|
||||||
|
child sees the parent's populated `_runtime_vars` (e.g.
|
||||||
|
`_is_root=True`) which would trip the `assert not
|
||||||
|
self.enable_modules` gate in `Actor._from_parent()` on the
|
||||||
|
subsequent parent→child `SpawnSpec` handshake if left alone.
|
||||||
|
|
||||||
|
'''
|
||||||
|
src: dict = (
|
||||||
|
_RUNTIME_VARS_DEFAULTS
|
||||||
|
if clear_values
|
||||||
|
else _runtime_vars
|
||||||
|
)
|
||||||
|
snapshot: dict = dict(src)
|
||||||
|
if as_dict:
|
||||||
|
return snapshot
|
||||||
|
return RuntimeVars(**snapshot)
|
||||||
|
|
||||||
|
|
||||||
|
def set_runtime_vars(
|
||||||
|
rtvars: dict | RuntimeVars,
|
||||||
|
) -> None:
|
||||||
|
'''
|
||||||
|
Atomically replace the module-level `_runtime_vars` contents
|
||||||
|
with those of `rtvars` (via `.clear()` + `.update()` so
|
||||||
|
live references to the same dict object remain valid).
|
||||||
|
|
||||||
|
Accepts either the historical `dict` form or the `RuntimeVars`
|
||||||
|
`msgspec.Struct` form (the latter still mostly unused but
|
||||||
|
the blessed forward shape — see the struct's definition).
|
||||||
|
|
||||||
|
Paired with `get_runtime_vars()` as the explicit
|
||||||
|
write-half of the runtime-vars API — prefer this over
|
||||||
|
direct mutation of `_runtime_vars[...]` from new call sites.
|
||||||
|
|
||||||
|
'''
|
||||||
|
if isinstance(rtvars, RuntimeVars):
|
||||||
|
# `msgspec.Struct` → dict via its declared field set;
|
||||||
|
# avoids pulling in `msgspec.structs.asdict` just for
|
||||||
|
# this one call path.
|
||||||
|
rtvars = {
|
||||||
|
field_name: getattr(rtvars, field_name)
|
||||||
|
for field_name in rtvars.__struct_fields__
|
||||||
|
}
|
||||||
|
_runtime_vars.clear()
|
||||||
|
_runtime_vars.update(rtvars)
|
||||||
|
|
||||||
|
|
||||||
def last_actor() -> Actor|None:
|
def last_actor() -> Actor|None:
|
||||||
|
|
|
||||||
|
|
@ -548,32 +548,24 @@ async def subint_forkserver_proc(
|
||||||
# every spawn — it's module-level in `_child` but
|
# every spawn — it's module-level in `_child` but
|
||||||
# cheap enough to re-resolve here.
|
# cheap enough to re-resolve here.
|
||||||
from tractor._child import _actor_child_main
|
from tractor._child import _actor_child_main
|
||||||
# XXX, fork inherits the parent's entire memory
|
# XXX, `os.fork()` inherits the parent's entire memory
|
||||||
# image — including `tractor.runtime._state` globals
|
# image, including `tractor.runtime._state._runtime_vars`
|
||||||
# that encode "this process is the root actor":
|
# (which in the parent encodes "this process IS the root
|
||||||
#
|
# actor"). A fresh `exec`-based child starts cold; we
|
||||||
# - `_runtime_vars['_is_root']` → True in parent
|
# replicate that here by explicitly resetting runtime
|
||||||
# - pre-populated `_root_mailbox`, `_registry_addrs`
|
# vars to their fresh-process defaults — otherwise
|
||||||
# - the parent's `_current_actor` singleton
|
# `Actor.__init__` takes the `is_root_process() == True`
|
||||||
#
|
# branch, pre-populates `self.enable_modules`, and trips
|
||||||
# A fresh `exec`-based child would start with the
|
# the `assert not self.enable_modules` gate at the top
|
||||||
# `_state` module's defaults (all falsey / empty).
|
# of `Actor._from_parent()` on the subsequent parent→
|
||||||
# Replicate that here so the new child-side `Actor`
|
# child `SpawnSpec` handshake. (`_state._current_actor`
|
||||||
# sees a "cold" runtime — otherwise `Actor.__init__`
|
# is unconditionally overwritten by `_trio_main` → no
|
||||||
# takes the `is_root_process() == True` branch and
|
# reset needed for it.)
|
||||||
# pre-populates `self.enable_modules`, which then
|
from tractor.runtime._state import (
|
||||||
# trips the `assert not self.enable_modules` gate at
|
get_runtime_vars,
|
||||||
# the top of `Actor._from_parent()` on the subsequent
|
set_runtime_vars,
|
||||||
# parent→child `SpawnSpec` handshake.
|
)
|
||||||
from tractor.runtime import _state
|
set_runtime_vars(get_runtime_vars(clear_values=True))
|
||||||
_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(
|
_actor_child_main(
|
||||||
uid=uid,
|
uid=uid,
|
||||||
loglevel=loglevel,
|
loglevel=loglevel,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue