fix: a full reorder ring keeps the arriving packet and counts the overflow, instead of dropping it silently

A packet that needed a slot and found none was neither held nor delivered: no
counter recorded it and the consumer reserved a hole nothing could ever backfill.
It now commits the gap to free a slot and takes the ordering decision once more
against the sequence that commit left behind, bounded to one retry; past that it
is delivered in order with the counters it skipped as its `lost`, never flagged
late, because a packet ahead of the sequence has no hole to be looked up by. A
duplicate can now replace its slot at a full ring, as hold_packet's own comment
already promised, and the 20 ms default window is a named constant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-10-09 06:50:43 +02:00
1 parent cb1bbd2b7b
commit 176ef1df9f
6 files changed
+113 -38

No files matched your search

+5
View File
@@ -216,6 +216,10 @@ struct StreamStats {
std::atomic<std::uint64_t> resequenced{0};
/// Out-of-order packets delivered behind the sequence, past the window.
std::atomic<std::uint64_t> late{0};
/// Times the client's reorder ring was full when a packet needed a slot, so the gap was
/// committed early. Nothing is dropped; the window was just shorter than asked for, and a
/// non-zero reading is what says the ring is undersized for the stream.
std::atomic<std::uint64_t> overflows{0};
/// Packets the client still refused outright (a duplicate, or any late packet while
/// resequencing is disabled). Counted since the beginning and displayed nowhere until
/// now, which is why an inversion had to be inferred from `packets` and `frames`
@@ -547,6 +551,7 @@ class ScopeModel {
/// Rebuilds buffers from the current channel list. Caller holds `mutex()`.
void rebuild_locked();
private:
void rebuild_channels();
/// Re-appends one synthetic signal per math channel, after the stream's own. Called under the