Skip to content

Attach the device before drain and retire the dead dep_gen capture API - #1725

Merged
ChaoWao merged 1 commit into
mainfrom
fix-pr1694-followups
Aug 6, 2026
Merged

Attach the device before drain and retire the dead dep_gen capture API#1725
ChaoWao merged 1 commit into
mainfrom
fix-pr1694-followups

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Follow-ups to the review on #1694, which merged as 4f417732.

1. drain_execution ran without an attached device context

simpler_wait_run and simpler_finalize_run both reached drain_execution
before any attach_current_thread. Drain synchronizes and destroys streams,
reads device memory via print_handshake_results, and frees device
allocations through its cleanup guard — all per-thread CANN state, and
attach_current_thread's own comment says "CANN device context is
per-thread, so every caller must attach explicitly."

This was latent rather than broken: the child progress loop is
single-threaded, so the launch-time attach happened to cover both drains.
But simpler_wait_run is public C API, bound as _wait_native_run under
nb::call_guard<nb::gil_scoped_release>(), and nothing documents a
same-thread requirement.

Both entry points now attach first, matching prepare / launch / poll.
rtSetDevice is idempotent on an already-attached thread, which is why poll
already calls it unconditionally every iteration. In finalize the existing
attach simply moves above the drain, so one call covers the drain and
validate_runtime_impl both.

