a2433d77cf
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 53s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
decky / build-publish (push) Successful in 19s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
ci / bench (push) Successful in 5m35s
docker / deploy-docs (push) Successful in 25s
arch / build-publish (push) Successful in 11m22s
android / android (push) Successful in 14m48s
deb / build-publish (push) Successful in 14m45s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m12s
ci / rust (push) Successful in 18m20s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m11s
flatpak / build-publish (push) Successful in 5m59s
windows-host / package (push) Successful in 8m3s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m47s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 4m27s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m6s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m52s
apple / swift (push) Successful in 5m12s
release / apple (push) Successful in 26m35s
apple / screenshots (push) Successful in 19m28s
Reversed/reordered delivery lets a FEC block reconstruct EARLY (data + recovery >= k), counting still-in-flight shards into fec_recovered_shards; window_loss_ppm then reported pure reordering as loss, inflating LossReports — which size adaptive FEC and, since the Automatic overhaul, feed the ABR controller (one severe window ends slow start FOR GOOD, so a reorder burst could permanently kneecap a session's climb). Early reconstruct stays (it's the latency-right choice); the accounting now nets it out. The reassembler counts a new fec_late_shards stat when a parity-restored data shard ARRIVES after all — matched exactly: the completed/abandoned-frame memory (ReassemblyWindow::completed, now a map) remembers which shards each terminal frame reconstructed, and a late arrival must match one (removed on hit), so wire duplicates of delivered shards and stragglers of failed blocks count nothing. In-flight blocks dedup via have_data. window_loss_ppm takes the late delta and estimates from (recovered - late), saturating across window boundaries; both callers (client core + probe) pass it. The e2e reorder tests now assert the NET equals the true kill count in both delivery orders, dup included (previously documented as a known inflation). Not mirrored into the C-ABI PunktfunkStats — the loss windows run in-core on every platform. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
3.1 KiB
Rust
72 lines
3.1 KiB
Rust
//! Live counters for the frame-pacing / quality logic and the web UI.
|
|
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
|
|
/// Immutable snapshot, copied across the C ABI as `PunktfunkStats`.
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
|
pub struct Stats {
|
|
pub frames_submitted: u64,
|
|
pub frames_completed: u64,
|
|
pub frames_dropped: u64,
|
|
pub packets_sent: u64,
|
|
pub packets_received: u64,
|
|
pub packets_dropped: u64,
|
|
/// Packets the host could NOT hand to the kernel because the send buffer was full (WouldBlock)
|
|
/// — the dominant loss mode at very high bitrate. Distinct from `packets_dropped` (recv-side
|
|
/// reassembler rejects). A non-zero, growing value means the link/encoder is outrunning the
|
|
/// send path; raise `net.core.wmem_max` / lower the bitrate, or wait for paced batched sending.
|
|
pub packets_send_dropped: u64,
|
|
pub fec_recovered_shards: u64,
|
|
/// Shards counted into [`fec_recovered_shards`](Self::fec_recovered_shards) that later ARRIVED
|
|
/// — reordered delivery lets a block reconstruct early from parity, so the still-in-flight
|
|
/// shards it "recovered" were late, not lost. Loss estimators must net this out
|
|
/// (`recovered - late`, see [`window_loss_ppm`](crate::quic::window_loss_ppm)) or plain
|
|
/// reordering reads as packet loss and spooks adaptive FEC + the bitrate controller.
|
|
/// Deliberately NOT mirrored into the C-ABI `PunktfunkStats` (loss windows run in-core).
|
|
pub fec_late_shards: u64,
|
|
pub bytes_sent: u64,
|
|
pub bytes_received: u64,
|
|
}
|
|
|
|
/// Atomic accumulators owned by a [`Session`](crate::session::Session). Snapshot to
|
|
/// [`Stats`] for readers. `Relaxed` ordering is fine: these are monotonic counters
|
|
/// read for display, never used to synchronize other memory.
|
|
#[derive(Default)]
|
|
pub struct StatsCounters {
|
|
pub frames_submitted: AtomicU64,
|
|
pub frames_completed: AtomicU64,
|
|
pub frames_dropped: AtomicU64,
|
|
pub packets_sent: AtomicU64,
|
|
pub packets_received: AtomicU64,
|
|
pub packets_dropped: AtomicU64,
|
|
pub packets_send_dropped: AtomicU64,
|
|
pub fec_recovered_shards: AtomicU64,
|
|
pub fec_late_shards: AtomicU64,
|
|
pub bytes_sent: AtomicU64,
|
|
pub bytes_received: AtomicU64,
|
|
}
|
|
|
|
impl StatsCounters {
|
|
#[inline]
|
|
pub fn add(counter: &AtomicU64, n: u64) {
|
|
counter.fetch_add(n, Ordering::Relaxed);
|
|
}
|
|
|
|
pub fn snapshot(&self) -> Stats {
|
|
let l = Ordering::Relaxed;
|
|
Stats {
|
|
frames_submitted: self.frames_submitted.load(l),
|
|
frames_completed: self.frames_completed.load(l),
|
|
frames_dropped: self.frames_dropped.load(l),
|
|
packets_sent: self.packets_sent.load(l),
|
|
packets_received: self.packets_received.load(l),
|
|
packets_dropped: self.packets_dropped.load(l),
|
|
packets_send_dropped: self.packets_send_dropped.load(l),
|
|
fec_recovered_shards: self.fec_recovered_shards.load(l),
|
|
fec_late_shards: self.fec_late_shards.load(l),
|
|
bytes_sent: self.bytes_sent.load(l),
|
|
bytes_received: self.bytes_received.load(l),
|
|
}
|
|
}
|
|
}
|