Skip to content

Refactor: make worker endpoints uniformly progressable - #1728

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
Crane-Liu:codex/endpoint-progressable-lanes
Aug 7, 2026
Merged

Refactor: make worker endpoints uniformly progressable#1728
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
Crane-Liu:codex/endpoint-progressable-lanes

Conversation

@Crane-Liu

@Crane-Liu Crane-Liu commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replace parallel active/staged worker state with explicit lane records and independent endpoint-capacity accounting
  • drive capacity-one local endpoints (sim, A5, L4+, and SUB) through the dedicated task frame without enabling successor staging
  • preserve logical pipeline lease identity on capacity-one endpoints while mapping the run onto physical task frame 0
  • make remote TASK traffic progressable with incremental non-blocking socket send/receive while preserving the ordered control lane

Remote control requests share the ordered command socket, so a control operation now waits for an in-flight remote task to complete or stop. Its worst-case added latency includes the task's remaining runtime, bounded by the progress deadline/stop path.

Testing

  • pre-commit run --files <changed files> (all hooks passed)
  • ctest --test-dir tests/ut/cpp/build-pr1694-followup -LE requires_hardware --output-on-failure (90/90)
  • pytest tests/ut -m "not requires_hardware" -q (1175 passed, 13 skipped, 14 deselected)
  • targeted former failures in test_host_worker.py, test_admission_fence.py, and test_host_buffer_registration.py (4 passed)
  • a2a3sim and a5sim vector examples
  • a2a3 hardware vector example via task-submit (task_20260806_201128_193519427714)

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Worker execution now uses explicit task frames and nonblocking progress for local and remote endpoints. WorkerThread tracks active and staged lanes with dispatch identity. Remote transports poll progress replies incrementally. Documentation and tests cover capacity-one execution, lifecycle handling, and shutdown.

Changes

Unified Worker Progress Execution

Layer / File(s) Summary
Task-frame execution
python/simpler/worker.py, src/common/hierarchical/worker_manager.h, tests/ut/cpp/hierarchical/test_scheduler.cpp
Mailbox loops select task frames independently from control frames. Worker handlers read task data from the selected frame. Tests cover capacity-one progress execution and frame-specific completion state.
Remote progress transport
src/common/hierarchical/remote_endpoint.*, tests/ut/cpp/hierarchical/test_remote_endpoint.cpp
Remote transports submit frames incrementally and poll replies without blocking. Endpoint commands validate replies, serialize with control operations, and handle stop and error states.
Active and staged lane scheduling
src/common/hierarchical/worker_manager.*
WorkerThread uses explicit active and staged lane records. Enqueue, activation, completion, and release operations validate lane ownership by dispatch identity.
Lifecycle and endpoint model documentation
docs/worker-manager.md
Documentation describes endpoint capacity, unified progress execution, lane accounting, shutdown behavior, and remote readiness gating.

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

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant WorkerThread
  participant RemoteL3Endpoint
  participant RemoteL3SocketTransport
  participant RemoteWorker
  WorkerThread->>RemoteL3Endpoint: submit_progress(ring, dispatch)
  RemoteL3Endpoint->>RemoteL3SocketTransport: submit_progress_frame(frame)
  RemoteL3SocketTransport->>RemoteWorker: send progress frame incrementally
  WorkerThread->>RemoteL3Endpoint: poll_progress(progress)
  RemoteL3Endpoint->>RemoteL3SocketTransport: poll_progress_reply(...)
  RemoteL3SocketTransport-->>RemoteL3Endpoint: return completed reply
  RemoteL3Endpoint-->>WorkerThread: report task completion
Loading

Poem

A rabbit sees frames hop,
While progress polls and waits.
Active lanes guard each task,
Staged lanes open gates.
Remote replies arrive in time—
The worker bounds ahead!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.94% 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 main change: making worker endpoints uniformly progressable.
Description check ✅ Passed The description directly explains the worker-state refactor, progressable endpoints, remote I/O changes, and testing performed.

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: 4

🧹 Nitpick comments (8)
src/common/hierarchical/remote_endpoint.cpp (3)

624-624: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider avoiding the per-dispatch frame copy.

progress_write_ = frame; copies the whole encoded task frame on every dispatch. The caller at line 822 builds the frame as a temporary and discards it. Task payloads carry the full argument blob, so this is one allocation plus one copy per task on the dispatch path.

