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> { |
41 | pub(crate) fn new(level: usize) -> Level<T> { |
42 | // Rust's derived implementations for arrays require that the value |
43 | // contained by the array be `Copy`. So, here we have to manually |
44 | // initialize every single slot. |
45 | macro_rules! s { |
46 | () => { |
47 | T::default() |
48 | }; |
49 | } |
50 | |
51 | Level { |
52 | level, |
53 | occupied: 0, |
54 | slot: [ |
55 | // It does not look like the necessary traits are |
56 | // derived for [T; 64]. |
57 | s!(), |
58 | s!(), |
59 | s!(), |
60 | s!(), |
61 | s!(), |
62 | s!(), |
63 | s!(), |
64 | s!(), |
65 | s!(), |
66 | s!(), |
67 | s!(), |
68 | s!(), |
69 | s!(), |
70 | s!(), |
71 | s!(), |
72 | s!(), |
73 | s!(), |
74 | s!(), |
75 | s!(), |
76 | s!(), |
77 | s!(), |
78 | s!(), |
79 | s!(), |
80 | s!(), |
81 | s!(), |
82 | s!(), |
83 | s!(), |
84 | s!(), |
85 | s!(), |
86 | s!(), |
87 | s!(), |
88 | s!(), |
89 | s!(), |
90 | s!(), |
91 | s!(), |
92 | s!(), |
93 | s!(), |
94 | s!(), |
95 | s!(), |
96 | s!(), |
97 | s!(), |
98 | s!(), |
99 | s!(), |
100 | s!(), |
101 | s!(), |
102 | s!(), |
103 | s!(), |
104 | s!(), |
105 | s!(), |
106 | s!(), |
107 | s!(), |
108 | s!(), |
109 | s!(), |
110 | s!(), |
111 | s!(), |
112 | s!(), |
113 | s!(), |
114 | s!(), |
115 | s!(), |
116 | s!(), |
117 | s!(), |
118 | s!(), |
119 | s!(), |
120 | s!(), |
121 | ], |
122 | } |
123 | } |
124 | |
125 | /// Finds the slot that needs to be processed next and returns the slot and |
126 | /// `Instant` at which this slot must be processed. |
127 | pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> { |
128 | // Use the `occupied` bit field to get the index of the next slot that |
129 | // needs to be processed. |
130 | let slot = match self.next_occupied_slot(now) { |
131 | Some(slot) => slot, |
132 | None => return None, |
133 | }; |
134 | |
135 | // From the slot index, calculate the `Instant` at which it needs to be |
136 | // processed. This value *must* be in the future with respect to `now`. |
137 | |
138 | let level_range = level_range(self.level); |
139 | let slot_range = slot_range(self.level); |
140 | |
141 | // TODO: This can probably be simplified w/ power of 2 math |
142 | let level_start = now - (now % level_range); |
143 | let mut deadline = level_start + slot as u64 * slot_range; |
144 | if deadline < now { |
145 | // A timer is in a slot "prior" to the current time. This can occur |
146 | // because we do not have an infinite hierarchy of timer levels, and |
147 | // eventually a timer scheduled for a very distant time might end up |
148 | // being placed in a slot that is beyond the end of all of the |
149 | // arrays. |
150 | // |
151 | // To deal with this, we first limit timers to being scheduled no |
152 | // more than MAX_DURATION ticks in the future; that is, they're at |
153 | // most one rotation of the top level away. Then, we force timers |
154 | // that logically would go into the top+1 level, to instead go into |
155 | // the top level's slots. |
156 | // |
157 | // What this means is that the top level's slots act as a |
158 | // pseudo-ring buffer, and we rotate around them indefinitely. If we |
159 | // compute a deadline before now, and it's the top level, it |
160 | // therefore means we're actually looking at a slot in the future. |
161 | debug_assert_eq!(self.level, super::NUM_LEVELS - 1); |
162 | |
163 | deadline += level_range; |
164 | } |
165 | debug_assert!( |
166 | deadline >= now, |
167 | "deadline={:016X}; now={:016X}; level={}; slot={}; occupied={:b}" , |
168 | deadline, |
169 | now, |
170 | self.level, |
171 | slot, |
172 | self.occupied |
173 | ); |
174 | |
175 | Some(Expiration { |
176 | level: self.level, |
177 | slot, |
178 | deadline, |
179 | }) |
180 | } |
181 | |
182 | fn next_occupied_slot(&self, now: u64) -> Option<usize> { |
183 | if self.occupied == 0 { |
184 | return None; |
185 | } |
186 | |
187 | // Get the slot for now using Maths |
188 | let now_slot = (now / slot_range(self.level)) as usize; |
189 | let occupied = self.occupied.rotate_right(now_slot as u32); |
190 | let zeros = occupied.trailing_zeros() as usize; |
191 | let slot = (zeros + now_slot) % 64; |
192 | |
193 | Some(slot) |
194 | } |
195 | |
196 | pub(crate) fn add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store) { |
197 | let slot = slot_for(when, self.level); |
198 | |
199 | self.slot[slot].push(item, store); |
200 | self.occupied |= occupied_bit(slot); |
201 | } |
202 | |
203 | pub(crate) fn remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store) { |
204 | let slot = slot_for(when, self.level); |
205 | |
206 | self.slot[slot].remove(item, store); |
207 | |
208 | if self.slot[slot].is_empty() { |
209 | // The bit is currently set |
210 | debug_assert!(self.occupied & occupied_bit(slot) != 0); |
211 | |
212 | // Unset the bit |
213 | self.occupied ^= occupied_bit(slot); |
214 | } |
215 | } |
216 | |
217 | pub(crate) fn pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option<T::Owned> { |
218 | let ret = self.slot[slot].pop(store); |
219 | |
220 | if ret.is_some() && self.slot[slot].is_empty() { |
221 | // The bit is currently set |
222 | debug_assert!(self.occupied & occupied_bit(slot) != 0); |
223 | |
224 | self.occupied ^= occupied_bit(slot); |
225 | } |
226 | |
227 | ret |
228 | } |
229 | |
230 | pub(crate) fn peek_entry_slot(&self, slot: usize) -> Option<T::Owned> { |
231 | self.slot[slot].peek() |
232 | } |
233 | } |
234 | |
235 | impl<T> fmt::Debug for Level<T> { |
236 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
237 | fmt.debug_struct("Level" ) |
238 | .field("occupied" , &self.occupied) |
239 | .finish() |
240 | } |
241 | } |
242 | |
243 | fn occupied_bit(slot: usize) -> u64 { |
244 | 1 << slot |
245 | } |
246 | |
247 | fn slot_range(level: usize) -> u64 { |
248 | LEVEL_MULT.pow(level as u32) as u64 |
249 | } |
250 | |
251 | fn level_range(level: usize) -> u64 { |
252 | LEVEL_MULT as u64 * slot_range(level) |
253 | } |
254 | |
255 | /// Convert a duration (milliseconds) and a level to a slot position |
256 | fn slot_for(duration: u64, level: usize) -> usize { |
257 | ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize |
258 | } |
259 | |
260 | #[cfg (all(test, not(loom)))] |
261 | mod test { |
262 | use super::*; |
263 | |
264 | #[test] |
265 | fn test_slot_for() { |
266 | for pos in 0..64 { |
267 | assert_eq!(pos as usize, slot_for(pos, 0)); |
268 | } |
269 | |
270 | for level in 1..5 { |
271 | for pos in level..64 { |
272 | let a = pos * 64_usize.pow(level as u32); |
273 | assert_eq!(pos as usize, slot_for(a as u64, level)); |
274 | } |
275 | } |
276 | } |
277 | } |
278 | |