diff --git a/src/congestion_control/delivery_rate.rs b/src/congestion_control/delivery_rate.rs index 596e3fe1..8179a2e1 100644 --- a/src/congestion_control/delivery_rate.rs +++ b/src/congestion_control/delivery_rate.rs @@ -15,8 +15,7 @@ //! A generic algorithm for a transport protocol sender to estimate the current //! delivery rate of its data on the fly. //! -//! See -//! . +//! See . use std::time::Duration; use std::time::Instant; @@ -26,33 +25,32 @@ use crate::connection::space::{RateSamplePacketState, SentPacket}; /// Rate sample output. /// -/// See -/// . +/// See draft-cheng-iccrg-delivery-rate-estimation-02 Section 3.1.3 #[derive(Debug, Default)] struct RateSample { - /// rs.delivery_rate: The delivery rate sample (in most cases rs.delivered / rs.interval). + /// The delivery rate sample (in most cases rs.delivered / rs.interval). delivery_rate: u64, - /// rs.is_app_limited: The P.is_app_limited from the most recent packet delivered; + /// The P.is_app_limited from the most recent packet delivered; /// indicates whether the rate sample is application-limited. is_app_limited: bool, - /// rs.interval: The length of the sampling interval. + /// The length of the sampling interval. interval: Duration, - /// rs.delivered: The amount of data marked as delivered over the sampling interval. + /// The amount of data marked as delivered over the sampling interval. delivered: u64, - /// rs.prior_delivered: The P.delivered count from the most recent packet delivered. + /// The P.delivered count from the most recent packet delivered. prior_delivered: u64, - /// rs.prior_time: The P.delivered_time from the most recent packet delivered. + /// The P.delivered_time from the most recent packet delivered. prior_time: Option, - /// rs.send_elapsed: Send time interval calculated from the most recent packet delivered. + /// Send time interval calculated from the most recent packet delivered. send_elapsed: Duration, - /// rs.ack_elapsed: ACK time interval calculated from the most recent packet delivered. + /// ACK time interval calculated from the most recent packet delivered. ack_elapsed: Duration, /// sample rtt. @@ -61,30 +59,32 @@ struct RateSample { /// Delivery rate estimator. /// -/// . +/// See draft-cheng-iccrg-delivery-rate-estimation-02 Section-3.1.1 #[derive(Debug)] pub struct DeliveryRateEstimator { - /// C.delivered: The total amount of data (measured in octets or in packets) delivered - /// so far over the lifetime of the transport connection. This does not include pure ACK packets. + /// The total amount of data (measured in octets or in packets) delivered so + /// far over the lifetime of the transport connection. This does not include + /// pure ACK packets. delivered: u64, - /// C.delivered_time: The wall clock time when C.delivered was last updated. + /// The wall clock time when C.delivered was last updated. delivered_time: Instant, - /// C.first_sent_time: If packets are in flight, then this holds the send time of the packet that - /// was most recently marked as delivered. Else, if the connection was recently idle, then this - /// holds the send time of most recently sent packet. + /// If packets are in flight, then this holds the send time of the packet + /// that was most recently marked as delivered. Else, if the connection was + /// recently idle, then this holds the send time of most recently sent packet. first_sent_time: Instant, - /// C.app_limited: The index of the last transmitted packet marked as application-limited, + /// The index of the last transmitted packet marked as application-limited, /// or 0 if the connection is not currently application-limited. last_app_limited_pkt_num: u64, - /// Record largest acked packet number to determine if app-limited state exits. + /// Record largest acked packet number to determine if app-limited state + /// exits. largest_acked_pkt_num: u64, - /// The last sent packet number. - /// If application-limited occurs, it will be the end of last_app_limited_pkt_num. + /// The last sent packet number. If application-limited occurs, it will be + /// the end of last_app_limited_pkt_num. last_sent_pkt_num: u64, /// Rate sample. @@ -93,7 +93,7 @@ pub struct DeliveryRateEstimator { impl DeliveryRateEstimator { /// Upon each packet transmission. - /// See . + /// See draft-cheng-iccrg-delivery-rate-estimation-02 Section-3.2 pub fn on_packet_sent( &mut self, packet: &mut SentPacket, @@ -117,7 +117,7 @@ impl DeliveryRateEstimator { } /// Update rate sampler (rs) when a packet is SACKed or ACKed. - /// See . + /// See draft-cheng-iccrg-delivery-rate-estimation-02 Section-3.3 pub fn update_rate_sample(&mut self, packet: &mut SentPacket) { if packet.rate_sample_state.delivered_time.is_none() || packet.time_acked.is_none() { // Packet already SACKed or packet not acked @@ -169,11 +169,13 @@ impl DeliveryRateEstimator { } /// Upon receiving ACK, fill in delivery rate sample rs. - /// See . + /// See draft-cheng-iccrg-delivery-rate-estimation-02 Section-3.3 pub fn generate_rate_sample(&mut self) { - // For each newly SACKed or ACKed packet P, - // `UpdateRateSample(P, rs)` - // It's done before generate_rate_sample is called. + // Note: The following steps should be executed before calling generate_rate_sample() + // ``` + // For each newly SACKed or ACKed packet P, + // UpdateRateSample(P, rs) + // ``` // Clear app-limited field if bubble is ACKed and gone. if self.is_app_limited() && self.largest_acked_pkt_num > self.last_app_limited_pkt_num { @@ -213,7 +215,7 @@ impl DeliveryRateEstimator { } /// Check if application limited. - /// See . + /// See draft-cheng-iccrg-delivery-rate-estimation-02 Section-3.4 pub fn is_app_limited(&self) -> bool { self.last_app_limited_pkt_num != 0 } diff --git a/src/connection/connection.rs b/src/connection/connection.rs index 08efa977..204dd879 100644 --- a/src/connection/connection.rs +++ b/src/connection/connection.rs @@ -1577,6 +1577,7 @@ impl Connection { } } + self.paths.mark_app_limited(pid, done == 0); if done == 0 { return Err(Error::Done); } @@ -1827,9 +1828,6 @@ impl Connection { ); } - // TODO: check app limited - // if write_status.in_flight == true and check app limited - let handshake_status = self.handshake_status(); self.paths.get_mut(path_id)?.recovery.on_packet_sent( sent_pkt, @@ -1990,7 +1988,6 @@ impl Connection { // No frames to be sent if st.frames.is_empty() { - // TODO: set app-limited return Err(Error::Done); } diff --git a/src/connection/path.rs b/src/connection/path.rs index b36f013a..8222f7eb 100644 --- a/src/connection/path.rs +++ b/src/connection/path.rs @@ -619,6 +619,14 @@ impl PathMap { left } + /// Mark whether it's currently limited by the application instead of + /// the connection window. + pub fn mark_app_limited(&mut self, pid: usize, run_out_of_data: bool) { + if let Some(path) = self.paths.get_mut(pid) { + path.recovery.mark_app_limited(run_out_of_data); + } + } + /// Schedule a Ping frame on the specified path or all active paths. pub fn mark_ping(&mut self, path_addr: Option) -> Result<()> { // If multipath is not enabled, schedule a Ping frame on the current diff --git a/src/connection/recovery.rs b/src/connection/recovery.rs index 3d5699cd..63be6ce6 100644 --- a/src/connection/recovery.rs +++ b/src/connection/recovery.rs @@ -108,6 +108,10 @@ pub struct Recovery { /// Cache pkt size pub cache_pkt_size: usize, + /// Whether the last `send` call yielded no packet because there was no + /// outgoing application data. + pub(super) is_app_limited: bool, + /// The time for last congestion window event last_cwnd_limited_time: Option, @@ -141,6 +145,7 @@ impl Recovery { pacer: Pacer::build_pacer_controller(conf), pacer_timer: None, cache_pkt_size: conf.max_datagram_size, + is_app_limited: false, last_cwnd_limited_time: None, stats: PathStats::default(), #[cfg(feature = "qlog")] @@ -365,7 +370,7 @@ impl Recovery { self.congestion.on_ack( sent_pkt, now, - false, + self.is_app_limited, &self.rtt, self.bytes_in_flight as u64, ); @@ -892,6 +897,14 @@ impl Recovery { self.pacer_timer.is_none() } + /// Mark whether the sender is currently limited by the application instead + /// of the congestion window. + pub(crate) fn mark_app_limited(&mut self, run_out_of_data: bool) { + self.is_app_limited = run_out_of_data + && self.pacer_timer.is_none() + && self.bytes_in_flight < self.congestion.congestion_window() as usize + } + /// Update statistics for the packet sent event pub(crate) fn stat_sent_event(&mut self, sent_pkts: u64, sent_bytes: u64) { self.stats.sent_count = self.stats.sent_count.saturating_add(sent_pkts);