Skip to content

Add: cooperative init cancellation and retryable resource cleanup journal - #1673

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:p0.2b1-fake-chip-harness
Aug 4, 2026
Merged

Add: cooperative init cancellation and retryable resource cleanup journal#1673
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:p0.2b1-fake-chip-harness

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

close() during INITIALIZING now cooperatively cancels the in-flight init
and ultimately reaches CLOSED, instead of failing fast. A latching cancel
token is observed at cooperative points (the child-readiness poll and the
READY commit gate); native segments run to completion uninterrupted.
Because an init blocked inside a native segment reaches no cooperative
point, the wait is bounded by _CLOSE_CANCEL_UNWIND_TIMEOUT_S — close()
raises rather than blocking forever. CLOSED is absorbing: a late init
rollback cannot resurrect FAILED.

Root cancellation raises InitCancelled, an ordinary RuntimeError, so
the init-owner thread can catch it with except Exception.
_StartupCancelled remains the signal-delivered BaseException used only
on the forked-child SIGTERM path.

A CleanupJournal tracks resources whose native free can fail. Entries are
removed only after successful cleanup (post-success), and a subsequent
close() retries any that remain. The journal is shared by both teardown
paths (_teardown_ready_tree and _abort_hierarchical). Unreaped children
from startup rollback are registered in the journal rather than
unconditionally discarded.

Mailbox shm names are now deterministic — sp-<token[:8]>-<suffix>, where
each Worker generates its own token. Because the name is fixed rather than
random, a segment left behind by a crashed prior run surfaces as a
FileExistsError on create instead of being silently bypassed. Zero wire
delta.

close() replay and residual-synthesis checks are journal-aware: a
non-empty journal permits retry; an empty journal with live resources is
poison.

Changes

  • worker.py: cancel token with bounded unwind wait, InitCancelled
    exception type, CleanupJournal class, journal integration in both
    teardown paths, deterministic shm naming, journal-aware close()
    terminal checks
  • 18 new tests covering journal semantics, cooperative cancellation, the
    uncooperative-init bound, CLOSED absorption, shm naming, and
    retry-on-close
  • 2 old fail-fast tests updated to match cooperative cancellation

Not in scope

Deterministic naming is a precondition for prefix-based orphan
recovery, not the recovery itself: nothing in this PR enumerates
/dev/shm. Reclaiming segments left by a hard-killed process tree is
follow-up work.

Verification

  • tests/ut/py/test_worker/: 534 passed, 3 skipped
  • Full tests/ut/py: 1029 passed, 14 skipped (5 TestTorchInterop
    failures are a local missing-torch artifact, green in CI)
  • test_uncooperative_init_bounds_the_close_wait confirmed to fail
    pre-fix (hangs to the test wall budget) and pass post-fix

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The worker lifecycle now supports deterministic shared-memory naming, cooperative initialization cancellation, and retryable cleanup journaling. Tests add a reusable fake-chip harness and cover hierarchical startup, teardown, registration rollback, slot reuse, and callable eligibility.

Changes

Worker lifecycle

Layer / File(s) Summary
Naming and cleanup primitives
python/simpler/worker.py, tests/ut/py/test_worker/test_p0_2c_cancellation_journal.py
Workers use startup-scoped shared-memory names. CleanupJournal retains failed cleanup operations for retry.
Startup cancellation and hierarchical rollback
python/simpler/worker.py, tests/ut/py/test_worker/test_p0_2c_cancellation_journal.py
Non-owner close() cancels initialization. Descendants inherit the shared token. Unreaped child cleanup is journaled.
Close and teardown integration
python/simpler/worker.py, tests/ut/py/test_worker/test_p0_2c_cancellation_journal.py
Close processes journal entries before normal teardown and remains retryable while cleanup debt exists.
Simulated chip lifecycle validation
tests/ut/py/test_worker/_harness.py, tests/ut/py/test_worker/test_startup_readiness.py, tests/ut/py/test_worker/test_host_worker.py, tests/ut/py/test_worker/test_l4_recursive.py, tests/ut/py/test_worker/test_l3_l2_orch_comm.py
A shared fake-chip harness supports bounded startup and injected failures. Tests cover registration, rollback, slot reuse, cancellation, and callable eligibility.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant WorkerClose as Worker.close
  participant WorkerInit as Worker.init
  participant CleanupJournal
  Caller->>WorkerClose: close during initialization
  WorkerClose->>WorkerInit: request cancellation and wake waiters
  WorkerInit->>CleanupJournal: record unreaped child cleanup
  WorkerInit-->>WorkerClose: finish initialization as CLOSED
  WorkerClose->>CleanupJournal: retry journaled cleanup