Add a std::vector<uint8_t>&& overload to RemoteL3Transport::submit_progress_frame, or change the parameter to by-value and move into progress_write_.

🤖 Prompt for 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.

In `@src/common/hierarchical/remote_endpoint.cpp` at line 624, Update
RemoteL3Transport::submit_progress_frame to accept the frame by value or add an
rvalue-reference overload, then move it into progress_write_ instead of copying.
Update the temporary-frame call site near the dispatch path to pass ownership
while preserving existing lvalue callers.

873-887: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Align exception safety between the two stop paths.

request_progress_stop is noexcept and wraps its body in try { ... } catch (...) {}. report_progress_error performs the same three operations without any guard.

WorkerThread::loop() calls the error reporter from its own exception handlers for activate_progress and poll_progress. An exception escaping report_progress_error there propagates out of the worker loop thread. Wrap the body the same way, or state why transport_->shutdown() cannot throw on this path.

🤖 Prompt for 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.

In `@src/common/hierarchical/remote_endpoint.cpp` around lines 873 - 887, Update
RemoteL3Endpoint::report_progress_error to match the exception-safety behavior
of request_progress_stop: guard the mutex lock, progress state updates, and
transport_->shutdown() call with a catch-all so exceptions cannot escape
WorkerThread::loop()’s error-handling path.

799-803: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the locking precondition of finish_progress_command.

finish_progress_command mutates pending_task_ and notifies command_cv_ without taking command_mu_. It requires the caller to hold that mutex. poll_progress satisfies this today. The precondition is not stated, so a future caller can introduce a data race on pending_task_.

Add a comment that states the precondition, or add an assertion in debug builds.

