Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |     |   Wurzel  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum hystart.rs  Sprache: unbekannt

 
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! HyStart++ (RFC 9406) test suite

use std::time::Duration;

use neqo_common::qdebug;
use test_fixture::now;

use super::make_cc_hystart;
use crate::{
    cc::{
        CongestionController as _, HyStartCssBaseline, classic_cc::SlowStart as _,
        hystart::HyStart, tests::INITIAL_CWND,
    },
    packet::MIN_INITIAL_PACKET_SIZE,
    recovery::sent,
    rtt::RttEstimate,
    stats::{CongestionControlStats, SlowStartExitReason},
};

const BASE_RTT: Duration = Duration::from_millis(100);
const HIGH_RTT: Duration = Duration::from_millis(120);
const LOW_RTT: Duration = Duration::from_millis(80);

/// Helper to create a HyStart instance with pacing enabled (L=infinity).
fn make_hystart_paced() -> HyStart {
    HyStart::new(true, HyStartCssBaseline::default())
}

/// Helper to create a HyStart instance with pacing disabled (L=8).
fn make_hystart_unpaced() -> HyStart {
    HyStart::new(false, HyStartCssBaseline::default())
}

/// Helper to set up HyStart state through two rounds with the given RTT values.
/// Can be used to test CSS entry (when `new_rtt` triggers it) or non-entry (when it doesn't).
fn maybe_enter_css(
    hystart: &mut HyStart,
    base_rtt: Duration,
    new_rtt: Duration,
    cc_stats: &mut CongestionControlStats,
) {
    // First round with base RTT
    let window_end = HyStart::N_RTT_SAMPLE as u64;
    hystart.on_packet_sent(window_end, MIN_INITIAL_PACKET_SIZE);

    assert!(hystart.window_end().is_some_and(|pn| pn == window_end));

    // Collect N_RTT_SAMPLE samples with base RTT and end first round
    for i in 0..=window_end {
        hystart.on_packets_acked(
            &RttEstimate::new(base_rtt),
            i,
            INITIAL_CWND,
            cc_stats,
            now(),
        );
    }

    assert!(hystart.window_end().is_none());

    // Second round with new RTT value
    let window_end2 = 2 * HyStart::N_RTT_SAMPLE as u64;
    hystart.on_packet_sent(window_end2, MIN_INITIAL_PACKET_SIZE);

    assert!(hystart.window_end().is_some_and(|pn| pn == window_end2));

    // Collect N_RTT_SAMPLE samples with new RTT and end second round
    for i in window_end + 1..=window_end2 {
        hystart.on_packets_acked(&RttEstimate::new(new_rtt), i, INITIAL_CWND, cc_stats, now());
    }

    assert!(hystart.window_end().is_none());
}

