fix(serial): recover ATX/DC extension workers from transient read errors - #1525
fix(serial): recover ATX/DC extension workers from transient read errors#1525ilyongnoh-ux wants to merge 1 commit into
Conversation
runATXControl and runDCControl returned on the first serial read error, permanently terminating the goroutine. A single transient read failure left the ATX/DC extension silent until the whole app was restarted. Intentional teardown (unmount, extension switch) works by having reopenSerialPort replace the global port, which makes the existing reader fail - an implicit stop signal. To keep that behaviour while surviving transient errors, pass the port each worker was started with and compare port generations on error: - If the global port differs from the worker's port, it was replaced on purpose: log Debug and return. - Otherwise the error is transient: log Warn, sleep with an exponential backoff (1s doubling up to 30s), rebuild the reader and keep looping. The backoff resets after any successfully read line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e2fd043df8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // If the global port has been swapped out (unmount or extension | ||
| // switch replaces it via reopenSerialPort), the read error is an | ||
| // intentional shutdown signal for this worker: stop quietly. | ||
| if port != p { |
There was a problem hiding this comment.
Synchronize port replacement detection
When an extension is unmounted or switched, reopenSerialPort writes the package-level port while the control goroutine can reach this comparison immediately after Close unblocks ReadString; concurrently reading and writing an interface value is a Go data race and can produce race-detector failures or, in rare cases, observe a torn interface value. The same pattern appears in runDCControl, so the replacement signal should use synchronized state or an explicit shutdown channel rather than reading port directly from the worker.
Useful? React with 👍 / 👎.
What
runATXControlandrunDCControlexit permanently on the first serial read error. After any transient UART hiccup the extension stays silent until the app restarts, with only a single Warn line as evidence. To users this looks like a dead ATX/DC board.How
Both workers now take the port they were started with as a parameter. On a read error they distinguish two cases:
reopenSerialPort) — the worker stops quietly, matching the existing implicit-shutdown design;No new synchronization is introduced: the global
portis read the same way the existing press/command paths already access it.Testing
🤖 Generated with Claude Code