Skip to main content

rns_core/resource/
window.rs

1use crate::constants::*;
2
3/// Window adaptation state for resource transfers.
4///
5/// Tracks window size, min/max bounds, and rate detection for
6/// fast/very-slow link adaptation.
7#[derive(Debug, Clone)]
8pub struct WindowState {
9    pub window: usize,
10    pub window_max: usize,
11    pub window_min: usize,
12    pub window_flexibility: usize,
13    pub fast_rate_rounds: usize,
14    pub very_slow_rate_rounds: usize,
15}
16
17impl WindowState {
18    pub fn new() -> Self {
19        WindowState {
20            window: RESOURCE_WINDOW,
21            window_max: RESOURCE_WINDOW_MAX_SLOW,
22            window_min: RESOURCE_WINDOW_MIN,
23            window_flexibility: RESOURCE_WINDOW_FLEXIBILITY,
24            fast_rate_rounds: 0,
25            very_slow_rate_rounds: 0,
26        }
27    }
28
29    /// Restore window state from a previous transfer on the same link.
30    pub fn restore(&mut self, previous_window: usize) {
31        self.window = previous_window;
32    }
33
34    /// Called when all outstanding parts in the window are received.
35    /// Grows window and ratchets window_min.
36    pub fn on_window_complete(&mut self) {
37        if self.window < self.window_max {
38            self.window += 1;
39            if (self.window as isize - self.window_min as isize) > (self.window_flexibility as isize - 1) {
40                self.window_min += 1;
41            }
42        }
43    }
44
45    /// Called on timeout waiting for parts.
46    /// Shrinks window, shrinks window_max (can decrease by 2).
47    pub fn on_timeout(&mut self) {
48        if self.window > self.window_min {
49            self.window -= 1;
50            if self.window_max > self.window_min {
51                self.window_max -= 1;
52                if (self.window_max as isize - self.window as isize) > (self.window_flexibility as isize - 1) {
53                    self.window_max -= 1;
54                }
55            }
56        }
57    }
58
59    /// Update rate tracking based on measured req_resp_rtt_rate.
60    /// Called after first part of a window is received.
61    pub fn update_req_resp_rate(&mut self, rate: f64) {
62        if rate > RESOURCE_RATE_FAST && self.fast_rate_rounds < RESOURCE_FAST_RATE_THRESHOLD {
63            self.fast_rate_rounds += 1;
64            if self.fast_rate_rounds == RESOURCE_FAST_RATE_THRESHOLD {
65                self.window_max = RESOURCE_WINDOW_MAX_FAST;
66            }
67        }
68    }
69
70    /// Update rate tracking based on measured data RTT rate.
71    /// Called after a full window of parts is received.
72    pub fn update_data_rate(&mut self, rate: f64) {
73        if rate > RESOURCE_RATE_FAST && self.fast_rate_rounds < RESOURCE_FAST_RATE_THRESHOLD {
74            self.fast_rate_rounds += 1;
75            if self.fast_rate_rounds == RESOURCE_FAST_RATE_THRESHOLD {
76                self.window_max = RESOURCE_WINDOW_MAX_FAST;
77            }
78        }
79
80        // Very slow detection only when fast_rate_rounds == 0
81        if self.fast_rate_rounds == 0
82            && rate < RESOURCE_RATE_VERY_SLOW
83            && self.very_slow_rate_rounds < RESOURCE_VERY_SLOW_RATE_THRESHOLD
84        {
85            self.very_slow_rate_rounds += 1;
86            if self.very_slow_rate_rounds == RESOURCE_VERY_SLOW_RATE_THRESHOLD {
87                self.window_max = RESOURCE_WINDOW_MAX_VERY_SLOW;
88            }
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_initial_state() {
99        let ws = WindowState::new();
100        assert_eq!(ws.window, RESOURCE_WINDOW); // 4
101        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // 10
102        assert_eq!(ws.window_min, RESOURCE_WINDOW_MIN); // 2
103        assert_eq!(ws.window_flexibility, RESOURCE_WINDOW_FLEXIBILITY); // 4
104        assert_eq!(ws.fast_rate_rounds, 0);
105        assert_eq!(ws.very_slow_rate_rounds, 0);
106    }
107
108    #[test]
109    fn test_window_increase_on_complete() {
110        let mut ws = WindowState::new();
111        // window=4, window_max=10, window_min=2, flexibility=4
112        ws.on_window_complete();
113        // window -> 5; (5-2)=3, not > (4-1)=3, so window_min stays 2
114        assert_eq!(ws.window, 5);
115        assert_eq!(ws.window_min, 2);
116
117        ws.on_window_complete();
118        // window -> 6; (6-2)=4 > 3, so window_min -> 3
119        assert_eq!(ws.window, 6);
120        assert_eq!(ws.window_min, 3);
121    }
122
123    #[test]
124    fn test_window_capped_at_max() {
125        let mut ws = WindowState::new();
126        ws.window = 10;
127        ws.window_max = 10;
128        ws.on_window_complete();
129        assert_eq!(ws.window, 10); // didn't increase
130    }
131
132    #[test]
133    fn test_window_decrease_on_timeout() {
134        let mut ws = WindowState::new();
135        // window=4, window_max=10, window_min=2
136        ws.on_timeout();
137        // window -> 3, window_max -> 9
138        // (9-3)=6 > (4-1)=3, so window_max -> 8
139        assert_eq!(ws.window, 3);
140        assert_eq!(ws.window_max, 8);
141    }
142
143    #[test]
144    fn test_window_min_floor() {
145        let mut ws = WindowState::new();
146        ws.window = RESOURCE_WINDOW_MIN;
147        ws.on_timeout();
148        assert_eq!(ws.window, RESOURCE_WINDOW_MIN); // can't go below min
149    }
150
151    #[test]
152    fn test_fast_rate_detection() {
153        let mut ws = WindowState::new();
154        // Need FAST_RATE_THRESHOLD(4) rounds of fast rate
155        for _ in 0..3 {
156            ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
157        }
158        assert_eq!(ws.fast_rate_rounds, 3);
159        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // not yet
160
161        ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
162        assert_eq!(ws.fast_rate_rounds, 4);
163        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_FAST); // now!
164    }
165
166    #[test]
167    fn test_fast_rate_rounds_never_reset() {
168        let mut ws = WindowState::new();
169        ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
170        assert_eq!(ws.fast_rate_rounds, 1);
171
172        // Slow rate doesn't reset fast_rate_rounds
173        ws.update_req_resp_rate(1.0);
174        assert_eq!(ws.fast_rate_rounds, 1);
175    }
176
177    #[test]
178    fn test_fast_rate_rounds_cap() {
179        let mut ws = WindowState::new();
180        for _ in 0..10 {
181            ws.update_req_resp_rate(RESOURCE_RATE_FAST + 1.0);
182        }
183        // Capped at threshold
184        assert_eq!(ws.fast_rate_rounds, RESOURCE_FAST_RATE_THRESHOLD);
185    }
186
187    #[test]
188    fn test_very_slow_detection() {
189        let mut ws = WindowState::new();
190        // fast_rate_rounds must be 0
191        assert_eq!(ws.fast_rate_rounds, 0);
192
193        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
194        assert_eq!(ws.very_slow_rate_rounds, 1);
195        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_SLOW); // not yet
196
197        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
198        assert_eq!(ws.very_slow_rate_rounds, 2);
199        assert_eq!(ws.window_max, RESOURCE_WINDOW_MAX_VERY_SLOW); // capped!
200    }
201
202    #[test]
203    fn test_very_slow_blocked_by_fast() {
204        let mut ws = WindowState::new();
205        // Set fast_rate_rounds > 0
206        ws.update_data_rate(RESOURCE_RATE_FAST + 1.0);
207        assert_eq!(ws.fast_rate_rounds, 1);
208
209        // Very slow should not trigger
210        ws.update_data_rate(RESOURCE_RATE_VERY_SLOW - 1.0);
211        assert_eq!(ws.very_slow_rate_rounds, 0);
212    }
213
214    #[test]
215    fn test_restore_window() {
216        let mut ws = WindowState::new();
217        ws.restore(8);
218        assert_eq!(ws.window, 8);
219    }
220
221    #[test]
222    fn test_window_never_below_min() {
223        let mut ws = WindowState::new();
224        ws.window = 2;
225        ws.window_min = 2;
226        ws.on_timeout();
227        assert_eq!(ws.window, 2);
228    }
229
230    #[test]
231    fn test_window_never_above_max() {
232        let mut ws = WindowState::new();
233        ws.window = 10;
234        ws.window_max = 10;
235        ws.on_window_complete();
236        assert_eq!(ws.window, 10);
237    }
238}