Fix reply correlation and failure reporting - #32
Open
glopesdev wants to merge 1 commit into
Open
Conversation
A pending request is keyed by address and message type rather than address alone, and each key holds every waiter, so a reply satisfies only requests of its own type. Before, a write reply would satisfy a read on the same register. A transport failure in the reader thread is reported to every waiting request and to any that follow, in place of being re-raised inside a daemon thread where it was lost, so a removed port no longer presents as a device that went quiet. An error reply raises DeviceError, which keeps the reply, in place of a RuntimeError that formatted the frame into its message and discarded it.
bruno-f-cruz
requested changes
Aug 21, 2026
bruno-f-cruz
left a comment
Member
There was a problem hiding this comment.
I may be misunderstanding this and I can try later, but can we add a test that checks whether a single reply satisfies multiple requests?
For instance
Read(44)
Read(44)
Reply(44) <consumes both
Which shouldn't happen
Member
Actually nvm. The current api is blocking so this should not happen. Maybe something to worry about if we go asyn |
bruno-f-cruz
approved these changes
Aug 21, 2026
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.
A reply could satisfy the wrong request. Pending requests were keyed by address alone, so a
Readand aWriteon one register would answer each other. This needs no concurrency, since awritethat times out followed by areadreturns the late write reply as the read result, with nothing raised.Three changes follow from that, all inside
Device. The public surface gains one name,DeviceError, and nothing else moves, sinceREPLY_TIMEOUT,raise_on_error, theITransportcontract andTransportErrorare untouched. Eight tests drive all of it through a scripted transport that replies from insidewrite, so a reply is never dispatched before its waiter exists and no test depends on timing.Correlating a reply with its request
Requests are keyed by address and message type, and each key holds every waiter.
Bonsai.Harphas always done this, filtering onmessage.IsMatch(command.Address, command.MessageType)and subscribing before it writes.The wire carries no request identifier, so two identical requests in flight stay indistinguishable in the reply. The only difference is that both waiters are now satisfied by one reply instead of one being stranded on a replaced waiter.
Reporting a failure instead of losing it
A
TransportErrorraised inside the reader thread was re-raised there and lost, since the thread is a daemon with nobody watching, so a removed port presented as a device that had gone quiet and every later call blamed a timeout. It is now reported to every waiting request and recorded, so a later request fails with the actual cause. The C# read loop callsOnErroron its response subject for the same reason.An error reply raised
RuntimeErrorwith the frame formatted into the message string and then discarded. It now raisesDeviceError, which keeps the reply asreply, so a caller can inspect what the device actually sent. A conformance check needs exactly that to assert which error a device gave.