Companion: keep a queued message until the sync reply is actually written 🤖🤖 - #3362
Open
notbucki wants to merge 1 commit into
Open
Companion: keep a queued message until the sync reply is actually written 🤖🤖#3362notbucki wants to merge 1 commit into
notbucki wants to merge 1 commit into
Conversation
…tten CMD_SYNC_NEXT_MESSAGE took the oldest frame out of the offline queue and then ignored the return value of writeFrame(). BLE and WiFi refuse a frame while their send queue is full, and a serial write can come back short, so in each of those cases the message was gone for good while the client saw nothing and asked for the next one. The frame now stays queued until the transport reports that it took it whole; on failure the client simply repeats the request. MultiSerialInterface reported success only when every enabled interface accepted the frame. An interface that is enabled but has nobody attached (BLE advertising, an idle port) fails each write, which would have made the retry above resend the same message forever on any build with more than one interface. It now counts a frame as delivered once any enabled interface took it. Covered by a native googletest. For that to be safe, ArduinoSerialInterface::writeFrame() now reports 0 only when nothing went out. A torn frame (short write on a CDC port with a stalled host) counts as taken: a retry cannot mend it, the receiver would swallow the next header as the missing payload, so the message is consumed as it was before. The contract is spelled out on BaseSerialInterface; BLE, WiFi and Ethernet already behaved that way. Covered by a second native googletest. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
CMD_SYNC_NEXT_MESSAGEtakes the oldest frame out of the offline queue and then ignores the return value ofwriteFrame(). Both BLE interfaces and the WiFi interface return 0 while their send queue is full, and a serial write can come back short; in each of those cases the message is gone for good, while the client saw nothing and asks for the next one.Change
MyMesh: the frame stays queued until the transport reports that it took it whole.getFromOfflineQueue()is split intopeekOfflineQueue()/popOfflineQueue(), and the sync handler pops only after a complete write. On failure the client simply repeats the request; nothing else changes for it.MultiSerialInterface::writeFrame()counted a frame as written only when every enabled interface accepted it. An interface that is enabled but has nobody attached (BLE advertising, an idle port) fails each write, which would turn the retry above into resending the same message forever on any build with more than one interface. It now reports success once any enabled interface took the frame. Single-interface builds, which is everything upstream ships, are unaffected: with one interface "any" and "all" coincide.ArduinoSerialInterface::writeFrame()reports 0 only when nothing went out. A torn frame (a short write on a CDC port whose host has stalled) counts as taken: a retry cannot mend it, the receiver would swallow the next header as the missing payload, so the message is consumed exactly as before this change. The contract is now spelled out onBaseSerialInterface; BLE, WiFi and Ethernet already behaved that way (0 while their send queue is full,lenotherwise).Testing
test_multi_serial_interface(4 cases; the first fails against the previousMultiSerialInterface, the other three pass on both versions, so it pins exactly the changed behaviour) andtest_arduino_serial_interface(5 cases: whole, nothing, torn header, torn payload, oversized).ArduinoSerialInterface.cppis added to the native build filter for that.heltec_v4_r8_companion_radio_usb,Heltec_v3_companion_radio_usb,RAK_4631_companion_radio_ble,heltec_v4_companion_radio_wifiandXiao_S3_WIO_companion_radio_serial(HWCDC, UART bridge, nRF52 BLE, WiFi, hardware serial).Known limits
isConnected()is unconditionallytrueandwrite()succeeds into the TX buffer whether or not a host reads it. Giving it a real connection state is what Companion USB interface: report real connection state, add flow control 🤖🤖 #3214 does; the two changes compose but do not depend on each other.Found while reviewing #3214, where flow control makes a 0 return from
writeFrame()a normal event rather than a rare one.Prepared with Claude Code (Claude Fable 5.1), which did the analysis, the patch and the test; a human reviewed and approved the change before submission. The patch was also put through an independent cross-provider review with Codex (
gpt-6-astra, reasoning effortmax) before posting; its one finding, that retrying after a torn serial write would corrupt the stream, led to thewriteFrame()contract above.