Each attach sits inside a try: attach_current_thread is not noexcept
(onboard calls rtSetDevice; sim's acquire_device allocates), and these
are C API boundaries where an escaping exception would terminate.

2. catch (...) {} around the drains now logs

The rc still propagates unchanged. Drain is where op-execute timeouts and
507018 cascades surface, and the rc alone doesn't say which — so the reason
is worth a line.

3. dep_gen host graph: invariant stated, dead API removed

Deleting the executor handoff left the capture correct but undocumented.
The graph is thread_local, built by the orchestration during prepare and
read by emit during drain — sound only because both now land on the child
progress loop's single thread. That invariant was unstated while the headers
still described the take/adopt transfer that used to enforce it, and
take_capture / adopt_capture / destroy_capture remained exported in
both arches with zero production callers.

Taking the reviewer's preferred resolution: state the invariant where the
graph lives, and delete the dead API rather than leave it reading as live.
Header prose, the emit error string, the device-runner comments and
docs/dfx/dep-gen.md are rewritten rather than word-swapped.

4. Restored test coverage for a surviving invariant

test_native_run_launch_signal.cpp was deleted along with the CV handoff it
covered. Three of its five cases went with the mechanism, correctly — but the
acceptance-identity case tested publish_acceptance, which survives, and
that gate is the only thing stopping a stale run from marking a task
accepted. test_native_run_acceptance.cpp restores direct coverage
(matching receipt stores, stale receipt does not, null accepted_state is
accepted); mutating the receipt.matches check makes it fail, so it is not
vacuous.

The dep_gen UT is rewritten from the deleted handoff mechanism to the
surviving same-thread invariant, including the cross-thread case that must
emit nothing rather than a partial deps.json.

Not addressed

CodeRabbit asked to reintroduce a LAUNCHING in-flight marker in
chip_worker.cpp. chip_worker.h:385-389 states all ChipWorker callers run
on the thread that called init(), so finalize() cannot race an in-flight
launch_native_run() and the phase gap it describes is not reachable. This
also matches the review, which called dropping Launching from both state
machines the right call.

Validation

  • 73/73 cpp UT (ctest -L no_hardware)
  • 218 passed — tests/ut/py/test_worker/test_host_worker.py
  • a2a3 onboard: host_build_graph 24 passed / 2 skipped; tensormap_and_ringbuffer 42 passed
  • a2a3sim: host_build_graph 20 passed / 4 skipped
  • The hbg dep_gen ST asserting all 6 edges passes onboard with
    --enable-dep-gen, confirming deps.json is still produced on the
    single-thread path.

simpler_wait_run and simpler_finalize_run reached drain_execution without
an attached device context. drain_execution synchronizes and destroys
streams, reads device memory via print_handshake_results, and frees device
allocations through its cleanup guard — all per-thread CANN state. Only the
single-threaded child progress loop kept this from surfacing: the
launch-time attach happened to cover both drains. simpler_wait_run is
public C API, bound as _wait_native_run under gil_scoped_release, so no
caller is obliged to stay on that thread.

Attach at both entry points, matching prepare, launch and poll. rtSetDevice
is idempotent on an already-attached thread, which is why poll already
calls it unconditionally. In finalize the existing attach moves above the
drain so one call covers both the drain and validate_runtime_impl. Both
attaches sit inside a try: attach_current_thread is not noexcept and these
are C API boundaries.

The silent `catch (...) {}` around each drain now logs. Drain is where
op-execute timeouts and 507018 cascades surface, and the rc alone does not
say which.

Deleting the executor handoff left the dep_gen host graph correct but
undocumented: the capture is thread_local, built by the orchestration
during prepare and read by emit during drain, which is sound only because
both now land on one thread. That invariant was unstated while the headers
still described the take/adopt transfer that used to enforce it, and
take_capture / adopt_capture / destroy_capture stayed exported in both
arches with no production callers. State the invariant where the graph
lives, and remove the dead API rather than leave it reading as live.

test_native_run_launch_signal.cpp was deleted with the CV handoff it
covered, but its acceptance-identity case tested publish_acceptance, which
survives — and that gate is the only thing stopping a stale run from
marking a task accepted. test_native_run_acceptance.cpp restores direct
coverage; mutating the receipt.matches check fails it. The dep_gen UT is
rewritten from the handoff mechanism to the same-thread invariant,
including the cross-thread case that must emit nothing.

Verified: 73/73 cpp UT; 218 host-worker py UT; a2a3 onboard
host_build_graph 24 passed / 2 skipped, tensormap 42 passed; a2a3sim
host_build_graph 20 passed / 4 skipped. The dep_gen ST asserting all 6
edges passes onboard, confirming deps.json is still produced.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR changes host dependency-graph capture from cross-thread snapshot transfer to same-thread, thread-local emission. Native and simulated run finalization now attach the calling thread before draining and validation. Tests cover graph emission and native-run acceptance.

Changes

Host graph lifecycle

Layer / File(s) Summary
Thread-local capture contract and implementation
src/a2a3/runtime/host_build_graph/..., src/a5/runtime/host_build_graph/...
Capture state remains thread-local. Snapshot transfer APIs were removed. Cross-thread emission returns -3 and reports the missing current-thread capture.
Run attachment and teardown integration
src/common/platform/.../c_api_shared.cpp, src/a2a3/platform/.../device_runner.cpp, docs/dfx/dep-gen.md
Native and simulated run paths attach the calling thread before drain and validation. Teardown documentation now describes same-thread host emission and device replay.
Behavior and acceptance tests
tests/ut/cpp/a2a3/test_dep_gen_host_graph.cpp, tests/ut/cpp/common/test_native_run_acceptance.cpp, tests/ut/cpp/CMakeLists.txt
Tests cover same-thread emission, cross-thread failure, capture reset, receipt generation checks, and acceptance without an acceptance-state pointer.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RunCaller
  participant simpler_finalize_run
  participant DeviceRunner
  participant HostBuildGraph
  RunCaller->>simpler_finalize_run: finalize run
  simpler_finalize_run->>DeviceRunner: attach calling thread
  simpler_finalize_run->>DeviceRunner: drain execution
  DeviceRunner->>HostBuildGraph: emit same-thread graph
  simpler_finalize_run->>DeviceRunner: validate runtime state
Loading

Possibly related PRs

Poem

A rabbit checked the graph at dawn,
No thread-bound snapshot hopped along.
The runner binds, then drains with care,
Fresh receipts pass; stale ones stay there.
Same-thread paths now leave a trace—
Hop, hop, to a cleaner place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% 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
Title check ✅ Passed The title clearly identifies the two primary changes: attaching the device before draining and removing the obsolete dep_gen capture API.
Description check ✅ Passed The description directly explains the device-attachment fixes, dep_gen API removal, test updates, validation, and the deliberate LAUNCHING decision.
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.

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.

@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: 1

🤖 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 `@docs/dfx/dep-gen.md`:
- Line 398: Update the dep_gen support matrix to consistently include a5
host-direct orchestration: revise the statements around the a2a3-only host
support and the claim that a5 lacks dep_gen, while retaining the existing a5
device-runner hookup details. State the supported a5 behavior once and ensure
all three related entries agree.
🪄 Autofix

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: 13356f8c-0ae1-42d2-9071-03bc42121165

📥 Commits

Reviewing files that changed from the base of the PR and between 4f41773 and 0bc02e8.

📒 Files selected for processing (12)
  • docs/dfx/dep-gen.md
  • src/a2a3/platform/onboard/host/device_runner.cpp
  • src/a2a3/platform/sim/host/device_runner.cpp
  • src/a2a3/runtime/host_build_graph/host/dep_gen_host_graph.cpp
  • src/a2a3/runtime/host_build_graph/runtime/dep_gen_host_graph.h
  • src/a5/runtime/host_build_graph/host/dep_gen_host_graph.cpp
  • src/a5/runtime/host_build_graph/runtime/dep_gen_host_graph.h
  • src/common/platform/onboard/host/c_api_shared.cpp
  • src/common/platform/sim/host/c_api_shared.cpp
  • tests/ut/cpp/CMakeLists.txt
  • tests/ut/cpp/a2a3/test_dep_gen_host_graph.cpp
  • tests/ut/cpp/common/test_native_run_acceptance.cpp

Comment thread docs/dfx/dep-gen.md
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