Loading

Possibly related PRs

Poem

A rabbit watched the workers start,
With tokens naming every part.
If startup stops, cleanup stays,
Then tries again on brighter days.
The mailbox trail is neat and clear—
“Hop on,” says Bun, “the close is here!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the two primary changes: cooperative initialization cancellation and retryable cleanup journaling.
Description check ✅ Passed The description directly explains the cancellation behavior, cleanup journal, deterministic naming, tests, and verification results.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ChaoWao ChaoWao changed the title Add: P0.2-c cooperative init cancellation and retryable resource cleanup journal Add: cooperative init cancellation and retryable resource cleanup journal Aug 4, 2026
@ChaoWao
ChaoWao force-pushed the p0.2b1-fake-chip-harness branch 2 times, most recently from e5f815d to 796efd4 Compare August 4, 2026 04:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/simpler/worker.py`:
- Around line 6768-6783: Update both teardown paths in python/simpler/worker.py
at lines 6768-6783 and 9075-9077: transfer every unreaped PID/SHM pair into
CleanupJournal after each attempt, rather than excluding killed or timed-out
children. Remove or retire the transferred entries from the active resource
lists so successful journal retries clear the residual inventory; update the
logic around _journal_child_survivors() and _reap_child_groups() accordingly.
- Around line 6119-6121: Update the SHM token initialization in the worker
startup flow so recursive startup preserves the inherited root token: generate a
new UUID only when _startup_deadline is None, or ensure _is_startup_root is
updated before the condition runs. Keep the existing token for preconstructed
inner workers initialized with a startup deadline, so descendants remain in the
root namespace.
- Around line 3029-3035: Update _cleanup_child() so failures from
SharedMemory.close() and unlink() are not silently swallowed: still attempt both
operations, propagate any close failure and any unlink OSError other than
FileNotFoundError, and treat only FileNotFoundError from unlink() as successful
already-reclaimed cleanup so CleanupJournal.drive() retains entries for genuine
failures.

In `@tests/ut/py/test_worker/test_p0_2c_cancellation_journal.py`:
- Around line 138-143: Update the test around _hard_timeout and w.close so
close() runs in a separate thread; wait until w._cancel_token is latched,
release the paused initializer via release.set(), then join the closer and
assert it completed without error and that w._lifecycle is CLOSED.

In `@tests/ut/py/test_worker/test_startup_readiness.py`:
- Around line 659-662: Update tests/ut/py/test_worker/test_startup_readiness.py
lines 659-662 and 1372-1376: execute Worker.close() in a separate thread, wait
for cancellation to latch, release the paused initialization
(_start_hierarchical at lines 659-662; ChipWorker.init() at lines 1372-1376),
then join both close and init threads. Assert the close result and
initialization-owner cancellation result only after both threads complete.
- Around line 1492-1506: Update Worker.register() to call
_eligible_target_need() before installing any post-init registration, rejecting
a ChipCallable on a chipless L3 with the expected RuntimeError instead of
creating an inert handle. Remove the strict xfail marker from
test_post_init_chip_callable_on_chipless_l3_rejected so the test runs as a
required passing case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 29d76f1b-2aa6-428c-b216-b900ceb52aea

📥 Commits

Reviewing files that changed from the base of the PR and between e711d48 and 796efd4.

📒 Files selected for processing (8)
  • python/simpler/worker.py
  • tests/ut/py/test_worker/__init__.py
  • tests/ut/py/test_worker/_harness.py
  • tests/ut/py/test_worker/test_host_worker.py
  • tests/ut/py/test_worker/test_l3_l2_orch_comm.py
  • tests/ut/py/test_worker/test_l4_recursive.py
  • tests/ut/py/test_worker/test_p0_2c_cancellation_journal.py
  • tests/ut/py/test_worker/test_startup_readiness.py

Comment thread python/simpler/worker.py
Comment thread python/simpler/worker.py
Comment thread python/simpler/worker.py Outdated
Comment thread tests/ut/py/test_worker/test_cancellation_journal.py
Comment thread tests/ut/py/test_worker/test_startup_readiness.py Outdated
Comment thread tests/ut/py/test_worker/test_startup_readiness.py Outdated
@ChaoWao
ChaoWao force-pushed the p0.2b1-fake-chip-harness branch 4 times, most recently from 16eda67 to f5e1915 Compare August 4, 2026 06:51
…rnal

- close() during INITIALIZING cooperatively cancels in-flight init via
  latching cancel token and ultimately reaches CLOSED; same-thread
  close-during-init is rejected
- The cancel wait is bounded by _CLOSE_CANCEL_UNWIND_TIMEOUT_S: the
  token is only observed at cooperative points, so an init blocked in a
  native segment (ChipWorker.init, the fork phase) reaches none of them
  and close() raises instead of blocking forever
- Root cancellation raises InitCancelled (a RuntimeError), catchable by
  ordinary handlers; _StartupCancelled stays the signal-delivered
  BaseException of the forked-child path
- CleanupJournal class with post-success deletion, idempotent drive(),
  and error accumulation shared by both teardown paths
- Journal driven before teardown in _teardown_ready_tree; entries that
  fail stay for retry on next close()
- _cleanup_child() propagates non-idempotent shared-memory cleanup
  failures so the journal retains entries for genuine errors
- Startup rollback registers unreaped children in journal instead of
  unconditionally clearing pid/shm lists
- Deterministic shm names from a per-Worker uuid token; a fixed name
  makes a segment left by a crashed prior run fail loudly on create
  rather than be silently bypassed. Zero wire delta
- close() prior-done replay and residual-synthesis checks are
  journal-aware (non-empty journal retryable; empty + live = poison)
- _has_live_resources() includes journal entries
- 18 new tests; 2 old fail-fast tests updated to cooperative cancel
@ChaoWao
ChaoWao force-pushed the p0.2b1-fake-chip-harness branch from f5e1915 to 3a6ed5d Compare August 4, 2026 08:08
@ChaoWao

ChaoWao commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

/run-cpu

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

/run-cpu lane finished with failurehttps://github.com/hw-native-sys/simpler/actions/runs/30892109556

  • ✅ pre-commit / pre-commit
  • ✅ detect-changes / detect-changes
  • ✅ ut-a2a3 / ut-a2a3
  • ✅ packaging / packaging
  • ✅ st-onboard-a5 / st-onboard-a5
  • ❌ st-onboard-a2a3 / st-onboard-a2a3
  • ✅ ut / ut
  • ✅ st-sim-a5 / st-sim-a5sim
  • ✅ profiling-flags-smoke / profiling-flags-smoke
  • ✅ ut-a5 / ut-a5
  • ✅ st-sim-a2a3 / st-sim-a2a3sim

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

/run-cpu lane passedhttps://github.com/hw-native-sys/simpler/actions/runs/30892109556

  • ✅ pre-commit / pre-commit
  • ✅ st-onboard-a2a3 / st-onboard-a2a3
  • ✅ detect-changes / detect-changes
  • ✅ ut-a2a3 / ut-a2a3
  • ✅ st-sim-a5 / st-sim-a5sim
  • ✅ profiling-flags-smoke / profiling-flags-smoke
  • ✅ st-onboard-a5 / st-onboard-a5
  • ✅ ut / ut
  • ✅ packaging / packaging
  • ✅ st-sim-a2a3 / st-sim-a2a3sim
  • ✅ ut-a5 / ut-a5

@ChaoWao
ChaoWao merged commit b714174 into hw-native-sys:main Aug 4, 2026
13 of 14 checks passed
@ChaoWao
ChaoWao deleted the p0.2b1-fake-chip-harness branch August 4, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant