fix: a lost packet reserves the samples it owed instead of vanishing from the timeline
This commit is contained in:
1 parent
85ba835d61
commit
5915fc8421
4 files changed
+183
-9
No files matched your search
@@ -167,6 +167,24 @@ class BufferStore {
|
||||
std::vector<std::shared_ptr<ChannelBuffer>> channels_;
|
||||
};
|
||||
|
||||
/// One reserved, invalidated span: samples the counter says exist and the stream has not
|
||||
/// delivered. Its stored slots hold NaN, so a reader needs no special case -- the table is
|
||||
/// what lets a *late* packet find the slots it belongs to, and what the band draws.
|
||||
struct Hole {
|
||||
f64 x_begin = 0.0; ///< stream seconds of the hole's first slot
|
||||
f64 x_end = 0.0; ///< stream seconds of the hole's last slot
|
||||
/// Absolute **time-ring** slot of the first reserved slot, and the count of them. The
|
||||
/// time ring is the one clock every channel shares, so this is the only slot address
|
||||
/// that is unambiguous: per-channel value rings run at `time_stride * stride[i]`, which
|
||||
/// differs between channels. A consumer that needs a *channel's* slots derives them from
|
||||
/// `tick_begin` and that channel's stride rather than reusing these.
|
||||
u64 slot_begin = 0;
|
||||
u64 slot_count = 0;
|
||||
u64 tick_begin = 0; ///< absolute base tick of the hole's first raw sample
|
||||
std::uint32_t counter_first = 0; ///< counter of the first missing packet
|
||||
std::uint32_t packets = 0; ///< packets the hole is worth
|
||||
};
|
||||
|
||||
/// Live connection and counter state, readable from the UI thread.
|
||||
struct StreamStats {
|
||||
std::atomic<std::uint64_t> packets{0};
|
||||
@@ -175,6 +193,8 @@ struct StreamStats {
|
||||
std::atomic<std::uint64_t> reconnects{0};
|
||||
std::atomic<bool> connected{false};
|
||||
std::atomic<bool> have_config{false};
|
||||
std::atomic<std::uint64_t> holes{0}; ///< reserved invalidated spans
|
||||
std::atomic<std::uint64_t> hole_slots{0}; ///< stored slots those spans cover
|
||||
};
|
||||
|
||||
/// What the plot is showing, which is one decision made once per frame: the bind, the
|
||||
@@ -301,6 +321,15 @@ class ScopeModel {
|
||||
/// is armed.
|
||||
BufferStore& live();
|
||||
|
||||
/// Records a reserved hole. Caller holds `mutex()` (the producer already does). The table
|
||||
/// is capped at `kMaxHoles`; past that the oldest is dropped, because an unbounded list of
|
||||
/// gaps is a leak and the oldest gap is the one furthest from being backfilled.
|
||||
static constexpr std::size_t kMaxHoles = 64;
|
||||
void note_hole(const Hole& h);
|
||||
const std::vector<Hole>& holes() const noexcept { return holes_; }
|
||||
/// Drops holes whose time-ring slots the ring has already discarded.
|
||||
void forget_holes_before(u64 slot);
|
||||
|
||||
/// Builds the captured event from the slice of the live buffer covering
|
||||
/// `[edge - pre_s, edge + post_s]` in stream seconds, and publishes it atomically.
|
||||
/// The stamps are re-based so the edge sits at x = 0. The plot binds to this
|
||||
@@ -544,6 +573,7 @@ class ScopeModel {
|
||||
|
||||
Partition part_;
|
||||
std::unique_ptr<BufferStore> store_;
|
||||
std::vector<Hole> holes_;
|
||||
TriggerConfig trigger_{};
|
||||
StreamStats stats_{};
|
||||
f64 epoch_ = 0.0;
|
||||
|
||||
Reference in new issue
Block a user