feat(serial): close/reopen the port and gate auto-reconnect (#192) - #193
Open
kipavy wants to merge 1 commit into
Open
feat(serial): close/reopen the port and gate auto-reconnect (#192)#193kipavy wants to merge 1 commit into
kipavy wants to merge 1 commit into
Conversation
A serial console holds /dev/ttyUSB0 for as long as its tab is open, and a drop starts the reconnect backoff, which reclaims the port every few seconds. Both fight the flashing tool on a board like an ESP32: the user has to close the whole tab to flash, and even then the loop can take the port back before esptool gets it. The serial status bar now carries two buttons: - A plug toggle that releases the port while keeping the tab, its scrollback and its config, and reopens it on the next click. The close marks the session disconnected before releasing, so the resulting serial-closed event is not read as a drop worth chasing. - An auto-reconnect toggle, stored on the connection so it survives a restart and follows the device. Off means a drop leaves the port free and the reopen button armed. Quick-connect sessions have no connection to persist it on and keep it for the session's lifetime instead. Reopening also exposed a defect in the backend: the previous read thread outlives its port on a cloned descriptor, so two threads would split the device's output and the older one's close would tear down the session that replaced it. Sessions now carry a generation a thread checks before continuing or reporting a close.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #192.
The problem
A serial console holds the port for as long as its tab is open, and a drop starts the reconnect backoff, which reclaims the port every few seconds for ~3 minutes. Both fight the flashing tool on a board like an ESP32: today the only way to free
/dev/ttyUSB0is to close the whole tab, and even then a stale loop can take the port back before esptool gets it.What changed
Two buttons in the serial status bar:
disconnectedbefore releasing the port, so theserial-closedevent that follows is not read as a drop worth chasing (handleSessionClosedonly starts the loop for a session that still readsconnected).serial_auto_reconnecton the connection, so it survives a restart and follows the device rather than the tab. It is also editable in the serial connection form. Off means a drop leaves the port free and the reopen button armed. Quick-connect serial sessions carry a sentinel connection id nothing resolves, so there the preference lives on the session for its lifetime.Default is on, and
undefinedreads as on, so existing connections behave exactly as before.Backend defect found on the way
Reopening a port left the previous read thread alive on its cloned descriptor: two threads would split the device's output between them, and the older one's
serial-closedwould tear down the session that replaced it. This predates the change, but a reopen button makes it reachable in one click. Sessions now carry a generation that a read thread checks before continuing and before reporting a close.Refactors carried along
StatusBarIconButtoninTerminalStatusBar— the SSH and serial reconnect buttons were the same button written twice; the serial copy is gone and the SSH one now uses the shared component.SettingRowinformShared— the icon + label + right-hand control row existed 7 times inConnectionForm; all of them now use the shared component instead of this change adding an 8th.markSessionDisconnectednext to the existingmarkSessionConnecting/markSessionErrorhelpers.Verification
Gates, all from the worktree:
tsc --noEmit— exit 0vitest run— 516 files, 3964 tests, 0 failurescargo test— 342 passed, 0 failedcargo fmt --all --check— cleancargo clippy --workspace --all-targets -- -D warnings— exit 0New tests: the auto-reconnect gate in
reconnectBackoff.test.ts, theserialAutoReconnectEnabledresolver,closeSerialPort/setSerialAutoReconnectinsessionStore.serialPort.test.ts(including that the session is marked disconnected before the port is released), and the generation guard inserial/connect_tests.rsviaTTYPort::pair().Live-verified in a headless container against an emulated device — a pty published as
/dev/ttyUSB99, printing a line a second, with SIGUSR1 as "unplug":disconnected, tab and config survive, and 0 output frames arrive over the next 3s while the button flips to "Reopen". The backoff never reclaimed the port.connected, 6 output frames in 4s, button flips back to "Close".disconnectedwithin 200ms and stays disconnected for the full 14s watch. The toggle is written to the connection (serial_auto_reconnect: false).connectingand the loop recovers toconnectedon its own once the device returns.Not proven here: the actual "port busy" contention. A pty does not enforce exclusive access the way a USB serial driver does, so the emulator can show that Voltius stops holding and stops reclaiming the port, but not that esptool then wins it. That last step needs real hardware.