Adapt runtime stream retransmit pacing
ci / test (push) Waiting to run
ci / fuzz-smoke (push) Waiting to run
ci / windows-client (push) Waiting to run
ci / package-release (linux-x86_64, ubuntu-latest) (push) Waiting to run
ci / package-release (macos-aarch64, macos-14) (push) Waiting to run
ci / package-release (macos-x86_64, macos-13) (push) Waiting to run
ci / package-release (windows-x86_64, windows-latest) (push) Waiting to run
ci / publish-gitea-release (push) Blocked by required conditions
ci / remote-bench (push) Waiting to run
ci / test (push) Waiting to run
ci / fuzz-smoke (push) Waiting to run
ci / windows-client (push) Waiting to run
ci / package-release (linux-x86_64, ubuntu-latest) (push) Waiting to run
ci / package-release (macos-aarch64, macos-14) (push) Waiting to run
ci / package-release (macos-x86_64, macos-13) (push) Waiting to run
ci / package-release (windows-x86_64, windows-latest) (push) Waiting to run
ci / publish-gitea-release (push) Blocked by required conditions
ci / remote-bench (push) Waiting to run
This commit is contained in:
+97
-20
@@ -5850,7 +5850,8 @@ async fn run_terminal(
|
||||
let mut last_packet_at = Instant::now();
|
||||
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
||||
let mut resize_tick = tokio::time::interval(Duration::from_millis(250));
|
||||
let mut stream_retransmit_tick = tokio::time::interval(Duration::from_millis(200));
|
||||
let mut stream_retransmit_tick =
|
||||
tokio::time::interval(dosh::transport::ADAPTIVE_RETRANSMIT_MIN);
|
||||
let mut frame_gap_tick = tokio::time::interval(Duration::from_millis(250));
|
||||
let mut last_size = terminal_size();
|
||||
// React to terminal resize the instant it happens via SIGWINCH (mosh-style),
|
||||
@@ -5896,6 +5897,7 @@ async fn run_terminal(
|
||||
let mut stream_pending_opens: HashMap<u64, PendingStreamOpen> = HashMap::new();
|
||||
let mut stream_next_send_offset: HashMap<u64, u64> = HashMap::new();
|
||||
let mut stream_sent_data: HashMap<u64, BTreeMap<u64, PendingStreamChunk>> = HashMap::new();
|
||||
let mut stream_retransmit_srtt: Option<Duration> = None;
|
||||
let mut stream_next_recv_offset: HashMap<u64, u64> = HashMap::new();
|
||||
let mut stream_recv_pending: HashMap<u64, BTreeMap<u64, Vec<u8>>> = HashMap::new();
|
||||
let mut stream_retired: HashSet<u64> = HashSet::new();
|
||||
@@ -6746,8 +6748,14 @@ async fn run_terminal(
|
||||
continue;
|
||||
};
|
||||
last_packet_at = Instant::now();
|
||||
if stream_pending_opens.remove(&ok.stream_id).is_none() {
|
||||
let Some(pending_open) = stream_pending_opens.remove(&ok.stream_id) else {
|
||||
continue;
|
||||
};
|
||||
if pending_open.attempts == 1 {
|
||||
dosh::transport::observe_retransmit_rtt(
|
||||
&mut stream_retransmit_srtt,
|
||||
pending_open.last_sent.elapsed(),
|
||||
);
|
||||
}
|
||||
opened_streams.insert(ok.stream_id);
|
||||
stream_send_credit.entry(ok.stream_id).or_insert(STREAM_INITIAL_WINDOW);
|
||||
@@ -6851,6 +6859,7 @@ async fn run_terminal(
|
||||
ack_stream_data(
|
||||
&mut stream_sent_data,
|
||||
&mut stream_send_credit,
|
||||
&mut stream_retransmit_srtt,
|
||||
adjust.stream_id,
|
||||
adjust.received_offset,
|
||||
);
|
||||
@@ -7105,6 +7114,7 @@ async fn run_terminal(
|
||||
&cred,
|
||||
&mut send_seq,
|
||||
&mut stream_sent_data,
|
||||
stream_retransmit_srtt,
|
||||
).await?;
|
||||
retransmit_stream_opens(
|
||||
&socket,
|
||||
@@ -7112,6 +7122,7 @@ async fn run_terminal(
|
||||
&cred,
|
||||
&mut send_seq,
|
||||
&mut stream_pending_opens,
|
||||
stream_retransmit_srtt,
|
||||
).await?;
|
||||
}
|
||||
}
|
||||
@@ -8027,12 +8038,17 @@ async fn retransmit_stream_data(
|
||||
cred: &CachedCredential,
|
||||
send_seq: &mut u64,
|
||||
stream_sent_data: &mut HashMap<u64, BTreeMap<u64, PendingStreamChunk>>,
|
||||
stream_retransmit_srtt: Option<Duration>,
|
||||
) -> Result<()> {
|
||||
let now = Instant::now();
|
||||
let retransmit_after = dosh::transport::adaptive_retransmit_after(
|
||||
dosh::transport::DEFAULT_RETRANSMIT_AFTER,
|
||||
stream_retransmit_srtt,
|
||||
);
|
||||
let mut retransmit = Vec::new();
|
||||
for (stream_id, chunks) in stream_sent_data.iter_mut() {
|
||||
for chunk in chunks.values_mut() {
|
||||
if now.duration_since(chunk.last_sent) < Duration::from_millis(200) {
|
||||
if now.duration_since(chunk.last_sent) < retransmit_after {
|
||||
continue;
|
||||
}
|
||||
chunk.last_sent = now;
|
||||
@@ -8052,11 +8068,16 @@ async fn retransmit_stream_opens(
|
||||
cred: &CachedCredential,
|
||||
send_seq: &mut u64,
|
||||
stream_pending_opens: &mut HashMap<u64, PendingStreamOpen>,
|
||||
stream_retransmit_srtt: Option<Duration>,
|
||||
) -> Result<()> {
|
||||
let now = Instant::now();
|
||||
let retransmit_after = dosh::transport::adaptive_retransmit_after(
|
||||
dosh::transport::DEFAULT_RETRANSMIT_AFTER,
|
||||
stream_retransmit_srtt,
|
||||
);
|
||||
let mut retransmit = Vec::new();
|
||||
for (stream_id, pending) in stream_pending_opens.iter_mut() {
|
||||
if now.duration_since(pending.last_sent) < Duration::from_millis(200) {
|
||||
if now.duration_since(pending.last_sent) < retransmit_after {
|
||||
continue;
|
||||
}
|
||||
pending.last_sent = now;
|
||||
@@ -8215,28 +8236,39 @@ fn retire_stream_state(
|
||||
fn ack_stream_data(
|
||||
stream_sent_data: &mut HashMap<u64, BTreeMap<u64, PendingStreamChunk>>,
|
||||
stream_send_credit: &mut HashMap<u64, usize>,
|
||||
stream_retransmit_srtt: &mut Option<Duration>,
|
||||
stream_id: u64,
|
||||
received_offset: u64,
|
||||
) {
|
||||
let Some(sent) = stream_sent_data.get_mut(&stream_id) else {
|
||||
return;
|
||||
};
|
||||
let acked_offsets: Vec<u64> = sent
|
||||
.iter()
|
||||
.filter_map(|(offset, chunk)| {
|
||||
let end = chunk.offset.saturating_add(chunk.bytes.len() as u64);
|
||||
(end <= received_offset).then_some(*offset)
|
||||
})
|
||||
.collect();
|
||||
let mut acked_bytes = 0usize;
|
||||
for offset in acked_offsets {
|
||||
if let Some(chunk) = sent.remove(&offset) {
|
||||
acked_bytes = acked_bytes.saturating_add(chunk.bytes.len());
|
||||
let mut samples = Vec::new();
|
||||
let remove_stream = {
|
||||
let Some(sent) = stream_sent_data.get_mut(&stream_id) else {
|
||||
return;
|
||||
};
|
||||
let acked_offsets: Vec<u64> = sent
|
||||
.iter()
|
||||
.filter_map(|(offset, chunk)| {
|
||||
let end = chunk.offset.saturating_add(chunk.bytes.len() as u64);
|
||||
(end <= received_offset).then_some(*offset)
|
||||
})
|
||||
.collect();
|
||||
for offset in acked_offsets {
|
||||
if let Some(chunk) = sent.remove(&offset) {
|
||||
acked_bytes = acked_bytes.saturating_add(chunk.bytes.len());
|
||||
if chunk.attempts == 1 {
|
||||
samples.push(chunk.last_sent.elapsed());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if sent.is_empty() {
|
||||
sent.is_empty()
|
||||
};
|
||||
if remove_stream {
|
||||
stream_sent_data.remove(&stream_id);
|
||||
}
|
||||
for sample in samples {
|
||||
dosh::transport::observe_retransmit_rtt(stream_retransmit_srtt, sample);
|
||||
}
|
||||
add_stream_credit(stream_send_credit, stream_id, acked_bytes);
|
||||
}
|
||||
|
||||
@@ -10794,7 +10826,7 @@ mod tests {
|
||||
},
|
||||
)]);
|
||||
|
||||
retransmit_stream_opens(&sender, addr, &cred, &mut send_seq, &mut pending)
|
||||
retransmit_stream_opens(&sender, addr, &cred, &mut send_seq, &mut pending, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -10815,6 +10847,51 @@ mod tests {
|
||||
assert_eq!(pending[&44].attempts, 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn client_stream_retransmit_uses_observed_rtt() {
|
||||
let receiver = tokio::net::UdpSocket::bind("127.0.0.1:0").await.unwrap();
|
||||
let sender = tokio::net::UdpSocket::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = receiver.local_addr().unwrap();
|
||||
let cred = CachedCredential {
|
||||
server: "host".to_string(),
|
||||
session: "term".to_string(),
|
||||
mode: "forward-only".to_string(),
|
||||
udp_host: "127.0.0.1".to_string(),
|
||||
udp_port: addr.port(),
|
||||
client_id: [1; 16],
|
||||
session_key: [2; 32],
|
||||
session_key_id: protocol::session_key_id(&[2; 32]),
|
||||
attach_ticket: Vec::new(),
|
||||
attach_ticket_psk: [3; 32],
|
||||
last_rendered_seq: 0,
|
||||
};
|
||||
let mut send_seq = 10;
|
||||
let mut pending = HashMap::from([(
|
||||
44,
|
||||
PendingStreamOpen {
|
||||
target_host: "127.0.0.1".to_string(),
|
||||
target_port: 8080,
|
||||
last_sent: Instant::now() - Duration::from_millis(50),
|
||||
attempts: 1,
|
||||
},
|
||||
)]);
|
||||
let mut srtt = None;
|
||||
dosh::transport::observe_retransmit_rtt(&mut srtt, Duration::from_millis(25));
|
||||
|
||||
retransmit_stream_opens(&sender, addr, &cred, &mut send_seq, &mut pending, srtt)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut buf = [0u8; 2048];
|
||||
let (n, _) = tokio::time::timeout(Duration::from_secs(1), receiver.recv_from(&mut buf))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let packet = protocol::decode(&buf[..n]).unwrap();
|
||||
assert_eq!(packet.header.kind, PacketKind::StreamOpen);
|
||||
assert_eq!(pending[&44].attempts, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_options_accept_check_and_role_flags() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user