1 use crate::time::wheel::Stack;
2 
3 use std::fmt;
4 
5 /// Wheel for a single level in the timer. This wheel contains 64 slots.
6 pub(crate) struct Level<T> {
7     level: usize,
8 
9     /// Bit field tracking which slots currently contain entries.
10     ///
11     /// Using a bit field to track slots that contain entries allows avoiding a
12     /// scan to find entries. This field is updated when entries are added or
13     /// removed from a slot.
14     ///
15     /// The least-significant bit represents slot zero.
16     occupied: u64,
17 
18     /// Slots
19     slot: [T; LEVEL_MULT],
20 }
21 
22 /// Indicates when a slot must be processed next.
23 #[derive(Debug)]
24 pub(crate) struct Expiration {
25     /// The level containing the slot.
26     pub(crate) level: usize,
27 
28     /// The slot index.
29     pub(crate) slot: usize,
30 
31     /// The instant at which the slot needs to be processed.
32     pub(crate) deadline: u64,
33 }
34 
35 /// Level multiplier.
36 ///
37 /// Being a power of 2 is very important.
38 const LEVEL_MULT: usize = 64;
39 
40 impl<T: Stack> Level<T> {
new(level: usize) -> Level<T>41     pub(crate) fn new(level: usize) -> Level<T> {
42         Level {
43             level,
44             occupied: 0,
45             slot: std::array::from_fn(|_| T::default()),
46         }
47     }
48 
49     /// Finds the slot that needs to be processed next and returns the slot and
50     /// `Instant` at which this slot must be processed.
next_expiration(&self, now: u64) -> Option<Expiration>51     pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> {
52         // Use the `occupied` bit field to get the index of the next slot that
53         // needs to be processed.
54         let slot = match self.next_occupied_slot(now) {
55             Some(slot) => slot,
56             None => return None,
57         };
58 
59         // From the slot index, calculate the `Instant` at which it needs to be
60         // processed. This value *must* be in the future with respect to `now`.
61 
62         let level_range = level_range(self.level);
63         let slot_range = slot_range(self.level);
64 
65         // TODO: This can probably be simplified w/ power of 2 math
66         let level_start = now - (now % level_range);
67         let mut deadline = level_start + slot as u64 * slot_range;
68         if deadline < now {
69             // A timer is in a slot "prior" to the current time. This can occur
70             // because we do not have an infinite hierarchy of timer levels, and
71             // eventually a timer scheduled for a very distant time might end up
72             // being placed in a slot that is beyond the end of all of the
73             // arrays.
74             //
75             // To deal with this, we first limit timers to being scheduled no
76             // more than MAX_DURATION ticks in the future; that is, they're at
77             // most one rotation of the top level away. Then, we force timers
78             // that logically would go into the top+1 level, to instead go into
79             // the top level's slots.
80             //
81             // What this means is that the top level's slots act as a
82             // pseudo-ring buffer, and we rotate around them indefinitely. If we
83             // compute a deadline before now, and it's the top level, it
84             // therefore means we're actually looking at a slot in the future.
85             debug_assert_eq!(self.level, super::NUM_LEVELS - 1);
86 
87             deadline += level_range;
88         }
89         debug_assert!(
90             deadline >= now,
91             "deadline={:016X}; now={:016X}; level={}; slot={}; occupied={:b}",
92             deadline,
93             now,
94             self.level,
95             slot,
96             self.occupied
97         );
98 
99         Some(Expiration {
100             level: self.level,
101             slot,
102             deadline,
103         })
104     }
105 
next_occupied_slot(&self, now: u64) -> Option<usize>106     fn next_occupied_slot(&self, now: u64) -> Option<usize> {
107         if self.occupied == 0 {
108             return None;
109         }
110 
111         // Get the slot for now using Maths
112         let now_slot = (now / slot_range(self.level)) as usize;
113         let occupied = self.occupied.rotate_right(now_slot as u32);
114         let zeros = occupied.trailing_zeros() as usize;
115         let slot = (zeros + now_slot) % 64;
116 
117         Some(slot)
118     }
119 
add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store)120     pub(crate) fn add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store) {
121         let slot = slot_for(when, self.level);
122 
123         self.slot[slot].push(item, store);
124         self.occupied |= occupied_bit(slot);
125     }
126 
remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store)127     pub(crate) fn remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store) {
128         let slot = slot_for(when, self.level);
129 
130         self.slot[slot].remove(item, store);
131 
132         if self.slot[slot].is_empty() {
133             // The bit is currently set
134             debug_assert!(self.occupied & occupied_bit(slot) != 0);
135 
136             // Unset the bit
137             self.occupied ^= occupied_bit(slot);
138         }
139     }
140 
pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option<T::Owned>141     pub(crate) fn pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option<T::Owned> {
142         let ret = self.slot[slot].pop(store);
143 
144         if ret.is_some() && self.slot[slot].is_empty() {
145             // The bit is currently set
146             debug_assert!(self.occupied & occupied_bit(slot) != 0);
147 
148             self.occupied ^= occupied_bit(slot);
149         }
150 
151         ret
152     }
153 
peek_entry_slot(&self, slot: usize) -> Option<T::Owned>154     pub(crate) fn peek_entry_slot(&self, slot: usize) -> Option<T::Owned> {
155         self.slot[slot].peek()
156     }
157 }
158 
159 impl<T> fmt::Debug for Level<T> {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result160     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
161         fmt.debug_struct("Level")
162             .field("occupied", &self.occupied)
163             .finish()
164     }
165 }
166 
occupied_bit(slot: usize) -> u64167 fn occupied_bit(slot: usize) -> u64 {
168     1 << slot
169 }
170 
slot_range(level: usize) -> u64171 fn slot_range(level: usize) -> u64 {
172     LEVEL_MULT.pow(level as u32) as u64
173 }
174 
level_range(level: usize) -> u64175 fn level_range(level: usize) -> u64 {
176     LEVEL_MULT as u64 * slot_range(level)
177 }
178 
179 /// Convert a duration (milliseconds) and a level to a slot position
slot_for(duration: u64, level: usize) -> usize180 fn slot_for(duration: u64, level: usize) -> usize {
181     ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize
182 }
183 
184 #[cfg(all(test, not(loom)))]
185 mod test {
186     use super::*;
187 
188     #[test]
test_slot_for()189     fn test_slot_for() {
190         for pos in 0..64 {
191             assert_eq!(pos as usize, slot_for(pos, 0));
192         }
193 
194         for level in 1..5 {
195             for pos in level..64 {
196                 let a = pos * 64_usize.pow(level as u32);
197                 assert_eq!(pos, slot_for(a as u64, level));
198             }
199         }
200     }
201 }
202