h2/proto/streams/
counts.rs1use super::*;
2
3#[derive(Debug)]
4struct Budget {
5 available: usize,
6 max: usize,
7}
8
9#[derive(Debug)]
10pub(super) struct BudgetExhausted;
11
12impl Budget {
13 fn new(max: usize) -> Self {
14 Budget {
15 available: max,
16 max,
17 }
18 }
19
20 fn consume(&mut self, amount: usize) -> Result<(), BudgetExhausted> {
21 self.available = self.available.checked_sub(amount).ok_or(BudgetExhausted)?;
22 Ok(())
23 }
24
25 fn replenish(&mut self, amount: usize) {
26 self.available = self.available.saturating_add(amount).min(self.max);
27 }
28}
29
30#[derive(Debug)]
31pub(super) struct Counts {
32 peer: peer::Dyn,
35
36 max_send_streams: usize,
38
39 num_send_streams: usize,
41
42 max_recv_streams: usize,
44
45 num_recv_streams: usize,
47
48 max_local_reset_streams: usize,
50
51 num_local_reset_streams: usize,
53
54 max_remote_reset_streams: usize,
56
57 num_remote_reset_streams: usize,
59
60 max_local_error_reset_streams: Option<usize>,
65
66 num_local_error_reset_streams: usize,
69
70 data_frame_budget: Budget,
72
73 num_recv_empty_data_frames: usize,
76}
77
78impl Counts {
79 pub fn new(peer: peer::Dyn, config: &Config) -> Self {
81 Counts {
82 peer,
83 max_send_streams: config.initial_max_send_streams,
84 num_send_streams: 0,
85 max_recv_streams: config.remote_max_initiated.unwrap_or(usize::MAX),
86 num_recv_streams: 0,
87 max_local_reset_streams: config.local_reset_max,
88 num_local_reset_streams: 0,
89 max_remote_reset_streams: config.remote_reset_max,
90 num_remote_reset_streams: 0,
91 max_local_error_reset_streams: config.local_max_error_reset_streams,
92 num_local_error_reset_streams: 0,
93 data_frame_budget: Budget::new(config.data_frame_budget),
94 num_recv_empty_data_frames: 0,
95 }
96 }
97
98 pub fn record_data_frame(&mut self, payload_len: usize) -> Result<(), BudgetExhausted> {
100 if payload_len == 0 {
101 self.num_recv_empty_data_frames = self
102 .num_recv_empty_data_frames
103 .checked_add(1)
104 .ok_or(BudgetExhausted)?;
105 if self.num_recv_empty_data_frames > MAX_RECV_EMPTY_DATA_FRAMES {
106 return Err(BudgetExhausted);
107 }
108 Ok(())
109 } else if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
110 self.data_frame_budget
111 .consume(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len)
112 } else {
113 self.data_frame_budget
114 .replenish(payload_len - DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD);
115 Ok(())
116 }
117 }
118
119 pub fn release_data_frame(&mut self, payload_len: usize) {
122 if payload_len != 0 && payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
123 self.data_frame_budget
124 .replenish(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len);
125 }
126 }
127
128 pub fn next_send_stream_will_reach_capacity(&self) -> bool {
133 self.max_send_streams <= (self.num_send_streams + 1)
134 }
135
136 pub fn peer(&self) -> peer::Dyn {
138 self.peer
139 }
140
141 pub fn has_streams(&self) -> bool {
142 self.num_send_streams != 0 || self.num_recv_streams != 0
143 }
144
145 pub fn can_inc_num_local_error_resets(&self) -> bool {
147 if let Some(max) = self.max_local_error_reset_streams {
148 max > self.num_local_error_reset_streams
149 } else {
150 true
151 }
152 }
153
154 pub fn inc_num_local_error_resets(&mut self) {
155 assert!(self.can_inc_num_local_error_resets());
156
157 self.num_local_error_reset_streams += 1;
159 }
160
161 pub(crate) fn max_local_error_resets(&self) -> Option<usize> {
162 self.max_local_error_reset_streams
163 }
164
165 pub fn can_inc_num_recv_streams(&self) -> bool {
167 self.max_recv_streams > self.num_recv_streams
168 }
169
170 pub fn inc_num_recv_streams(&mut self, stream: &mut store::Ptr) {
176 assert!(self.can_inc_num_recv_streams());
177 assert!(!stream.is_counted);
178
179 self.num_recv_streams += 1;
181 stream.is_counted = true;
182 }
183
184 pub fn can_inc_num_send_streams(&self) -> bool {
186 self.max_send_streams > self.num_send_streams
187 }
188
189 pub fn inc_num_send_streams(&mut self, stream: &mut store::Ptr) {
195 assert!(self.can_inc_num_send_streams());
196 assert!(!stream.is_counted);
197
198 self.num_send_streams += 1;
200 stream.is_counted = true;
201 }
202
203 pub fn can_inc_num_reset_streams(&self) -> bool {
205 self.max_local_reset_streams > self.num_local_reset_streams
206 }
207
208 pub fn inc_num_reset_streams(&mut self) {
214 assert!(self.can_inc_num_reset_streams());
215
216 self.num_local_reset_streams += 1;
217 }
218
219 pub(crate) fn max_remote_reset_streams(&self) -> usize {
220 self.max_remote_reset_streams
221 }
222
223 pub(crate) fn can_inc_num_remote_reset_streams(&self) -> bool {
226 self.max_remote_reset_streams > self.num_remote_reset_streams
227 }
228
229 pub(crate) fn inc_num_remote_reset_streams(&mut self) {
235 assert!(self.can_inc_num_remote_reset_streams());
236
237 self.num_remote_reset_streams += 1;
238 }
239
240 pub(crate) fn dec_num_remote_reset_streams(&mut self) {
241 assert!(self.num_remote_reset_streams > 0);
242
243 self.num_remote_reset_streams -= 1;
244 }
245
246 pub fn apply_remote_settings(&mut self, settings: &frame::Settings, is_initial: bool) {
247 match settings.max_concurrent_streams() {
248 Some(val) => self.max_send_streams = val as usize,
249 None if is_initial => self.max_send_streams = usize::MAX,
250 None => {}
251 }
252 }
253
254 pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
261 where
262 F: FnOnce(&mut Self, &mut store::Ptr) -> U,
263 {
264 let is_pending_reset = stream.is_pending_reset_expiration();
266
267 let ret = f(self, &mut stream);
269
270 self.transition_after(stream, is_pending_reset);
271
272 ret
273 }
274
275 pub fn transition_after(&mut self, mut stream: store::Ptr, is_reset_counted: bool) {
277 tracing::trace!(
278 "transition_after; stream={:?}; state={:?}; is_closed={:?}; \
279 pending_send_empty={:?}; buffered_send_data={}; \
280 num_recv={}; num_send={}",
281 stream.id,
282 stream.state,
283 stream.is_closed(),
284 stream.pending_send.is_empty(),
285 stream.buffered_send_data,
286 self.num_recv_streams,
287 self.num_send_streams
288 );
289
290 if stream.is_closed() {
291 if !stream.is_pending_reset_expiration() {
292 stream.unlink();
293 if is_reset_counted {
294 self.dec_num_reset_streams();
295 }
296 }
297
298 if !stream.state.is_scheduled_reset() && stream.is_counted {
299 tracing::trace!("dec_num_streams; stream={:?}", stream.id);
300 self.dec_num_streams(&mut stream);
302 }
303 }
304
305 if stream.is_released() {
307 stream.remove();
308 }
309 }
310
311 pub(crate) fn max_send_streams(&self) -> usize {
314 self.max_send_streams
315 }
316
317 pub(crate) fn max_recv_streams(&self) -> usize {
320 self.max_recv_streams
321 }
322
323 fn dec_num_streams(&mut self, stream: &mut store::Ptr) {
324 assert!(stream.is_counted);
325
326 if self.peer.is_local_init(stream.id) {
327 assert!(self.num_send_streams > 0);
328 self.num_send_streams -= 1;
329 stream.is_counted = false;
330 } else {
331 assert!(self.num_recv_streams > 0);
332 self.num_recv_streams -= 1;
333 stream.is_counted = false;
334 }
335 }
336
337 fn dec_num_reset_streams(&mut self) {
338 assert!(self.num_local_reset_streams > 0);
339 self.num_local_reset_streams -= 1;
340 }
341}
342
343impl Drop for Counts {
344 fn drop(&mut self) {
345 use std::thread;
346
347 if !thread::panicking() {
348 debug_assert!(!self.has_streams());
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE;
357
358 fn counts() -> Counts {
359 Counts::new(
360 peer::Dyn::Server,
361 &Config {
362 initial_max_send_streams: 0,
363 local_max_buffer_size: 0,
364 local_next_stream_id: 2.into(),
365 local_push_enabled: false,
366 extended_connect_protocol_enabled: false,
367 local_reset_duration: Duration::ZERO,
368 local_reset_max: 0,
369 remote_reset_max: 0,
370 remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
371 remote_max_initiated: None,
372 local_max_error_reset_streams: None,
373 data_frame_budget: DEFAULT_DATA_FRAME_BUDGET,
374 },
375 )
376 }
377
378 #[test]
379 fn budget_is_bounded() {
380 let mut budget = Budget::new(10);
381
382 budget.consume(4).unwrap();
383 budget.replenish(20);
384 assert_eq!(budget.available, 10);
385 }
386
387 #[test]
388 fn budget_reports_exhaustion_without_underflowing() {
389 let mut budget = Budget::new(10);
390
391 budget.consume(10).unwrap();
392 assert!(budget.consume(1).is_err());
393 assert_eq!(budget.available, 0);
394 }
395
396 #[test]
397 fn good_sized_data_frames_do_not_exhaust_budget() {
398 let mut counts = counts();
399
400 for _ in 0..1_000_000 {
401 counts
402 .record_data_frame(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD)
403 .unwrap();
404 }
405 }
406
407 #[test]
408 fn consumed_small_data_frames_do_not_exhaust_budget() {
409 let mut counts = counts();
410
411 for _ in 0..1_000_000 {
412 counts.record_data_frame(1).unwrap();
413 counts.release_data_frame(1);
414 }
415 }
416
417 #[test]
418 fn empty_data_frames_do_not_consume_data_frame_budget() {
419 let mut counts = counts();
420 counts.data_frame_budget = Budget::new(0);
421
422 for _ in 0..MAX_RECV_EMPTY_DATA_FRAMES {
423 counts.record_data_frame(0).unwrap();
424 }
425
426 assert!(counts.record_data_frame(0).is_err());
429 assert!(counts.record_data_frame(1).is_err());
430 }
431
432 #[test]
433 fn large_data_frames_do_not_replenish_empty_data_frame_limit() {
434 let mut counts = counts();
435
436 for _ in 0..MAX_RECV_EMPTY_DATA_FRAMES {
437 counts.record_data_frame(0).unwrap();
438 counts
439 .record_data_frame(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD * 2)
440 .unwrap();
441 }
442 assert!(counts.record_data_frame(0).is_err());
443 }
444}