/// Tests that rounds are started and finished correctly and that `window_end` is set accordingly.
#[test]
fn round_tracking_lifecycle() {
    let mut hystart = make_hystart_paced();

    // Before any packet is sent, window_end should be None
    assert!(hystart.window_end().is_none());

    // Start a round with window_end = 10
    let window_end = 10;
    hystart.on_packet_sent(window_end, MIN_INITIAL_PACKET_SIZE);
    assert_eq!(
        hystart.window_end(),
        Some(window_end),
        "First send should start round with window_end"
    );

    // Send more packets - window_end should not change during the round
    hystart.on_packet_sent(11, MIN_INITIAL_PACKET_SIZE);
    hystart.on_packet_sent(12, MIN_INITIAL_PACKET_SIZE);
    assert_eq!(
        hystart.window_end(),
        Some(window_end),
        "window_end should not change during round"
    );

    // Ack packets less than window_end - round should continue
    for pn in 0..(window_end - 1) {
        hystart.on_packets_acked(
            &RttEstimate::new(BASE_RTT),
            pn, // All < window_end
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
        assert_eq!(
            hystart.window_end(),
            Some(window_end),
            "Round should continue while largest_acked < window_end"
        );
    }
    assert!(
        hystart.rtt_sample_count() > 0,
        "We should have collected RTT samples in this round."
    );
    let min_rtt = hystart.current_round_min_rtt().unwrap();

    // Now ack window_end - this should end the round and switch over RTT tracking to the next.
    hystart.on_packets_acked(
        &RttEstimate::new(BASE_RTT),
        window_end, // largest_acked=window_end, round ends
        INITIAL_CWND,
        &mut CongestionControlStats::default(),
        now(),
    );
    assert!(
        hystart.window_end().is_none(),
        "Round should end when largest_acked >= window_end"
    );
    assert_eq!(
        hystart.rtt_sample_count(),
        0,
        "RTT sample count should have been reset for the next round"
    );
    assert_eq!(
        hystart.last_round_min_rtt(),
        Some(min_rtt),
        "RTT tracking should have been updated for the next round"
    );

    // Set next window_end
    let window_end2 = 100;
    hystart.on_packet_sent(window_end2, MIN_INITIAL_PACKET_SIZE);
    assert_eq!(
        hystart.window_end(),
        Some(window_end2),
        "New round should now have set new window_end"
    );
}

/// Tests that `current_round_min_rtt` is tracked correctly when packets are acked.
#[test]
fn rtt_sample_collection_tracks_minimum() {
    let mut hystart = make_hystart_paced();

    // First ACK with RTT of 100ms
    hystart.on_packets_acked(
        &RttEstimate::new(BASE_RTT),
        0,
        INITIAL_CWND,
        &mut CongestionControlStats::default(),
        now(),
    );
    assert_eq!(hystart.rtt_sample_count(), 1);
    assert_eq!(
        hystart.current_round_min_rtt(),
        Some(BASE_RTT),
        "First sample should set the minimum"
    );

    // Second ACK with RTT of 80ms (lower) - should update minimum
    hystart.on_packets_acked(
        &RttEstimate::new(LOW_RTT),
        1,
        INITIAL_CWND,
        &mut CongestionControlStats::default(),
        now(),
    );
    assert_eq!(hystart.rtt_sample_count(), 2);
    assert_eq!(
        hystart.current_round_min_rtt(),
        Some(LOW_RTT),
        "Lower RTT should update the minimum"
    );

    // Third ACK with RTT of 120ms (higher) - should NOT update minimum
    hystart.on_packets_acked(
        &RttEstimate::new(HIGH_RTT),
        2,
        INITIAL_CWND,
        &mut CongestionControlStats::default(),
        now(),
    );
    assert_eq!(hystart.rtt_sample_count(), 3);
    assert_eq!(
        hystart.current_round_min_rtt(),
        Some(LOW_RTT),
        "Higher RTT should not update the minimum"
    );
}

#[test]
#[expect(
    clippy::cast_possible_truncation,
    reason = "No truncation will happen for values from 1 to 10."
)]
fn rtt_sample_count_increments_per_ack() {
    let mut hystart = make_hystart_paced();

    assert_eq!(hystart.rtt_sample_count(), 0);

    for i in 0..10 {
        hystart.on_packets_acked(
            &RttEstimate::new(BASE_RTT),
            i,
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
        assert_eq!(hystart.rtt_sample_count(), (i + 1) as usize);
    }
}

#[test]
fn css_entry_not_triggered_with_insufficient_samples() {
    let mut hystart = make_hystart_paced();

    // First round to set baseline RTT
    let window_end1 = (HyStart::N_RTT_SAMPLE) as u64;
    hystart.on_packet_sent(window_end1, MIN_INITIAL_PACKET_SIZE);

    for i in 0..=window_end1 {
        hystart.on_packets_acked(
            &RttEstimate::new(BASE_RTT),
            i,
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    // Second round with increased RTT but insufficient samples
    let window_end2 = window_end1 + HyStart::N_RTT_SAMPLE as u64;
    hystart.on_packet_sent(window_end2, MIN_INITIAL_PACKET_SIZE);

    // Collect only N_RTT_SAMPLE - 1 samples, not enough to enter CSS with
    for i in (window_end1 + 1)..window_end2 {
        hystart.on_packets_acked(
            &RttEstimate::new(HIGH_RTT),
            i,
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    assert!(
        !hystart.in_css(),
        "CSS should not be entered with insufficient samples"
    );
}

#[test]
fn css_entry_triggered_on_rtt_increase() {
    let mut hystart = make_hystart_paced();
    let mut cc_stats = CongestionControlStats::default();

    // Use helper to set up two rounds with RTT increase
    // rtt_thresh = max(4ms, min(100ms / 8, 16ms)) = max(4ms, min(12.5ms, 16ms)) = 12.5ms
    // Since 120ms >= 100ms + 12.5ms, CSS should be entered
    maybe_enter_css(&mut hystart, BASE_RTT, HIGH_RTT, &mut cc_stats);

    assert!(hystart.in_css(), "CSS should be entered on RTT increase");
    assert_eq!(cc_stats.hystart_css_entries, 1);
}

#[test]
fn css_entry_not_triggered_on_small_rtt_increase() {
    let mut hystart = make_hystart_paced();

    // Use helper with small RTT increase that's below threshold
    // rtt_thresh = 12.5ms, but increase is only 5ms
    maybe_enter_css(
        &mut hystart,
        BASE_RTT,
        Duration::from_millis(105),
        &mut CongestionControlStats::default(),
    );

    assert!(
        !hystart.in_css(),
        "CSS should not be entered on small RTT increase"
    );
}

#[test]
fn css_entry_triggered_on_min_rtt_thresh() {
    let mut hystart = make_hystart_paced();
    let mut cc_stats = CongestionControlStats::default();

    // Test MIN_RTT_THRESH bound: very small base RTT
    // rtt_thresh = max(MIN_RTT_THRESH, min(10ms / 8, MAX_RTT_THRESH))
    //            = max(4ms, 1.25ms) = 4ms
    // 15ms >= 10ms + 4ms, so CSS should be entered
    maybe_enter_css(
        &mut hystart,
        Duration::from_millis(10),
        Duration::from_millis(15),
        &mut cc_stats,
    );

    assert!(
        hystart.in_css(),
        "CSS should be entered with MIN_RTT_THRESH"
    );
    assert_eq!(cc_stats.hystart_css_entries, 1);
}

#[test]
fn css_entry_triggered_on_max_rtt_thresh() {
    let mut hystart = make_hystart_paced();
    let mut cc_stats = CongestionControlStats::default();

    // Test MAX_RTT_THRESH bound: large base RTT
    // rtt_thresh = max(MIN_RTT_THRESH, min(200ms / 8, MAX_RTT_THRESH))
    //            = max(4ms, min(25ms, 16ms)) = 16ms
    // 218ms >= 200ms + 16ms, so CSS should be entered
    maybe_enter_css(
        &mut hystart,
        Duration::from_millis(200),
        Duration::from_millis(218),
        &mut cc_stats,
    );

    assert!(
        hystart.in_css(),
        "CSS should be entered with MAX_RTT_THRESH"
    );
    assert_eq!(cc_stats.hystart_css_entries, 1);
}

#[test]
fn css_growth_rate_is_one_quarter() {
    const NEW_ACKED: usize = 4 * MIN_INITIAL_PACKET_SIZE;
    let mut hystart = make_hystart_paced();

    maybe_enter_css(
        &mut hystart,
        BASE_RTT,
        HIGH_RTT,
        &mut CongestionControlStats::default(),
    );
    assert!(hystart.in_css(), "Should have entered CSS");

    let cwnd_increase = hystart.calc_cwnd_increase(NEW_ACKED, MIN_INITIAL_PACKET_SIZE);

    // In CSS, growth is divided by CSS_GROWTH_DIVISOR
    assert_eq!(
        cwnd_increase,
        NEW_ACKED / HyStart::CSS_GROWTH_DIVISOR,
        "CSS growth should be 1/{} of new_acked",
        HyStart::CSS_GROWTH_DIVISOR
    );
}

#[test]
fn css_exit_after_n_rounds() {
    let mut hystart = make_hystart_paced();
    let mut cc_stats = CongestionControlStats::default();
    maybe_enter_css(&mut hystart, BASE_RTT, HIGH_RTT, &mut cc_stats);
    assert!(hystart.in_css(), "Should have entered CSS");
    assert_eq!(hystart.css_round_count(), 1);

    // Note: maybe_enter_css already completed a partial round in CSS (count = 1) so we start with
    // round 2.
    for round in 2..=HyStart::CSS_ROUNDS {
        // Start a new round
        let new_window_end = round as u64 * 100;
        hystart.on_packet_sent(new_window_end, MIN_INITIAL_PACKET_SIZE);

        // Collect samples
        for i in 0..HyStart::N_RTT_SAMPLE {
            hystart.on_packets_acked(
                &RttEstimate::new(HIGH_RTT),
                i as u64,
                INITIAL_CWND,
                &mut cc_stats,
                now(),
            );
        }

        // End round by acking window_end
        let exit_slow_start = hystart.on_packets_acked(
            &RttEstimate::new(HIGH_RTT),
            new_window_end,
            INITIAL_CWND,
            &mut cc_stats,
            now(),
        );

        if round < HyStart::CSS_ROUNDS {
            assert!(
                exit_slow_start.is_none(),
                "Should not exit before {} rounds completed but exited after round {round}",
                HyStart::CSS_ROUNDS,
            );
            assert!(hystart.in_css(), "Should still be in CSS");
            assert_eq!(cc_stats.hystart_css_rounds_finished, round);
        } else {
            assert!(
                exit_slow_start.is_some(),
                "Should exit after {} rounds have completed",
                HyStart::CSS_ROUNDS
            );
            assert_eq!(cc_stats.hystart_css_rounds_finished, HyStart::CSS_ROUNDS);
        }
    }
}

#[test]
fn css_back_to_slow_start_on_rtt_decrease() {
    const CSS_BASELINE_RTT: Duration = HIGH_RTT;
    const LOWER_RTT: Duration = Duration::from_millis(110);
    let mut hystart = make_hystart_paced();
    let mut cc_stats = CongestionControlStats::default();
    maybe_enter_css(&mut hystart, BASE_RTT, CSS_BASELINE_RTT, & style='color:red'>mut cc_stats);
    assert!(hystart.in_css(), "Should have entered CSS");
    assert_eq!(cc_stats.hystart_css_entries, 1);

    // Start a new round in CSS
    let new_window_end = 300;
    hystart.on_packet_sent(new_window_end, MIN_INITIAL_PACKET_SIZE);

    // RTT decreases below baseline - should exit CSS
    for i in 0..HyStart::N_RTT_SAMPLE {
        hystart.on_packets_acked(
            &RttEstimate::new(LOWER_RTT),
            i as u64, // Less than window_end
            INITIAL_CWND,
            &mut cc_stats,
            now(),
        );
    }

    assert!(
        !hystart.in_css(),
        "Should exit CSS when RTT decreases below baseline"
    );
    assert_eq!(
        hystart.css_round_count(),
        0,
        "CSS round count should be reset"
    );
}

#[test]
fn css_exit_only_with_new_samples() {
    let mut hystart = make_hystart_paced();

    // First round with base RTT to set a baseline that we can compare RTT against
    let window_end = HyStart::N_RTT_SAMPLE as u64;
    hystart.on_packet_sent(window_end, MIN_INITIAL_PACKET_SIZE);

    assert!(hystart.window_end().is_some_and(|pn| pn == window_end));

    // Collect N_RTT_SAMPLE samples with base RTT and end first round
    for i in 0..=window_end {
        hystart.on_packets_acked(
            &RttEstimate::new(BASE_RTT),
            i,
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    assert!(hystart.window_end().is_none());

    // Start second round with a high window end
    let window_end2 = 300;
    hystart.on_packet_sent(window_end2, MIN_INITIAL_PACKET_SIZE);

    // Collect N_RTT_SAMPLE samples with higher RTT to enter CSS
    for _i in 0..HyStart::N_RTT_SAMPLE as u64 {
        hystart.on_packets_acked(
            &RttEstimate::new(HIGH_RTT),
            0,
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    assert!(hystart.in_css(), "Should have entered CSS");

    // ACK with low RTT should not exit CSS without new samples
    hystart.on_packets_acked(
        &RttEstimate::new(LOW_RTT),
        0,
        INITIAL_CWND,
        &mut CongestionControlStats::default(),
        now(),
    );

    assert!(
        hystart.in_css(),
        "Should still be in CSS after one low RTT ACK"
    );

    // Collect N_RTT_SAMPLE-1 more samples with low RTT
    for _i in 1..HyStart::N_RTT_SAMPLE as u64 {
        hystart.on_packets_acked(
            &RttEstimate::new(LOW_RTT),
            0,
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    assert!(
        !hystart.in_css(),
        "Should exit CSS after having enough samples"
    );
}

#[test]
fn css_alternative_baseline() {
    const CSS_ENTRY_RTT: Duration = HIGH_RTT;
    let mut hystart = HyStart::new(true, HyStartCssBaseline::EntryThreshold);
    maybe_enter_css(
        &mut hystart,
        BASE_RTT,
        CSS_ENTRY_RTT,
        &mut CongestionControlStats::default(),
    );
    assert!(hystart.in_css(), "Should have entered CSS");

    // Start a new round in CSS
    let new_window_end = 300;
    hystart.on_packet_sent(new_window_end, MIN_INITIAL_PACKET_SIZE);

    // RTT decreases just below CSS_ENTRY_RTT - should not exit CSS if using alternative baseline.
    // When using the RFC default then this would've exited.
    for i in 0..HyStart::N_RTT_SAMPLE {
        hystart.on_packets_acked(
            &RttEstimate::new(CSS_ENTRY_RTT.checked_sub(Duration::from_micros(1)).unwrap()),
            i as u64, // Less than window_end
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    assert!(
        hystart.in_css(),
        "Should still be in CSS after RTT decrease just below CSS_ENTRY_RTT"
    );

    // RTT decreases below the threshold that we entered CSS with - should exit CSS.
    // With BASE_RTT = 100ms we have a threshold of 112.5ms, so let's take a value of 112.499ms.
    let below_threshold_rtt = BASE_RTT + Duration::from_micros(12_499);
    hystart.on_packets_acked(
        &RttEstimate::new(below_threshold_rtt),
        0, // Less than window_end
        INITIAL_CWND,
        &mut CongestionControlStats::default(),
        now(),
    );

    assert!(
        !hystart.in_css(),
        "Should exit CSS when RTT decreases below alternative baseline"
    );
}

#[test]
fn css_exit_to_slow_start_restores_normal_growth() {
    const CSS_BASELINE_RTT: Duration = HIGH_RTT;
    const LOWER_RTT: Duration = Duration::from_millis(110);
    const NEW_ACKED: usize = 4 * MIN_INITIAL_PACKET_SIZE;
    let mut hystart = make_hystart_paced();
    maybe_enter_css(
        &mut hystart,
        BASE_RTT,
        CSS_BASELINE_RTT,
        &mut CongestionControlStats::default(),
    );
    assert!(hystart.in_css(), "Should have entered CSS");

    // Test CSS growth (1/CSS_GROWTH_DIVISOR rate)
    let cwnd_increase = hystart.calc_cwnd_increase(NEW_ACKED, MIN_INITIAL_PACKET_SIZE);
    assert_eq!(
        cwnd_increase,
        NEW_ACKED / HyStart::CSS_GROWTH_DIVISOR,
        "CSS growth should be 1/{}",
        HyStart::CSS_GROWTH_DIVISOR
    );

    // Start new round with lower RTT
    let new_window_end = 400;
    hystart.on_packet_sent(new_window_end, MIN_INITIAL_PACKET_SIZE);
    for i in 0..HyStart::N_RTT_SAMPLE {
        hystart.on_packets_acked(
            &RttEstimate::new(LOWER_RTT),
            i as u64, // Less than window_end
            INITIAL_CWND,
            &mut CongestionControlStats::default(),
            now(),
        );
    }

    assert!(!hystart.in_css(), "Should have exited CSS");

    // Test normal slow start growth (1:1 rate)
    let cwnd_increase = hystart.calc_cwnd_increase(NEW_ACKED, MIN_INITIAL_PACKET_SIZE);
    assert_eq!(cwnd_increase, NEW_ACKED, "Normal SS growth should be 1:1");
}

#[test]
fn l_limit_paced_no_cap() {
    let mut hystart = make_hystart_paced(); // L = infinity
    hystart.on_packet_sent(0, MIN_INITIAL_PACKET_SIZE);

    // Try to increase by more than NON_PACED_L * SMSS
    let cwnd_increase =
        hystart.calc_cwnd_increase(100 * MIN_INITIAL_PACKET_SIZE, MIN_INITIAL_PACKET_SIZE);

    assert_eq!(
        cwnd_increase,
        100 * MIN_INITIAL_PACKET_SIZE,
        "Paced should have no cap"
    );
}

#[test]
fn l_limit_unpaced_is_capped() {
    let mut hystart = make_hystart_unpaced(); // L = NON_PACED_L
    hystart.on_packet_sent(0, MIN_INITIAL_PACKET_SIZE);

    // Try to increase by more than NON_PACED_L * SMSS
    let cwnd_increase =
        hystart.calc_cwnd_increase(100 * MIN_INITIAL_PACKET_SIZE, MIN_INITIAL_PACKET_SIZE);

    assert_eq!(
        cwnd_increase,
        HyStart::NON_PACED_L * MIN_INITIAL_PACKET_SIZE,
        "Unpaced should cap at L * SMSS"
    );
}

/// Integration test that is run through a `ClassicCongestionControl` instance and moves through the
/// full slowstart -> CSS -> congestion avoidance lifetime while continuously ACK'ing and sending
/// packets.
#[test]
fn integration_full_slow_start_to_css_to_ca() {
    let mut cc = make_cc_hystart(true);
    let mut stats = CongestionControlStats::default();
    let mut now = now();

    let base_rtt = BASE_RTT;
    let increased_rtt = HIGH_RTT;
    let base_rtt_est = RttEstimate::new(base_rtt);
    let increased_rtt_est = RttEstimate::new(increased_rtt);

    assert_eq!(cc.ssthresh(), None, "Should start in slow start");

    let mut next_send: u64 = 0;
    let mut next_ack: u64 = 0;
    let mut css_detected = false;
    let mut ca_detected = false;

    // Send initial cwnd worth of packets.
    let initial_cwnd_packets = cc.cwnd() / MIN_INITIAL_PACKET_SIZE;
    for _ in 0..initial_cwnd_packets {
        let pkt = sent::make_packet(next_send, now, MIN_INITIAL_PACKET_SIZE);
        cc.on_packet_sent(&pkt, now, false);
        next_send += 1;
    }

    // Wait 1 `base_rtt` for first ACK to arrive.
    now += base_rtt;

    // Continuous send/ACK alternation:
    // ACK'ing packet 0 ends round 1, next send starts round 2 and so on.
    let max_iterations = 1000; // Enough for multiple CSS rounds and CA entry
    for iteration in 0..max_iterations {
        // ACK the next packet
        let ack_pn = next_ack;

        // Have `base_rtt` for the first cwnd that was sent before the loop. Have `increased_rtt`
        // for all subsequent packets to trigger and go through CSS.
        let rtt_to_use = if ack_pn < initial_cwnd_packets as u64 {
            base_rtt
        } else {
            increased_rtt
        };
        let rtt_est = if ack_pn < initial_cwnd_packets as u64 {
            &base_rtt_est
        } else {
            &increased_rtt_est
        };

        let pkt = sent::make_packet(
            ack_pn,
            now.checked_sub(rtt_to_use).unwrap(),
            MIN_INITIAL_PACKET_SIZE,
        );
        let cwnd_before = cc.cwnd();
        let ssthresh_before = cc.ssthresh();
        cc.on_packets_acked(&[pkt], rtt_est, now, &mut stats);
        let cwnd_after = cc.cwnd();
        let ssthresh_after = cc.ssthresh();
        let growth = cwnd_after - cwnd_before;
        next_ack += 1;

        // Detect CSS: growth becomes 1/4
        if growth > 0
            && growth == MIN_INITIAL_PACKET_SIZE / HyStart::CSS_GROWTH_DIVISOR
            && !css_detected
        {
            css_detected = true;
            qdebug!("CSS entered at ack_pn={ack_pn}, iteration={iteration}");
        }

        // Detect CA: ssthresh has been set
        if ssthresh_before.is_none() && ssthresh_after.is_some() {
            ca_detected = true;
            qdebug!("CA entered at ack_pn={ack_pn}, iteration={iteration}");
            // This assert makes sure that the ACK that we decided to move to CA on does not apply
            // exponential growth from slow start/CSS anymore.
            assert!(
                growth < MIN_INITIAL_PACKET_SIZE / HyStart::CSS_GROWTH_DIVISOR,
                "We should be using CA growth once we detected exit to CA."
            );
            assert_eq!(
                stats.slow_start_exit_reason,
                Some(SlowStartExitReason::Heuristic)
            );
            assert_eq!(stats.hystart_css_rounds_finished, HyStart::CSS_ROUNDS);
            break;
        }

        // As the cwnd grows during the loop above we cannot keep alternating ACK'ing and sending
        // just one packet. This sends more packets until the cwnd is full so we don't become
        // app-limited.
        while cc.bytes_in_flight() < cc.cwnd() {
            let send_pn = next_send;
            let pkt = sent::make_packet(send_pn, now, MIN_INITIAL_PACKET_SIZE);
            cc.on_packet_sent(&pkt, now, false);
            next_send += 1;
        }

        // Advance time by a small increment to simulate continuous operation.
        now += increased_rtt / 10;
    }

    assert!(css_detected, "Should have entered CSS");
    assert!(ca_detected, "Should have entered CA after CSS rounds");
    assert_eq!(
        cc.ssthresh(),
        Some(cc.cwnd()),
        "ssthresh should be set in CA"
    );
}

Messung V0.5 in Prozent
C=89 H=99 G=94

[Seitenstruktur0.21Druckenetwas mehr zur Ethik2026-10-08]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....

Besucher

Besucher

Statistik
#Sources=1126864
#Domains=2039723