Skip to content

[Code Health] MailboxState conflates task phase, init, control and directives in one 13-value enum #1764

Description

@ChaoWao

Category

Technical Debt (cleanup, refactor)

Component

Host Runtime

Description

MailboxState is a single 13-value enum that carries four unrelated kinds of
thing
in one shared-memory word. This is the unfinished half of the
async-preparation pipeline's E2 step: "let the mailbox express only phase, and
store the preparation disposition separately."
The disposition half shipped in #1650
(48dc78bf, as a side effect of unrelated work rather than any E2 PR):
MailboxPreparationDisposition (worker_manager.h:84-88) got its own frame
offset, its own validation, and a poison path for out-of-enum values. The
phase half did not.

Today's members, grouped by what they actually are (hit counts across src/ +
python/):

Kind Members Uses
Task phase — the only kind E2 wants to keep here IDLE=0 · TASK_READY=1 · FRAME_STAGED=8 · TASK_LAUNCHED=9 · TASK_DONE=2 · TASK_FAILED=10 —
Init lifecycle INIT_READY=6 · INIT_FAILED=7 30 · 17
Control channel CONTROL_REQUEST=4 · CONTROL_DONE=5 12 · 17
Directives written by the peer SHUTDOWN=3 · ACTIVATE=11 · PREPARE_READY=12 32 · 10 · 8

ACTIVATE is not an observed state at all — it is CAS-written into the word
as a command (worker_manager.cpp:724 does FRAME_STAGED -> ACTIVATE).

Three consequences, none of which a rename fixes:

1. The phase values are interleaved, so "is this a phase?" needs a hand-written
set.
They occupy 1, 2, 8, 9, 10, with non-phase values at 3–7, 11, 12. The
resulting predicate is spelled out by hand, e.g. worker.py:2702:

live_states = (_TASK_READY, _PREPARE_READY, _ACTIVATE, _FRAME_STAGED, _TASK_LAUNCHED)

Two directives mixed with three phases in one tuple — exactly the conflation E2
exists to remove. Every such set must be maintained by hand and can silently
disagree with the next one.

2. The TASK_ / FRAME_ prefixes are load-bearing, not noise. The step's
target vocabulary is READY / STAGED / LAUNCHED / DONE / FAILED; the code needs
the prefixes only to disambiguate from INIT_READY and CONTROL_DONE in the
same enum. So renaming before splitting produces a strictly worse state —
READY sitting next to INIT_READY. The rename is a consequence of the
split, not a substitute for it.

3. FRAME_STAGED already means two different things in one header.
MailboxState::FRAME_STAGED = 8 (:77) and
WorkerProgressKind::FRAME_STAGED = 0 (:291). Same identifier, same file,
different values.

The constraint that shapes any fix: this word is a cross-process wire
contract, not an internal enum. It is a volatile int32_t at
MAILBOX_OFF_STATE mutated by mailbox_compare_exchange_state, in shared
memory between a parent and its forked children — the POD boundary in
.claude/rules/codestyle.md §8. And the Python side re-declares all 13
values by hand
as module constants (worker.py:310-329), with no generation
or assertion tying them to the C++ enum.

Therefore: renaming enumerators is free (compile-time only); renumbering is
not
— an already-running child and a freshly started parent must agree on the
integers. Any fix has to keep the numeric values fixed or ship a synchronized
change on both sides.

Not on the critical path for anything today. Filing it so the scope is on record
rather than being re-estimated as "just a rename" — which is how it was
previously (mis)scoped.

Location

Verified against upstream/main@4739cbf4:

- `src/common/hierarchical/worker_manager.h:60-82` — `MailboxState`, the 13-value enum
- `src/common/hierarchical/worker_manager.h:77` — `MailboxState::FRAME_STAGED = 8`
- `src/common/hierarchical/worker_manager.h:290-294` — `WorkerProgressKind`, whose `FRAME_STAGED = 0` (`:291`) collides by name
- `src/common/hierarchical/worker_manager.h:117` — `MAILBOX_OFF_STATE`, the shared-memory offset of the word
- `src/common/hierarchical/worker_manager.cpp:724` — `ACTIVATE` CAS-written as a directive (`FRAME_STAGED -> ACTIVATE`)
- `python/simpler/worker.py:310-329` — all 13 values re-declared by hand, no single source of truth
- `python/simpler/worker.py:2702` — `live_states`, a hand-written set mixing directives with phases

Proposed Fix

Split by concern while keeping every numeric value unchanged, so nothing on
the wire moves:

  • MailboxTaskPhase — IDLE, TASK_READY, FRAME_STAGED, TASK_LAUNCHED,
    TASK_DONE, TASK_FAILED
  • MailboxInitState — INIT_READY, INIT_FAILED
  • MailboxControlState — CONTROL_REQUEST, CONTROL_DONE
  • MailboxDirective — SHUTDOWN, ACTIVATE, PREPARE_READY

They continue to share the one word, with value ranges that do not overlap, so
the split is source-level only and no running pair can disagree.

Then, and only then, drop the now-redundant TASK_ / FRAME_ prefixes, and
rename WorkerProgressKind::FRAME_STAGED or the phase member so the collision
in worker_manager.h goes away.

Independently worth doing and cheap: give the Python constants a generation step
or a start-up assertion against the C++ values, so the two cannot drift
silently. That is the part with real (if latent) risk today — a wrong constant
there is a silent cross-process protocol mismatch, not a compile error.

Priority

Low (no impact today, good to fix eventually)

Related: #1650 (delivered the disposition half) · #1728 and #1739 (E1/E1', the
sibling step, both complete)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    code healthTechnical debt, robustness, code quality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions