Internal: Fix reader reordering in FlowSynchronizationGroup::waitForDataAt - #711
Merged
Merged
Conversation
…ataAt std::forward_list::splice_after(pos, other, it) moves the element *following* `it`, not `*it`. waitForDataAt passed the iterator of the entry it wanted to promote, so it moved that entry's successor instead, and when the entry to promote was the last one in the list, `std::next(it)` was the end iterator and the call was undefined behaviour. In practice the list ends up empty, and every subsequent call iterates over nothing and returns MXL_STATUS_OK immediately: the group silently stops synchronizing after its first reordering, which is a failure that looks like success. A single-member group never reaches that branch, since the guard requires an entry whose observed source delay exceeds the head's, so only groups with two or more readers are affected. Pass the predecessor of the entry to be promoted, and keep track of it while walking the list. Signed-off-by: rochonma <mathieu.rochon@radio-canada.ca>
Signed-off-by: rochonma <mathieu.rochon@radio-canada.ca>
KimonHoffmann
approved these changes
Sep 9, 2026
KimonHoffmann
left a comment
Collaborator
There was a problem hiding this comment.
Thank you for catching this and fixing the issue!
The changes look good to me.
garethsb
approved these changes
Sep 9, 2026
|
Successfully created backport PR for |
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.
What's broken
A
FlowSynchronizationGroupwith two or more readers silently stops synchronizing after its first internal reordering. From that point on,mxlFlowSynchronizationGroupWaitForDataAtreturnsMXL_STATUS_OKin microseconds regardless of whether the data is actually there.It fails by looking like success: no error code, no log line, no crash. A consumer keeps calling the API and keeps being told the data is ready, and simply reads whatever happens to be in the ring.
Why
waitForDataAtopportunistically promotes the flow with the largest observed source delay to the front of_readers, so that later calls block on the slowest source first. The promotion was written as:std::forward_list::splice_after(pos, other, it)moves the element followingit, not*it- a singly-linked list can only unlink an element through its predecessor. So passing the iterator of the entry to be promoted moves that entry's successor instead.Two failure shapes:
std::next(current)is the end iterator and the call is undefined behavior. With libstdc++ the list's head pointer is nulled - the list becomes empty and its nodes are leaked.waitForDataAtthen iterates over nothing and returnsMXL_STATUS_OKimmediately, forever.A standalone reproducer of just the list operation, under ASan/UBSan:
When it triggers
It fires the first time a non-head member's observed source delay exceeds the head's record - in practice almost immediately whenever one source is consistently later than another, which is the normal use case for sync-group.
The fix
Track the predecessor of the entry being examined and pass that to
splice_after. After the splice the predecessor already precedes the loop cursor, so it must not be advanced - hence thecontinue.The promotion condition can only hold for an entry that is not the head, so the tracked predecessor is always a real element and never
before_begin().Testing
Adds
lib/tests/test_flow_sync_groups.cpp-Synchronization group : Repeated waits. Two discrete flows sharing a grain rate are added to one group; the second member's grain is committed deliberately late sot that the reordering is triggered on the first wait. A second wait on the same group then asks for a grain nobody will ever write and must time out. The reordering logic is independent of reader type - variant only selects which wait function is called - so the test uses two discrete flows for determinism.Before the fix that second wait returns
MXL_STATUS_OKinstantly instead of blocking for its 200 ms timeout. After the fix it times out correctly.Built and tested locally on Linux x86_64 with both the
Linux-GCC-ReleaseandLinux-Clang-Releasepresets:allbuilds warning-free andctestreports 60/60 passing on each.clang-format --dry-run --Werroroverlibandtoolsis clean.