From 7be6a0d541ac4e75ac93faa0a740c253a68c730c Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Tue, 31 Mar 2026 18:45:04 +0400 Subject: [PATCH] Fix IPv6 UDP TX: adaptive backoff on ENOBUFS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IPv6 UDP sends hit ENOBUFS much faster than IPv4 (smaller kernel buffers, NDP overhead). Fixed: - Adaptive backoff: 200us→10ms as errors accumulate, resets on success - Higher error threshold: 50k instead of 1k before stopping - Yield with sleep when errors have been seen recently Co-Authored-By: Claude Opus 4.6 (1M context) --- src/server.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/server.rs b/src/server.rs index a97f840..0f98e7d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -736,13 +736,17 @@ async fn udp_tx_loop( Err(e) => { consecutive_errors += 1; if consecutive_errors == 1 { - tracing::warn!("UDP TX send error: {} (target={})", e, target); + tracing::debug!("UDP TX send error: {} (target={})", e, target); } - if consecutive_errors > 1000 { + if consecutive_errors > 50000 { tracing::warn!("UDP TX: too many consecutive send errors, stopping"); break; } - tokio::time::sleep(Duration::from_micros(200)).await; + // Adaptive backoff: sleep longer as errors accumulate + let backoff = Duration::from_micros( + (200 + consecutive_errors.min(5000) as u64 * 10).min(10000) + ); + tokio::time::sleep(backoff).await; continue; } } @@ -766,9 +770,14 @@ async fn udp_tx_loop( } } None => { - // Unlimited: yield every 64 packets to keep system responsive + // Unlimited: yield every 64 packets. On ENOBUFS-prone systems + // (IPv6, macOS), sleep briefly to let the kernel drain buffers. if seq % 64 == 0 { - tokio::task::yield_now().await; + if consecutive_errors > 0 { + tokio::time::sleep(Duration::from_micros(500)).await; + } else { + tokio::task::yield_now().await; + } } } }