fix(daemon): close the serial port after deploy's post-flash monitor (#1426) - #1427
fix(daemon): close the serial port after deploy's post-flash monitor (#1426)#1427zackees wants to merge 1 commit into
Conversation
…1426) `detach_reader()` only drops the client from `reader_client_ids`; it does not touch `serial_handle`. Both other detach sites follow it with `close_port_after_grace_if_idle` to release the OS handle: - handlers/operations/monitor.rs:361 - handlers/websockets.rs:101 The deploy path's post-flash monitor did not, so after every deploy the `SerialSession` and its file descriptor stayed open for the life of the daemon. The next client then got EBUSY on a board that was enumerated and perfectly healthy. Observed on a FastLED bench: four distinct fbuild-daemon pids each held /dev/ttyACM0 across one session, every one of them after a deploy. `open()` took 13.3s while held and 0.00s once the daemon was killed. It reads as a wedged device -- the error even says "serial driver may be wedged" -- and `lsof` does not show it, because the fd lives in the daemon rather than in the CLI process that appears to be at fault. It also broke a real test path: FastLED's `autoresearch rp2350w --net-peer --ota` failed at its first RPC because the companion's port was still held by that same run's deploy. Plain `--net-peer` passes, and the difference is deploy duration -- 96.5s there versus 25.7s in OTA mode -- so the shorter deploy loses the race against the leaked handle. That intermittency is what made it look like an OTA-logic fault. Mirrors the monitor.rs cleanup exactly: same `has_clients` guard, same 2s grace, so a close -> immediate reconnect pattern still does not thrash the USB CDC handle. cargo check -p fbuild-daemon --all-targets: clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkufoNxfnNRU9psT3R9F51
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe deploy handler now checks for attached monitor clients after post-deploy reader detachment. If no clients remain, it schedules the physical serial handle for closure after a two-second grace period. ChangesSerial port cleanup
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to Post-deploy monitoring now releases an idle serial handle after a short grace period while preserving immediate reconnects. The change is ready to merge with no identified current-head blocking risk. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
|
Flagging a verification gap before this gets merged, since it is approved. What I actually ran: What I did not run: the That matters more than usual here because this change touches serial-session lifetime on the deploy path, which every flashed board goes through. If it regresses anything around session state or close generations, the blast radius is wide. I am raising it because I found the same gap elsewhere today: a change of mine passed The change itself is small and mirrors existing code — The bug is well evidenced. Four distinct Someone with a machine that can link the crate should run |
|
Retracting the verification gap I flagged above — the tests run clean: Together with I should correct the reason I gave for not running them earlier. I attributed the failed attempts to host memory pressure, and that was a misreading on my part: I was watching the So the coverage gap I described is closed, and the caution about merging without it no longer applies. |
Fixes #1426.
Cause
detach_reader()only removes the client fromreader_client_ids— it does not touchserial_handle. Both other detach sites follow it withclose_port_after_grace_if_idle()to release the OS handle:handlers/operations/monitor.rs:361handlers/websockets.rs:101The deploy path's post-flash monitor did not. So after every deploy the
SerialSessionand its file descriptor stayed open for the life of the daemon, and the next client gotEBUSYon a board that was enumerated and perfectly healthy.Why it was hard to see
Three things conspired:
open_port(...) exceeded 3s; serial driver may be wedged— so the natural response is to power-cycle or reflash a healthy board.lsof /dev/ttyACM0shows nothing, because the fd lives in the daemon, not in the CLI process that appears to fail. Only walking/proc/*/fdfinds it.Evidence
Four distinct
fbuild-daemonpids each held/dev/ttyACM0across one FastLED bench session — every one of them after a deploy:open()took 13.3 s while held and 0.00 s after killing the daemon. Killing it also let the same board deploy and pass RPC smoke immediately.It broke a real test path. FastLED's
autoresearch rp2350w --net-peer --otafailed at its first RPC because the companion board's port was still held by that same run's deploy:--net-peer--net-peer --otaThe shorter deploy loses the race against the leaked handle; the longer one wins it. That is the intermittency, and it is why the failure was initially attributed to OTA logic (FastLED#3956) rather than to serial lifetime.
Change
One call site, mirroring
monitor.rsexactly — samehas_clientsguard, same 2 s grace, so a close → immediate reconnect still does not thrash the USB CDC handle. The comment records why this site differed, since that is the non-obvious part.Verification
cargo check -p fbuild-daemon --all-targets— cleanNot yet verified end-to-end on the bench: that needs a daemon built from this branch and installed ahead of the pinned release, which I would rather not do to a live fixture mid-session. The reproduction is deterministic (
fbuild deploy, then check/proc/*/fdfor a holder), so it should be quick to confirm.Related: FastLED#3956 (the test path this unblocks), #1424 (same family — a healthy port reported unusable for a host-side reason).
🤖 Generated with Claude Code
https://claude.ai/code/session_01KkufoNxfnNRU9psT3R9F51
Summary by CodeRabbit