Retire the blocking endpoint channel and tighten the mailbox contract - #1739
Conversation
📝 WalkthroughWalkthroughThe worker execution path now uses progress-based submission and polling. Synchronous endpoint execution and ChangesProgress-driven worker execution
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant WorkerThread
participant WorkerEndpoint
participant Mailbox
WorkerThread->>WorkerEndpoint: submit_progress dispatch
WorkerEndpoint->>Mailbox: publish task frame
WorkerThread->>WorkerEndpoint: activate_progress
WorkerThread->>WorkerEndpoint: poll_progress
Mailbox-->>WorkerEndpoint: acceptance or completion event
WorkerEndpoint-->>WorkerThread: progress result
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
tests/ut/cpp/hierarchical/test_remote_endpoint.cpp (1)
798-800: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRename the test to match the new behavior.
The assertion now checks a synchronous throw from
submit_progress. The test nameBareHostPointerWithoutSidecarIsEndpointFailurestill describes an endpoint-failure completion, which this test no longer exercises.♻️ Proposed rename
-TEST(RemoteEndpoint, BareHostPointerWithoutSidecarIsEndpointFailure) { +TEST(RemoteEndpoint, BareHostPointerWithoutSidecarRejectedAtSubmission) {🤖 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 798 - 800, Rename the test containing the submit_progress EXPECT_THROW assertion from BareHostPointerWithoutSidecarIsEndpointFailure to a name describing the synchronous submission-time rejection, rather than an endpoint-failure completion.
🤖 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/worker-manager.md`:
- Around line 147-148: Update section 3.3 in the worker-manager documentation to
remove the obsolete “blocking chip dispatch” wording and describe only the
progress-driven dispatch path, keeping the surrounding sticky-marker behavior
accurate.
In `@src/common/hierarchical/worker_manager.cpp`:
- Around line 496-506: The WorkerThread::loop() in-flight path currently spins
while poll_progress remains non-blocking. Add a short bounded cv_ wait when
inflight_ is nonzero, using the existing mutex and condition variable, while
preserving immediate processing for queued work and shutdown handling.
In `@tests/ut/py/test_worker/test_host_buffer_registration.py`:
- Line 250: Move task_frame.release() into the test’s existing finally block so
it executes on both success and assertion-failure paths, alongside
mailbox_buf.release(). Keep the normal assertions and cleanup behavior otherwise
unchanged.
---
Nitpick comments:
In `@tests/ut/cpp/hierarchical/test_remote_endpoint.cpp`:
- Around line 798-800: Rename the test containing the submit_progress
EXPECT_THROW assertion from BareHostPointerWithoutSidecarIsEndpointFailure to a
name describing the synchronous submission-time rejection, rather than an
endpoint-failure completion.
🪄 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: 8e70b010-cc78-46fd-b299-93ca6b553bb8
📒 Files selected for processing (13)
docs/task-flow.mddocs/worker-manager.mdpython/bindings/worker_bind.hpython/simpler/worker.pysrc/common/hierarchical/remote_endpoint.cppsrc/common/hierarchical/remote_endpoint.hsrc/common/hierarchical/worker_manager.cppsrc/common/hierarchical/worker_manager.htests/ut/cpp/hierarchical/test_remote_endpoint.cpptests/ut/cpp/hierarchical/test_scheduler.cpptests/ut/py/test_worker/test_admission_fence.pytests/ut/py/test_worker/test_error_propagation.pytests/ut/py/test_worker/test_host_buffer_registration.py
💤 Files with no reviewable changes (2)
- src/common/hierarchical/remote_endpoint.cpp
- src/common/hierarchical/remote_endpoint.h
6da2830 to
367418b
Compare
Once both endpoint kinds became progress-driven, the blocking channel lost its last production caller. WorkerEndpoint::run was still a pure virtual on the interface, and run_with_accept, WorkerThread::dispatch_process, the non-progress half of WorkerThread::loop, LocalMailboxEndpoint::run, RemoteL3Endpoint::run and the Python base-frame _TASK_READY branch all existed only to serve it. Nothing but the unit tests reached any of them, so the progressable() predicate had no false case left to select and is gone with them. The tests that did reach it covered invariants that outlive the channel, so they move to the progress path rather than being dropped: - LocalMailboxPublishesAcceptanceBeforeCompletion and AcceptanceIsReadableAfterTaskDone still pin that acceptance is a distinct event ahead of completion, and that it survives a TASK_DONE that lands with no poll in between — now asserted over submit_progress/poll_progress. The lease they check travels in the published task frame, not the control base frame. - RemoteTaskErrorMapsToTaskFailure keeps its TASK_FAILURE mapping, which poll_progress reproduces unchanged. - BareHostPointerWithoutSidecarIsRejectedAtSubmission changes shape honestly: encoding happens while publishing, so an unsidecar'd bare pointer is now rejected at submit_progress instead of reported as a completion. It matches the message, since submit_progress has several other runtime_error paths. - SuccessCompletionMapsToSuccess is dropped as redundant — TaskDispatchUsesProgressSubmissionAndPolling already covers that path; its last_frame assertion moves there. - FakeEndpoint becomes progress-driven, queueing the completion its next poll reports, which preserves the completion-time acceptance run_with_accept gave. _run_mailbox_loop now states its real contract. The `len(buf) >= 2 * MAILBOX_FRAME_SIZE` guard admitted a buffer that no production mailbox ever produces — all three are MAILBOX_SIZE — and the only short buffer in the tree was one test fixture, which CPython sizes exactly rather than page-rounding. The fixture allocates a whole mailbox, so the guard and both `task_buf is not None` checks go away and `buf` is a whole mailbox by construction. The frame_index ternary in publish_task_frame reads as if slot_id were a frame number; it is a native pipeline slot bounded by the runtime PipelineContract, which a comment now states. ProgressStopReleasesWaitingControl polled progress as a fallback when the control future was not ready, so it passed whether or not request_progress_stop actually woke the waiter — the one thing it exists to prove. Without the fallback it passes in 50 ms and hangs when the wakeup is removed. Stale references the removal left behind are updated in the same change: the docs' blocking-dispatch sentences, the dispatch_process mentions in the bindings and error-propagation docstrings, and the four build_task_payload throws that still named RemoteL3Endpoint::run. Two stale test comments are corrected: the loop polls three words per iteration, not two, and the admission-fence handler takes the task buffer. Verified: 73/73 cpp UT; 1189 passed / 13 skipped py UT; a2a3 onboard sweep 54 passed plus 24 passed / 2 skipped in the resource phase, sdma lane 1 passed; a2a3sim sweep 45 passed plus 21 passed / 4 skipped.
Follow-ups to the review on #1728, which merged as
9a03d92c. All five items addressed.② Blocking channel deleted (was: "delete or explain")
Once both endpoint kinds became progress-driven, the blocking channel lost its
last production caller. Deleted end to end:
WorkerEndpoint::run(pure virtual) andrun_with_acceptoff the interfaceLocalMailboxEndpoint::run/run_with_accept,RemoteL3Endpoint::runWorkerThread::dispatch_processand the non-progress half ofWorkerThread::loop_TASK_READYbranchprogressable()— with no blocking path left, itsfalsecase selects nothingThe review's survey was one caller short. It listed only C++ UTs, but
test_l3_sub_worker_maps_rewrites_and_unmaps_host_bufferscripts_TASK_READYon the base frame, so deleting the Python branch broke it. That test is
rescripted onto the task frame.
Tests reaching the channel covered invariants that outlive it, so they move to
the progress path rather than being dropped:
LocalMailboxPublishesAcceptanceBeforeCompletionAcceptanceIsReadableAfterTaskDoneTASK_DONElanding with no poll in between.RemoteTaskErrorMapsToTaskFailurepoll_progressreproduces theTASK_FAILUREmapping unchanged.BareHostPointerWithoutSidecarIsRejectedAtSubmissionsubmit_progressrather than reported as a completion. Renamed to match, and it asserts the message sincesubmit_progresshas several otherruntime_errorpaths.SuccessCompletionMapsToSuccessTaskDispatchUsesProgressSubmissionAndPollingalready covers it. Itslast_frameassertion moved there.FakeEndpointrun_with_acceptused to give it.① Guard removed, fixture fixed
Verified both halves of the claim before acting: all three production mailboxes
are
MAILBOX_SIZE, and CPython'sSharedMemoryreports the exact requestedlength (
size=64→len(buf) == 64), so a one-frame buffer is not page-roundedback into range. The fixture now allocates a whole mailbox, and the guard plus
both
task_buf is not Nonechecks are gone —bufis a whole mailbox byconstruction. A short buffer now fails fast at
_buffer_field_addrwith a clearValueErrorduring setup rather than producing a garbage address.③ Invariant stated at the ternary
frame_indexreads as ifslot_idwere a frame number. A present-tense commentnow states it is a native pipeline slot bounded by the runtime
PipelineContract, mapped 1:1 by two-frame endpoints and pinned to frame 0 bysingle-frame ones.
④ Fallback removed — and verified non-vacuous
ProgressStopReleasesWaitingControlpolled progress when the control future wasnot ready, so it passed whether or not
request_progress_stopwoke the waiter —the one thing it exists to prove. Fallback removed; verified by mutation:
with
command_cv_.notify_all()deleted the test hangs, and with it restored itpasses in 50 ms.
⑤ Stale comments
Three words per iteration, not two; the admission-fence handler takes the task
buffer.
Stale references the removal left behind
Per
doc-consistency.md§1, grepping the deleted names surfaced references mychange made wrong —
docs/worker-manager.md(blocking-dispatch wording, ×3),docs/task-flow.md,python/bindings/worker_bind.h,python/simpler/worker.py,test_error_propagation.py, and the fourbuild_task_payloadthrows that stillnamed
RemoteL3Endpoint::run. All updated to describe the paths that now exist.Validation
73/73cpp UT (ctest -L no_hardware)1189passed / 13 skipped — fulltests/ut/pysdmalane 1 passedOne open thread
CodeRabbit suggests a bounded
cv_wait inWorkerThread::loop()so thein-flight branch does not spin a core. Declined and left unresolved for a human
call:
codestyle.mdrule 5 forbids backoff timers on the dispatch path, nothingnotifies
cv_on child completion (so await_forwould be a pure timer thatadds latency to every task), and the spin is pre-existing — this PR only deleted
the other loop branch. A real wakeup primitive would be the rule-compliant fix
and deserves its own PR.