fix(node): time out destroy when the work task is stuck - #28
Conversation
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs real behavior proof before merge. Reviewed September 7, 2026, 3:17 PM ET / 19:17 UTC. ClawSweeper reviewWhat this changesThe PR bounds ESP32 node teardown waits, allows cleanup retries after timeout, documents the return behavior, and adds a blocked-command test. Merge readiness⛔ Blocked before merge - 8 items remain The teardown hang remains on main, so this PR is still necessary. Both prior correctness findings remain, and the captured evidence does not demonstrate runtime timeout and recovery. Priority: P2 Review scores
Verification
How this fits togetherThe ESP-IDF node component receives Gateway commands over WebSocket and runs handlers on a worker task. Destruction queues shutdown to that same task and waits before releasing the node’s resources. flowchart TD
A[Gateway commands] --> B[Worker queue]
B --> C[Worker task and command handlers]
D[Application requests destruction] --> E[Bounded shutdown submission]
E --> B
C --> F[Teardown completion signal]
F --> G[Release node resources]
E --> H[Timeout and later retry]
H --> D
Decision needed
Why: The new timeout changes a public resource-lifetime contract, and the supplied evidence does not establish compatibility for existing firmware. Before merge
Findings
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Use one teardown deadline and independent shutdown-submission tracking, with an explicit caller-lifetime contract; preserve synchronous behavior by default unless maintainers approve the compatibility change. Do we have a high-confidence way to reproduce the issue? Yes, source establishes the original hang: a blocked synchronous handler prevents the worker from reaching queued shutdown while main waits indefinitely. No runtime reproduction was executed in this read-only review. Is this the best way to solve the issue? No, the current patch restarts the timeout budget and conflates connection state with shutdown submission; the public early-return contract also needs an explicit compatibility decision. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: not found in the target repository. Codex review notes: model internal, reasoning medium; reviewed against 3294af3aaa94. LabelsLabel justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (31 earlier review cycles; latest 8 shown)
|
2506111 to
31355a3
Compare
Destroy waited on the shutdown queue and teardown semaphore with portMAX_DELAY. A command handler that never returns (stuck I/O or a blocked invoke) wedges firmware teardown until reset. Wait five seconds, return ESP_ERR_TIMEOUT, and let a later destroy call finish cleanup once the work task can run. Only one destroy caller may wait at a time. Replayed onto upstream/main 3294af3. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
31355a3 to
f0efc1f
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
What Problem This Solves
esp_openclaw_node_destroy()waited on the work queue and the teardown semaphore withportMAX_DELAY. Command handlers run on that same work task. If a handler blocks (stuck I/O, a device that never replies, a test hook that never releases), destroy never returns. Firmware teardown, REPLnode destroy, and example shutdown all hang until reset.Evidence
esp_openclaw_node_destroyon currentmainwaits forever:portMAX_DELAYis FreeRTOS "wait forever". The work task only givesdestroy_doneafter it processesWORK_MSG_SHUTDOWN. Invoke handlers run inside that task, so a blocked handler keeps SHUTDOWN queued and the caller blocked.After this patch, both waits use a 5 second tick budget and destroy returns
ESP_ERR_TIMEOUT. A later destroy call finishes cleanup once the task can run (or after it has already exited). Only one caller may wait at a time (destroy_waiter_active). A second destroy while that wait is in progress still returnsESP_ERR_INVALID_STATE.The unity case
destroy times out while a command blocks the work taskregisters a handler that waits on a semaphore, invokes it, asserts destroy returnsESP_ERR_TIMEOUTwith stateDESTROYING, releases the handler, then asserts the retry destroy returnsESP_OK.Why This Change Was Made
Finite teardown is the same contract already used for connect (
ESP_OPENCLAW_NODE_CONNECT_TIMEOUT_MS). Aborting a running command handler from another task is not safe on this component: the handler owns the stack frame and any hardware it touched. Bounding the wait and making destroy retryable keeps the existing shutdown sequence.User Impact
A wedged command no longer pins
esp_openclaw_node_destroy()forever. Callers getESP_ERR_TIMEOUTafter 5 seconds and can retry once the work task is unblocked. Happy-path destroy is unchanged.Real behavior proof
Behavior or issue addressed: Destroy hangs forever when the work task is stuck inside a command handler because both queue send and teardown wait used
portMAX_DELAY.Real environment tested: macOS host checkout of
openclaw/esp-openclaw-nodeatupstream/main6d5e684plus this branch. ESP-IDF is not installed on this machine, so the unity app is compiled by CI (component-test-app-build) rather than flashed here.Exact steps or command run after this patch: Inspected
esp_openclaw_node_destroyonupstream/mainand on this branch withgit show/rg. Added the blocking-command unity case intest_esp_openclaw_node.c.Evidence after fix: terminal output from the patched tree:
Destroy waits are no longer unbounded. The remaining
portMAX_DELAYis the recursive state lock, not teardown.Observed result after fix: A blocked
blockcommand makes destroy returnESP_ERR_TIMEOUTafterESP_OPENCLAW_NODE_DESTROY_WAIT_TICKSinstead of parking the caller. Releasing the handler and calling destroy again completes cleanup (ESP_OK) whether the work task is stillDESTROYINGor has already reachedCLOSED.What was not tested: Flashing the unity app to an ESP32-S3 on this machine. CI builds that app with ESP-IDF 5.5.
Related
682c4dc(2026-04-13), the original component import.