@@ -113,6 +113,10 @@ pub struct Congestion {
113113 /// Initial congestion window size in terms of packet count.
114114 pub ( crate ) initial_congestion_window_packets : usize ,
115115
116+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
117+ /// Fixed congestion window in bytes for the unchecked controller.
118+ pub ( crate ) unchecked_congestion_window : usize ,
119+
116120 max_datagram_size : usize ,
117121
118122 pub ( crate ) lost_count : usize ,
@@ -146,6 +150,9 @@ impl Congestion {
146150
147151 initial_congestion_window_packets : recovery_config
148152 . initial_congestion_window_packets ,
153+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
154+ unchecked_congestion_window : recovery_config
155+ . unchecked_congestion_window ,
149156
150157 max_datagram_size : recovery_config. max_send_udp_payload_size ,
151158
@@ -247,6 +254,9 @@ impl Congestion {
247254pub ( crate ) struct CongestionControlOps {
248255 pub on_init : fn ( r : & mut Congestion ) ,
249256
257+ /// Updates controller state after the packet-size limit changes.
258+ pub on_mtu_update : fn ( congestion : & mut Congestion ) ,
259+
250260 pub on_packet_sent : fn (
251261 r : & mut Congestion ,
252262 sent_bytes : usize ,
@@ -283,6 +293,34 @@ pub(crate) struct CongestionControlOps {
283293 ) -> std:: fmt:: Result ,
284294}
285295
296+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
297+ static CONGESTION_WINDOW_UNCHECKED : CongestionControlOps = CongestionControlOps {
298+ on_init : reset_unchecked_congestion_window,
299+ on_mtu_update : reset_unchecked_congestion_window,
300+ on_packet_sent : |congestion, _, _, _| {
301+ reset_unchecked_congestion_window ( congestion)
302+ } ,
303+ on_packets_acked : |congestion, _, _, _, _| {
304+ reset_unchecked_congestion_window ( congestion)
305+ } ,
306+ congestion_event : |congestion, _, _, _, _| {
307+ reset_unchecked_congestion_window ( congestion)
308+ } ,
309+ checkpoint : reset_unchecked_congestion_window,
310+ rollback : |congestion| {
311+ reset_unchecked_congestion_window ( congestion) ;
312+ true
313+ } ,
314+ #[ cfg( feature = "qlog" ) ]
315+ state_str : |_, _| "congestion_window_unchecked" ,
316+ debug_fmt : |_, _| Ok ( ( ) ) ,
317+ } ;
318+
319+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
320+ fn reset_unchecked_congestion_window ( congestion : & mut Congestion ) {
321+ congestion. congestion_window = congestion. unchecked_congestion_window ;
322+ }
323+
286324impl From < CongestionControlAlgorithm > for & ' static CongestionControlOps {
287325 fn from ( algo : CongestionControlAlgorithm ) -> Self {
288326 match algo {
@@ -293,13 +331,80 @@ impl From<CongestionControlAlgorithm> for &'static CongestionControlOps {
293331 // LegacyRecovery never gets a RecoveryConfig with the
294332 // Bbr2Gcongestion algorithm.
295333 CongestionControlAlgorithm :: Bbr2Gcongestion => unreachable ! ( ) ,
334+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
335+ CongestionControlAlgorithm :: CongestionWindowUnchecked =>
336+ & CONGESTION_WINDOW_UNCHECKED ,
296337 }
297338 }
298339}
299340
300341#[ cfg( test) ]
301342mod tests {
302343 use super :: * ;
344+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
345+ use crate :: test_utils;
346+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
347+ use crate :: Config ;
348+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
349+ use rstest:: rstest;
350+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
351+ use std:: time:: Duration ;
352+
353+ #[ cfg( feature = "congestion_window_unchecked_available" ) ]
354+ #[ rstest]
355+ fn unchecked_window_is_restored_by_callbacks (
356+ #[ values( usize :: MAX , 40 * 1024 * 1024 ) ] window : usize ,
357+ ) {
358+ let mut config = Config :: new ( crate :: PROTOCOL_VERSION )
359+ . expect ( "create transport configuration" ) ;
360+ config. set_cc_algorithm (
361+ CongestionControlAlgorithm :: CongestionWindowUnchecked ,
362+ ) ;
363+ config. set_unchecked_congestion_window ( window) ;
364+ let mut congestion =
365+ Congestion :: from_config ( & RecoveryConfig :: from_config ( & config) ) ;
366+ assert_eq ! ( congestion. congestion_window( ) , window) ;
367+
368+ let callbacks: [ fn ( & mut Congestion ) ; 5 ] = [
369+ |congestion| {
370+ ( congestion. cc_ops . on_packet_sent ) (
371+ congestion,
372+ 1_200 ,
373+ 0 ,
374+ Instant :: now ( ) ,
375+ ) ;
376+ } ,
377+ |congestion| {
378+ ( congestion. cc_ops . on_packets_acked ) (
379+ congestion,
380+ 0 ,
381+ & mut Vec :: new ( ) ,
382+ Instant :: now ( ) ,
383+ & RttStats :: new (
384+ Duration :: from_millis ( 100 ) ,
385+ Duration :: from_millis ( 25 ) ,
386+ ) ,
387+ ) ;
388+ } ,
389+ |congestion| {
390+ let now = Instant :: now ( ) ;
391+ let packet = test_utils:: helper_packet_sent ( 0 , now, 1_200 ) ;
392+ ( congestion. cc_ops . congestion_event ) (
393+ congestion, 1_200 , 1_200 , & packet, now,
394+ ) ;
395+ } ,
396+ |congestion| ( congestion. cc_ops . checkpoint ) ( congestion) ,
397+ |congestion| {
398+ assert ! ( ( congestion. cc_ops. rollback) ( congestion) ) ;
399+ } ,
400+ ] ;
401+
402+ for callback in callbacks {
403+ congestion. congestion_window = 0 ;
404+ callback ( & mut congestion) ;
405+ assert_eq ! ( congestion. congestion_window( ) , window) ;
406+ }
407+ }
303408
304409 #[ test]
305410 fn ssthresh_init ( ) {
0 commit comments