mirror of
https://github.com/rustdesk/hbb_common.git
synced 2026-10-20 15:37:37 +00:00
A peer that is killed, logged out or rebooted sends no reset over UDP, so a session only learns of it by timing out. Nothing here acts on that; these are the two observations a caller needs to. `rx_progress` counts arriving fragments. A message reaches the caller only once it is whole, so a large one - a clipboard image is the case that occurs - leaves the receive loop with nothing to observe for as long as the transfer takes, and a peer sending steadily looks exactly like one that died. A counter, not a timestamp: `Instant::now()` costs an order of magnitude more per read, and callers only compare successive samples. `webrtc_disconnected` exposes the ICE state webrtc-rs already raises ~5s after it stops hearing from the peer. It is deliberately not a fourth `WebRTCConnectionState`: that enum gates send and receive readiness and session cache admissibility, and a transient state must change neither. TCP and WebSocket answer for neither, on purpose: this change leaves their existing close, error and timeout behaviour exactly as it was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns
52 lines
1.5 KiB
Rust
52 lines
1.5 KiB
Rust
use std::sync::{
|
|
atomic::{AtomicU64, Ordering},
|
|
Arc,
|
|
};
|
|
|
|
/// Bytes received from the peer, counted before framing.
|
|
///
|
|
/// A message reaches the application only once it is complete, so a large one — a clipboard image
|
|
/// is the case that occurs in practice — leaves the receive loop with nothing to observe for as
|
|
/// long as the transfer takes, and a peer that is sending steadily looks identical to one that
|
|
/// died. This counter moves on every read, so liveness stays observable across such a message.
|
|
///
|
|
/// A counter rather than a timestamp on purpose: `Instant::now()` costs an order of magnitude more
|
|
/// than a relaxed add, and the reader only ever compares successive samples, so the clock is
|
|
/// needed once per sampling tick instead of once per read.
|
|
#[derive(Clone, Default)]
|
|
pub struct RxProbe(Arc<AtomicU64>);
|
|
|
|
impl RxProbe {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn add(&self, n: usize) {
|
|
self.0.fetch_add(n as u64, Ordering::Relaxed);
|
|
}
|
|
|
|
/// Changes whenever bytes arrived. Compare successive samples; the absolute value and its
|
|
/// wrap-around carry no meaning.
|
|
#[inline]
|
|
pub fn get(&self) -> u64 {
|
|
self.0.load(Ordering::Relaxed)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn clones_share_one_counter() {
|
|
let probe = RxProbe::new();
|
|
let cloned = probe.clone();
|
|
assert_eq!(probe.get(), 0);
|
|
cloned.add(7);
|
|
assert_eq!(probe.get(), 7);
|
|
probe.add(3);
|
|
assert_eq!(cloned.get(), 10);
|
|
}
|
|
}
|