• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Utility file to provide a ratelimit object
6 
7 use std::cmp;
8 use std::time::Duration;
9 use std::time::Instant;
10 
11 const BUS_LOCK_SLICE_TIME: u32 = 1000000000;
12 
13 pub struct Ratelimit {
14     slice_start_time: Instant,
15     slice_end_time: Instant,
16     slice_quota: u64,
17     slice_ns: Duration,
18     dispatched: u64,
19 }
20 
21 impl Ratelimit {
new() -> Self22     pub fn new() -> Self {
23         Ratelimit {
24             slice_start_time: Instant::now(),
25             slice_end_time: Instant::now(),
26             slice_quota: 0,
27             slice_ns: Duration::new(0, BUS_LOCK_SLICE_TIME),
28             dispatched: 0,
29         }
30     }
ratelimit_set_speed(&mut self, speed: u64)31     pub fn ratelimit_set_speed(&mut self, speed: u64) {
32         if speed == 0 {
33             self.slice_quota = 0;
34         } else {
35             self.slice_quota = cmp::max(
36                 speed * self.slice_ns.as_nanos() as u64 / BUS_LOCK_SLICE_TIME as u64,
37                 1,
38             );
39         }
40     }
41     /// Calculate and return delay for next request in ns
42     /// Record that we sent n data units (where n matches the scale chosen
43     /// during ratelimit_set_speed).If we may send more data units in the
44     ///  current time slice, return 0 (i.e. no delay).
45     /// Otherwise return the amount of time (in ns) until the start of
46     /// the next time slice that will permit sending the next chunk of data.
47     ///
48     /// Recording sent data units even after exceeding the quota is permitted;
49     /// the time slice will be extended accordingly.
ratelimit_calculate_delay(&mut self, n: u64) -> u6450     pub fn ratelimit_calculate_delay(&mut self, n: u64) -> u64 {
51         let now: Instant = Instant::now();
52         if self.slice_quota == 0 {
53             return 0;
54         }
55         if self.slice_end_time < now {
56             // Previous, possibly extended, time slice finished; reset the accounting.
57             self.slice_start_time = now;
58             self.slice_end_time = now + self.slice_ns;
59             self.dispatched = 0;
60         }
61 
62         self.dispatched += n;
63         if self.dispatched < self.slice_quota {
64             // We may send further data within the current time slice,
65             // no need to delay the next request.
66             return 0;
67         }
68         // Quota exceeded. Wait based on the excess amount and then start a new slice.
69         let delay_slices: f64 = self.dispatched as f64 / self.slice_quota as f64;
70         self.slice_end_time = self.slice_start_time + delay_slices as u32 * self.slice_ns;
71         (self.slice_end_time - now).as_nanos() as u64
72     }
73 }
74