@@ -17,22 +17,36 @@ pub(super) struct Pacer {
1717 last_window : u64 ,
1818 last_mtu : u16 ,
1919 tokens : u64 ,
20+ max_bytes_per_second : Option < u64 > ,
2021 prev : Instant ,
2122}
2223
2324impl Pacer {
2425 /// Obtains a new [`Pacer`].
25- pub ( super ) fn new ( smoothed_rtt : Duration , window : u64 , mtu : u16 , now : Instant ) -> Self {
26+ pub ( super ) fn new (
27+ smoothed_rtt : Duration ,
28+ window : u64 ,
29+ mtu : u16 ,
30+ max_bytes_per_second : Option < u64 > ,
31+ now : Instant ,
32+ ) -> Self {
33+ let window = rate_limited_window ( smoothed_rtt, window, max_bytes_per_second) ;
2634 let capacity = optimal_capacity ( smoothed_rtt, window, mtu) ;
2735 Self {
2836 capacity,
2937 last_window : window,
3038 last_mtu : mtu,
3139 tokens : capacity,
40+ max_bytes_per_second,
3241 prev : now,
3342 }
3443 }
3544
45+ /// Obtains the `max_bytes_per_second` used when this [`Pacer`] was constructed.
46+ pub ( crate ) fn max_bytes_per_second ( & self ) -> Option < u64 > {
47+ self . max_bytes_per_second
48+ }
49+
3650 /// Record that a packet has been transmitted.
3751 pub ( super ) fn on_transmit ( & mut self , packet_length : u16 ) {
3852 self . tokens = self . tokens . saturating_sub ( packet_length. into ( ) )
@@ -58,6 +72,7 @@ impl Pacer {
5872 "zero-sized congestion control window is nonsense"
5973 ) ;
6074
75+ let window = rate_limited_window ( smoothed_rtt, window, self . max_bytes_per_second ) ;
6176 if window != self . last_window || mtu != self . last_mtu {
6277 self . capacity = optimal_capacity ( smoothed_rtt, window, mtu) ;
6378
@@ -147,6 +162,27 @@ fn optimal_capacity(smoothed_rtt: Duration, window: u64, mtu: u16) -> u64 {
147162 )
148163}
149164
165+ /// Clamps the window to limit the sending rate to `max_bytes_per_second`.
166+ ///
167+ /// If `max_bytes_per_second` is `None`, the original window is returned.
168+ fn rate_limited_window (
169+ smoothed_rtt : Duration ,
170+ window : u64 ,
171+ max_bytes_per_second : Option < u64 > ,
172+ ) -> u64 {
173+ let Some ( max_bytes_per_second) = max_bytes_per_second else {
174+ return window;
175+ } ;
176+
177+ let rate_window = max_bytes_per_second as f64 * smoothed_rtt. as_secs_f64 ( ) ;
178+
179+ // the pacer refills tokens at x1.25 speed, so we shrink the window to cancel out the speedup
180+ // (otherwise the actual sending rate could be higher than `max_bytes_per_second`)
181+ let adjusted_rate_window = ( rate_window / 1.25 ) . round ( ) ;
182+
183+ Ord :: min ( window, Ord :: max ( adjusted_rate_window as u64 , 1 ) )
184+ }
185+
150186/// Period of traffic to batch together on a reasonably fast connection
151187const TARGET_BURST_INTERVAL : Duration = Duration :: from_millis ( 2 ) ;
152188
@@ -173,17 +209,17 @@ mod tests {
173209 let rtt = Duration :: from_micros ( 400 ) ;
174210
175211 assert ! (
176- Pacer :: new( rtt, 30000 , 1500 , new_instant)
212+ Pacer :: new( rtt, 30000 , 1500 , None , new_instant)
177213 . delay( Duration :: from_micros( 0 ) , 0 , 1500 , 1 , old_instant)
178214 . is_none( )
179215 ) ;
180216 assert ! (
181- Pacer :: new( rtt, 30000 , 1500 , new_instant)
217+ Pacer :: new( rtt, 30000 , 1500 , None , new_instant)
182218 . delay( Duration :: from_micros( 0 ) , 1600 , 1500 , 1 , old_instant)
183219 . is_none( )
184220 ) ;
185221 assert ! (
186- Pacer :: new( rtt, 30000 , 1500 , new_instant)
222+ Pacer :: new( rtt, 30000 , 1500 , None , new_instant)
187223 . delay( Duration :: from_micros( 0 ) , 1500 , 1500 , 3000 , old_instant)
188224 . is_none( )
189225 ) ;
@@ -196,18 +232,18 @@ mod tests {
196232 let rtt = Duration :: from_millis ( 50 ) ;
197233 let now = Instant :: now ( ) ;
198234
199- let pacer = Pacer :: new ( rtt, window, mtu, now) ;
235+ let pacer = Pacer :: new ( rtt, window, mtu, None , now) ;
200236 assert_eq ! (
201237 pacer. capacity,
202238 ( window as u128 * TARGET_BURST_INTERVAL . as_nanos( ) / rtt. as_nanos( ) ) as u64
203239 ) ;
204240 assert_eq ! ( pacer. tokens, pacer. capacity) ;
205241
206- let pacer = Pacer :: new ( Duration :: from_millis ( 0 ) , window, mtu, now) ;
242+ let pacer = Pacer :: new ( Duration :: from_millis ( 0 ) , window, mtu, None , now) ;
207243 assert_eq ! ( pacer. capacity, MAX_BURST_SIZE * mtu as u64 ) ;
208244 assert_eq ! ( pacer. tokens, pacer. capacity) ;
209245
210- let pacer = Pacer :: new ( rtt, 1 , mtu, now) ;
246+ let pacer = Pacer :: new ( rtt, 1 , mtu, None , now) ;
211247 assert_eq ! ( pacer. capacity, mtu as u64 ) ;
212248 assert_eq ! ( pacer. tokens, pacer. capacity) ;
213249 }
@@ -219,7 +255,7 @@ mod tests {
219255 let rtt = Duration :: from_millis ( 50 ) ;
220256 let now = Instant :: now ( ) ;
221257
222- let mut pacer = Pacer :: new ( rtt, window, mtu, now) ;
258+ let mut pacer = Pacer :: new ( rtt, window, mtu, None , now) ;
223259 assert_eq ! (
224260 pacer. capacity,
225261 ( window as u128 * TARGET_BURST_INTERVAL . as_nanos( ) / rtt. as_nanos( ) ) as u64
@@ -258,7 +294,7 @@ mod tests {
258294 let rtt = Duration :: from_millis ( 50 ) ;
259295 let old_instant = Instant :: now ( ) ;
260296
261- let mut pacer = Pacer :: new ( rtt, window, mtu, old_instant) ;
297+ let mut pacer = Pacer :: new ( rtt, window, mtu, None , old_instant) ;
262298 let packet_capacity = pacer. capacity / mtu as u64 ;
263299
264300 for _ in 0 ..packet_capacity {
@@ -321,4 +357,38 @@ mod tests {
321357 ) ;
322358 assert_eq ! ( pacer. tokens, pacer. capacity) ;
323359 }
360+
361+ #[ test]
362+ fn computes_pause_correctly_for_rate_limited ( ) {
363+ let window = 2_000_000u64 ;
364+ let mtu = 1000 ;
365+ let rtt = Duration :: from_millis ( 50 ) ;
366+ let old_instant = Instant :: now ( ) ;
367+
368+ let mut pacer = Pacer :: new ( rtt, window, mtu, Some ( 2_000 ) , old_instant) ;
369+ assert_eq ! (
370+ pacer. delay( rtt, 1_000 , mtu, window, old_instant) ,
371+ None ,
372+ "When capacity is available packets should be sent immediately"
373+ ) ;
374+ pacer. on_transmit ( mtu) ;
375+
376+ let actual_delay = pacer
377+ . delay ( rtt, 1_000 , mtu, window, old_instant)
378+ . expect ( "Send must be delayed" )
379+ . duration_since ( old_instant) ;
380+
381+ let expected_delay = Duration :: from_millis ( 500 ) ;
382+ let diff = actual_delay. abs_diff ( expected_delay) ;
383+
384+ // Allow up to 2ns difference due to rounding
385+ assert ! (
386+ diff < Duration :: from_nanos( 2 ) ,
387+ "expected ≈ {expected_delay:?}, got {actual_delay:?} (diff {diff:?})"
388+ ) ;
389+
390+ // Should be able to send after a while
391+ let now = old_instant + expected_delay / 2 ;
392+ assert_eq ! ( pacer. delay( rtt, 500 , mtu, window, now) , None ) ;
393+ }
324394}
0 commit comments