use super::super::config::*;
use super::NetWriter;
impl NetWriter {
pub(super) async fn handle_small_probe(&mut self) {
let probe_tick_due = is_probe_tick_due(self.sent_cnt);
let mut need_probe = if let Some(sndbuf_cap) = self.get_cached_sndbuf() {
self.bytes_since_probe >= small_probe_stride(sndbuf_cap)
} else {
probe_tick_due
};
if !need_probe && probe_tick_due {
if let (Some(p), Some(sndbuf_cap)) = self.peek_kernel_queues()
&& sndbuf_cap > 0
{
let pct = (p.saturating_mul(100) / sndbuf_cap) as u8;
let avg = self.estimate_avg_len();
if pct >= emergency_pct_for(avg) {
need_probe = true;
}
}
if !need_probe && self.time_gate_due(NET_BACKOFF_SMALL_PROBE_MS) {
need_probe = true;
}
}
if need_probe {
let avg = self.recalc_avg_now();
if let Some(cap) = self.get_cached_sndbuf() {
self.maybe_toggle_nodelay(cap, avg);
}
self.maybe_backoff().await;
self.last_probe_at = Some(std::time::Instant::now());
self.bytes_since_probe = 0;
}
}
pub(super) async fn handle_large_probe(&mut self) {
let mut need_probe = false;
if let Some(sndbuf_cap) = self.get_cached_sndbuf() {
let avg = if self.avg_write_len > 0.0 {
self.avg_write_len as usize
} else {
self.estimate_avg_len()
};
let stride = large_probe_stride(sndbuf_cap, avg);
if self.bytes_since_probe >= stride && self.time_gate_due(NET_BACKOFF_LARGE_PROBE_MS) {
need_probe = true;
}
if self.bytes_since_probe >= stride {
self.bytes_since_probe = 0;
}
} else if is_probe_tick_due(self.sent_cnt) {
need_probe = true;
}
if need_probe {
let avg = self.recalc_avg_now();
if let Some(cap) = self.get_cached_sndbuf() {
self.maybe_toggle_nodelay(cap, avg);
}
self.maybe_backoff().await;
}
}
pub(super) fn time_gate_due(&self, min_ms: u64) -> bool {
let now = std::time::Instant::now();
let min_dur = std::time::Duration::from_millis(min_ms);
self.last_probe_at
.map(|t| now.saturating_duration_since(t) >= min_dur)
.unwrap_or(true)
}
pub(super) fn current_avg_len(&self) -> usize {
self.avg_write_len.max(1.0) as usize
}
pub(super) fn recalc_avg_now(&mut self) -> usize {
let avg = if self.avg_writes_acc > 0 {
let denom = (self.avg_writes_acc as usize).max(1);
(self.avg_bytes_acc / denom).max(1)
} else {
self.current_avg_len()
};
self.avg_write_len = avg as f64;
self.avg_bytes_acc = 0;
self.avg_writes_acc = 0;
avg
}
pub(super) fn estimate_avg_len(&self) -> usize {
if self.avg_writes_acc > 0 {
(self.avg_bytes_acc / (self.avg_writes_acc as usize).max(1)).max(1)
} else {
self.current_avg_len()
}
}
}
#[cfg(test)]
mod probe_tests {
use super::*;
use std::time::Instant;
fn create_test_writer() -> NetWriter {
NetWriter::test_stub()
}
#[tokio::test]
async fn test_time_gate_due_first_call() {
let writer = create_test_writer();
assert!(writer.time_gate_due(100));
}
#[tokio::test]
async fn test_time_gate_due_within_interval() {
let mut writer = create_test_writer();
writer.last_probe_at = Some(Instant::now());
assert!(!writer.time_gate_due(100));
}
#[tokio::test]
async fn test_time_gate_due_after_interval() {
let mut writer = create_test_writer();
let past_time = Instant::now() - std::time::Duration::from_millis(150);
writer.last_probe_at = Some(past_time);
assert!(writer.time_gate_due(100));
}
fn create_test_writer_with_config() -> NetWriter {
NetWriter::test_stub()
}
#[test]
fn test_current_avg_len_minimum_value() {
let writer = create_test_writer_with_config();
assert!(writer.current_avg_len() >= 1);
}
#[test]
fn test_current_avg_len_with_positive_average() {
let mut writer = create_test_writer_with_config();
writer.avg_write_len = 1500.5;
assert_eq!(writer.current_avg_len(), 1500);
}
#[test]
fn test_recalc_avg_now_with_accumulators() {
let mut writer = create_test_writer_with_config();
writer.avg_bytes_acc = 3000;
writer.avg_writes_acc = 2;
let average = writer.recalc_avg_now();
assert_eq!(average, 1500);
assert_eq!(writer.avg_write_len, 1500.0);
assert_eq!(writer.avg_bytes_acc, 0);
assert_eq!(writer.avg_writes_acc, 0);
}
#[test]
fn test_recalc_avg_now_without_accumulators() {
let mut writer = create_test_writer_with_config();
writer.avg_write_len = 800.0;
let average = writer.recalc_avg_now();
assert_eq!(average, 800);
}
#[test]
fn test_estimate_avg_len_with_accumulators() {
let mut writer = create_test_writer_with_config();
writer.avg_bytes_acc = 4500;
writer.avg_writes_acc = 3;
let estimated = writer.estimate_avg_len();
assert_eq!(estimated, 1500);
assert_eq!(writer.avg_bytes_acc, 4500);
assert_eq!(writer.avg_writes_acc, 3);
}
#[test]
fn test_estimate_avg_len_without_accumulators() {
let mut writer = create_test_writer_with_config();
writer.avg_write_len = 1200.0;
let estimated = writer.estimate_avg_len();
assert_eq!(estimated, 1200);
}
#[tokio::test]
async fn test_handle_small_probe_integration() {
let mut writer = create_test_writer();
writer.test_override_sndbuf(Some(1024 * 1024));
writer.bytes_since_probe = 65 * 1024; writer.test_set_backpressure_enabled(true);
writer.handle_small_probe().await;
assert!(writer.last_probe_at.is_some());
assert_eq!(writer.bytes_since_probe, 0);
}
#[tokio::test]
async fn test_handle_large_probe_integration() {
let mut writer = create_test_writer();
writer.test_override_sndbuf(Some(1024 * 1024));
writer.avg_write_len = 1500.0;
writer.bytes_since_probe = 200 * 1024;
writer.last_probe_at = Some(Instant::now() - std::time::Duration::from_millis(10));
writer.test_set_backpressure_enabled(true);
writer.handle_large_probe().await;
assert_eq!(writer.bytes_since_probe, 0); }
#[test]
fn test_zero_value_handling() {
let mut writer = create_test_writer_with_config();
writer.avg_write_len = 0.0;
writer.avg_bytes_acc = 0;
writer.avg_writes_acc = 0;
assert_eq!(writer.current_avg_len(), 1);
assert_eq!(writer.estimate_avg_len(), 1);
}
#[test]
fn test_performance_characteristics() {
let writer = create_test_writer_with_config();
let start = Instant::now();
for _ in 0..10000 {
let _ = writer.time_gate_due(100);
let _ = writer.current_avg_len();
let _ = writer.estimate_avg_len();
}
let duration = start.elapsed();
assert!(duration.as_millis() < 100); }
}