♻️ Proposed change
+// The caller must hold `command_mu_`. This function mutates `pending_task_`
+// and notifies `command_cv_` without taking the mutex itself.
 void RemoteL3Endpoint::finish_progress_command(uint64_t sequence) {
🤖 Prompt for 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.

In `@src/common/hierarchical/remote_endpoint.cpp` around lines 799 - 803, Document
the locking precondition on RemoteL3Endpoint::finish_progress_command: state
that callers must hold command_mu_ before invoking it, since the method updates
pending_task_ and notifies command_cv_ without locking. Keep the existing
behavior unchanged; poll_progress already satisfies this requirement.
tests/ut/cpp/hierarchical/test_remote_endpoint.cpp (2)

387-407: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider asserting the progress failure path.

The test covers only EndpointOutcome::SUCCESS. FakeRemoteTransport already exposes next_error_code and next_error_message. Set them and assert that poll_progress reports EndpointOutcome::TASK_FAILURE with the propagated message. That covers lines 858-863 of RemoteL3Endpoint::poll_progress.

🤖 Prompt for 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.

In `@tests/ut/cpp/hierarchical/test_remote_endpoint.cpp` around lines 387 - 407,
Add a failure-path case to TaskDispatchUsesProgressSubmissionAndPolling using
FakeRemoteTransport::next_error_code and next_error_message before submission;
then assert poll_progress returns EndpointOutcome::TASK_FAILURE and propagates
the configured error message in the completion result, while preserving the
existing success assertions.

280-283: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a not-ready mode to the fake transport.

poll_progress_reply always returns true. The endpoint-level "reply not ready" path is therefore never exercised. That path is RemoteL3Endpoint::poll_progress line 852, which returns false and leaves pending_task_ occupied for a later poll.

That path is the core behavior this PR adds. The socket test at lines 620-647 covers the transport in isolation, not the endpoint.

Add a counter or flag that makes the fake return false for the first N calls. Then assert that poll_progress returns false, that pending_task_ survives, and that a later poll completes the same dispatch.

🤖 Prompt for 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.

In `@tests/ut/cpp/hierarchical/test_remote_endpoint.cpp` around lines 280 - 283,
Extend the fake transport used by poll_progress_reply with configurable
not-ready behavior, returning false for the first N polls before delivering the
reply. Add endpoint-level assertions that RemoteL3Endpoint::poll_progress
returns false while pending_task_ remains occupied, then verify a later poll
completes the same dispatch.
tests/ut/cpp/hierarchical/test_scheduler.cpp (1)

1203-1232: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering the capacity-one failure path.

This test covers only the success path. progressable() now returns true for task_frame_count=1, so capacity-one local endpoints newly use submit/poll. Add a case that sets the frame state to TASK_FAILED with a populated error region. Then assert that poll_progress reports EndpointOutcome::TASK_FAILURE and propagates the error message.

🤖 Prompt for 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.

In `@tests/ut/cpp/hierarchical/test_scheduler.cpp` around lines 1203 - 1232, The
test around CapacityOneMailboxUsesTheProgressTaskFrame covers only successful
completion; add a capacity-one failure case using the same LocalMailboxEndpoint
and progress submission flow. Set the task frame to TASK_FAILED with a populated
error region, then assert poll_progress returns COMPLETED with
EndpointOutcome::TASK_FAILURE and preserves the error message.
src/common/hierarchical/remote_endpoint.h (1)

74-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the literal 40 with a shared header-size constant.

40 is the wire frame-header size. It now appears as a bare literal here and at remote_endpoint.cpp lines 626, 628, 671, and 689. remote_endpoint.cpp line 539 already declares static constexpr size_t HEADER_BYTES = 40; inside read_frame. A wire-format change must update six independent sites.

Promote a single remote_l3::FRAME_HEADER_BYTES constant and use it at every site.

♻️ Proposed change
-    size_t progress_read_size_{40};
+    size_t progress_read_size_{remote_l3::FRAME_HEADER_BYTES};
🤖 Prompt for 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.

In `@src/common/hierarchical/remote_endpoint.h` at line 74, Replace the duplicated
frame-header size literals by promoting a shared remote_l3::FRAME_HEADER_BYTES
constant in the common header, then use it for progress_read_size_ and every
corresponding size calculation in read_frame and its related remote_endpoint.cpp
sites. Remove the local HEADER_BYTES definition and ensure all six wire-header
references use the shared constant.
python/simpler/worker.py (1)

1808-1809: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Check mailbox size before slicing the task frame.

_run_mailbox_loop slices buf[MAILBOX_FRAME_SIZE : 2 * MAILBOX_FRAME_SIZE], but memoryview slices clamp when out of range. The C++ endpoint constants and Python shared-memory allocation use MAILBOX_SIZE (control frame plus two task frames), so the current callers have space. Keep the task-frame allocation invariant in C++/Python, or add an explicit len(buf) >= 2 * MAILBOX_FRAME_SIZE check here.

🤖 Prompt for 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.

In `@python/simpler/worker.py` around lines 1808 - 1809, Update _run_mailbox_loop
to validate that buf has at least 2 * MAILBOX_FRAME_SIZE bytes before slicing
the task frame, while preserving the existing C++/Python mailbox allocation
invariant. Ensure undersized buffers are rejected explicitly rather than
allowing a clamped memoryview slice to proceed.
🤖 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 `@src/common/hierarchical/remote_endpoint.cpp`:
- Around line 891-894: Update run_control at
src/common/hierarchical/remote_endpoint.cpp:891-894 to include
progress_stop_requested_ in the command_cv_ wait predicate and throw using
progress_stop_reason_ when woken by that state. In
src/common/hierarchical/remote_endpoint.cpp:1115-1120, notify command_cv_ after
setting the stop state. In request_progress_stop and report_progress_error at
src/common/hierarchical/remote_endpoint.cpp:873-887, also notify all waiters
after setting progress_stop_requested_ and progress_stop_reason_.
- Around line 826-829: Update the catch-all unwind block in the relevant run
flow to wrap command_lane_.finish_reply(sequence) in a nested try/catch that
suppresses any exception, then rethrow the original exception unchanged; match
the existing guarded pattern used by run() and run_control.
- Around line 639-679: Update poll_progress_reply to reset
progress_command_active_ on every exception path, including check_health(),
timeout, send/receive failures, peer closure, invalid payloads, and reply
validation errors. Use a scope guard that clears the progress state unless the
call completes successfully; ensure nonblocking return-false paths and the final
successful return mark the operation as not failed.

In `@tests/ut/cpp/hierarchical/test_remote_endpoint.cpp`:
- Around line 638-643: Reduce the retry-loop bound in the completion polling
block around poll_progress_reply from 200 to 100 iterations, preserving the 10
ms sleep and completion condition so the test remains within the transport
deadline while allowing the expected 500 ms reply.

---

Nitpick comments:
In `@python/simpler/worker.py`:
- Around line 1808-1809: Update _run_mailbox_loop to validate that buf has at
least 2 * MAILBOX_FRAME_SIZE bytes before slicing the task frame, while
preserving the existing C++/Python mailbox allocation invariant. Ensure
undersized buffers are rejected explicitly rather than allowing a clamped
memoryview slice to proceed.

In `@src/common/hierarchical/remote_endpoint.cpp`:
- Line 624: Update RemoteL3Transport::submit_progress_frame to accept the frame
by value or add an rvalue-reference overload, then move it into progress_write_
instead of copying. Update the temporary-frame call site near the dispatch path
to pass ownership while preserving existing lvalue callers.
- Around line 873-887: Update RemoteL3Endpoint::report_progress_error to match
the exception-safety behavior of request_progress_stop: guard the mutex lock,
progress state updates, and transport_->shutdown() call with a catch-all so
exceptions cannot escape WorkerThread::loop()’s error-handling path.
- Around line 799-803: Document the locking precondition on
RemoteL3Endpoint::finish_progress_command: state that callers must hold
command_mu_ before invoking it, since the method updates pending_task_ and
notifies command_cv_ without locking. Keep the existing behavior unchanged;
poll_progress already satisfies this requirement.

In `@src/common/hierarchical/remote_endpoint.h`:
- Line 74: Replace the duplicated frame-header size literals by promoting a
shared remote_l3::FRAME_HEADER_BYTES constant in the common header, then use it
for progress_read_size_ and every corresponding size calculation in read_frame
and its related remote_endpoint.cpp sites. Remove the local HEADER_BYTES
definition and ensure all six wire-header references use the shared constant.

In `@tests/ut/cpp/hierarchical/test_remote_endpoint.cpp`:
- Around line 387-407: Add a failure-path case to
TaskDispatchUsesProgressSubmissionAndPolling using
FakeRemoteTransport::next_error_code and next_error_message before submission;
then assert poll_progress returns EndpointOutcome::TASK_FAILURE and propagates
the configured error message in the completion result, while preserving the
existing success assertions.
- Around line 280-283: Extend the fake transport used by poll_progress_reply
with configurable not-ready behavior, returning false for the first N polls
before delivering the reply. Add endpoint-level assertions that
RemoteL3Endpoint::poll_progress returns false while pending_task_ remains
occupied, then verify a later poll completes the same dispatch.

In `@tests/ut/cpp/hierarchical/test_scheduler.cpp`:
- Around line 1203-1232: The test around
CapacityOneMailboxUsesTheProgressTaskFrame covers only successful completion;
add a capacity-one failure case using the same LocalMailboxEndpoint and progress
submission flow. Set the task frame to TASK_FAILED with a populated error
region, then assert poll_progress returns COMPLETED with
EndpointOutcome::TASK_FAILURE and preserves the error message.
🪄 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: cb539989-1f47-4b80-9898-17aeb0647fd8

📥 Commits

Reviewing files that changed from the base of the PR and between 764f456 and c5b9792.

📒 Files selected for processing (8)
  • docs/worker-manager.md
  • python/simpler/worker.py
  • src/common/hierarchical/remote_endpoint.cpp
  • src/common/hierarchical/remote_endpoint.h
  • src/common/hierarchical/worker_manager.cpp
  • src/common/hierarchical/worker_manager.h
  • tests/ut/cpp/hierarchical/test_remote_endpoint.cpp
  • tests/ut/cpp/hierarchical/test_scheduler.cpp

Comment thread src/common/hierarchical/remote_endpoint.cpp Outdated
Comment thread src/common/hierarchical/remote_endpoint.cpp
Comment thread src/common/hierarchical/remote_endpoint.cpp
Comment thread tests/ut/cpp/hierarchical/test_remote_endpoint.cpp
@Crane-Liu
Crane-Liu force-pushed the codex/endpoint-progressable-lanes branch from c5b9792 to 0685f5b Compare August 7, 2026 07:26
- Replace parallel active and staged identity fields with explicit lane
  records while keeping capacity accounting independent.
- Drive capacity-one local and remote endpoints through submit/poll
  progress, mapping logical leases onto the available task frames.
- Preserve single-frame child-loop compatibility and harden remote
  progress I/O, stop, error, and control-wait handling.
- Cover single-frame, remote, SUB, L4, simulation, and hardware paths.
@Crane-Liu
Crane-Liu force-pushed the codex/endpoint-progressable-lanes branch from 0685f5b to 4e7c027 Compare August 7, 2026 07:35
@ChaoWao
ChaoWao merged commit 9a03d92 into hw-native-sys:main Aug 7, 2026
19 checks passed
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.

2 participants