tokio/runtime/time/wheel/
level.rs1use crate::runtime::time::{TimerHandle, TimerShared};
2use crate::util::linked_list::LinkedList;
3
4use std::{array, fmt, ptr::NonNull};
5
6pub(crate) struct Level {
8 level: usize,
9
10 occupied: u64,
18
19 slot: [LinkedList<TimerShared>; LEVEL_MULT],
21}
22
23#[derive(Debug)]
25pub(crate) struct Expiration {
26 pub(crate) level: usize,
28
29 pub(crate) slot: usize,
31
32 pub(crate) deadline: u64,
34}
35
36const LEVEL_MULT: usize = 1 << super::BITS_PER_LEVEL;
40
41impl Level {
42 pub(crate) fn new(level: usize) -> Level {
43 Level {
44 level,
45 occupied: 0,
46 slot: array::from_fn(|_| LinkedList::default()),
47 }
48 }
49
50 pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> {
53 let slot = self.next_occupied_slot(now)?;
56
57 let level_range = level_range(self.level);
61 let slot_range = slot_range(self.level);
62
63 let level_start = now & !(level_range - 1);
66 let mut deadline = level_start + slot as u64 * slot_range;
67
68 if deadline <= now {
69 debug_assert_eq!(self.level, super::NUM_LEVELS - 1);
86
87 deadline += level_range;
88 }
89
90 debug_assert!(
91 deadline >= now,
92 "deadline={:016X}; now={:016X}; level={}; lr={:016X}, sr={:016X}, slot={}; occupied={:b}",
93 deadline,
94 now,
95 self.level,
96 level_range,
97 slot_range,
98 slot,
99 self.occupied
100 );
101
102 Some(Expiration {
103 level: self.level,
104 slot,
105 deadline,
106 })
107 }
108
109 fn next_occupied_slot(&self, now: u64) -> Option<usize> {
110 if self.occupied == 0 {
111 return None;
112 }
113
114 let now_slot = ((now / slot_range(self.level)) % LEVEL_MULT as u64) as usize + 1;
123 let occupied = self.occupied.rotate_right(now_slot as u32);
124 let zeros = occupied.trailing_zeros() as usize;
125 let slot = (zeros + now_slot) % LEVEL_MULT;
126
127 Some(slot)
128 }
129
130 pub(crate) unsafe fn add_entry(&mut self, item: TimerHandle) {
131 let slot = slot_for(unsafe { item.registered_when() }, self.level);
132
133 self.slot[slot].push_front(item);
134
135 self.occupied |= occupied_bit(slot);
136 }
137
138 pub(crate) unsafe fn remove_entry(&mut self, item: NonNull<TimerShared>) {
139 let slot = slot_for(unsafe { item.as_ref().registered_when() }, self.level);
140
141 unsafe {
142 assert!(
143 self.slot[slot].remove(item).is_some(),
144 "Attempt to remove item not present in the timing wheel"
145 )
146 };
147 if self.slot[slot].is_empty() {
148 debug_assert!(self.occupied & occupied_bit(slot) != 0);
150
151 self.occupied ^= occupied_bit(slot);
153 }
154 }
155
156 pub(crate) fn take_slot(&mut self, slot: usize) -> LinkedList<TimerShared> {
157 self.occupied &= !occupied_bit(slot);
158
159 std::mem::take(&mut self.slot[slot])
160 }
161}
162
163impl fmt::Debug for Level {
164 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
165 fmt.debug_struct("Level")
166 .field("occupied", &self.occupied)
167 .finish()
168 }
169}
170
171fn occupied_bit(slot: usize) -> u64 {
172 1 << slot
173}
174
175fn slot_range(level: usize) -> u64 {
176 1 << (super::BITS_PER_LEVEL * level)
177}
178
179fn level_range(level: usize) -> u64 {
180 1 << (super::BITS_PER_LEVEL * (level + 1))
181}
182
183fn slot_for(duration: u64, level: usize) -> usize {
185 ((duration >> (level * super::BITS_PER_LEVEL)) % LEVEL_MULT as u64) as usize
186}
187
188#[cfg(all(test, not(loom)))]
189mod test {
190 use super::*;
191
192 #[test]
193 fn test_slot_for() {
194 for pos in 0..64 {
195 assert_eq!(pos as usize, slot_for(pos, 0));
196 }
197
198 for level in 1..5 {
199 for pos in level..64 {
200 let a = pos * 64_usize.pow(level as u32);
201 assert_eq!(pos, slot_for(a as u64, level));
202 }
203 }
204 }
205
206 fn level_with(level: usize, occupied: u64) -> Level {
207 let mut level = Level::new(level);
208 level.occupied = occupied;
209 level
210 }
211
212 #[test]
213 fn next_occupied_slot_on_an_empty_level() {
214 assert_eq!(Level::new(0).next_occupied_slot(0), None);
215 assert_eq!(Level::new(5).next_occupied_slot(1 << 36), None);
216 }
217
218 #[test]
219 fn next_occupied_slot_of_a_single_slot() {
220 let level = level_with(0, 1 << 10);
222
223 assert_eq!(level.next_occupied_slot(0), Some(10));
224 assert_eq!(level.next_occupied_slot(9), Some(10));
225 assert_eq!(level.next_occupied_slot(10), Some(10));
227 assert_eq!(level.next_occupied_slot(11), Some(10));
228 assert_eq!(level.next_occupied_slot(64 + 3), Some(10));
230 }
231
232 #[test]
233 fn next_occupied_slot_picks_the_nearest_slot_forward() {
234 let level = level_with(0, (1 << 10) | (1 << 40));
235
236 assert_eq!(level.next_occupied_slot(0), Some(10));
237 assert_eq!(level.next_occupied_slot(20), Some(40));
238 assert_eq!(level.next_occupied_slot(41), Some(10));
240 }
241
242 #[test]
243 fn next_occupied_slot_skips_the_slot_holding_now() {
244 let level = level_with(5, 0b11);
248
249 assert_eq!(level.next_occupied_slot(0), Some(1));
250 assert_eq!(level.next_occupied_slot((1 << 30) - 1), Some(1));
251
252 let level = level_with(5, 1 | (1 << 40));
254
255 assert_eq!(level.next_occupied_slot(0), Some(40));
256 }
257
258 #[test]
259 fn next_occupied_slot_of_the_slot_holding_now_when_it_is_the_only_one() {
260 let level = level_with(5, 1);
263
264 assert_eq!(level.next_occupied_slot(0), Some(0));
265 assert_eq!(level.next_occupied_slot((1 << 30) - 1), Some(0));
266 }
267
268 #[test]
269 fn next_occupied_slot_of_the_last_slot() {
270 let level = level_with(5, 1 << 63);
271
272 assert_eq!(level.next_occupied_slot(62 << 30), Some(63));
273 assert_eq!(level.next_occupied_slot(63 << 30), Some(63));
274 }
275
276 #[test]
277 fn next_expiration_reports_the_start_of_the_slot() {
278 let expiration = level_with(1, 1 << 3).next_expiration(100).unwrap();
280
281 assert_eq!(expiration.level, 1);
282 assert_eq!(expiration.slot, 3);
283 assert_eq!(expiration.deadline, 192);
284 }
285
286 #[test]
287 fn next_expiration_below_the_top_level() {
288 let level = level_with(4, 1 << 1);
289
290 assert_eq!(level.next_expiration(0).unwrap().deadline, 1 << 24);
291 assert_eq!(level.next_expiration(1000).unwrap().deadline, 1 << 24);
292 }
293
294 #[test]
295 fn next_expiration_at_the_top_level() {
296 let level = level_with(5, 1 << 1);
297
298 assert_eq!(level.next_expiration(0).unwrap().deadline, 1 << 30);
299 assert_eq!(level.next_expiration(1000).unwrap().deadline, 1 << 30);
300 }
301
302 #[test]
303 fn next_expiration_wraps_a_slot_at_or_behind_now() {
304 let level = level_with(5, 1 << 0);
307
308 assert_eq!(level.next_expiration(0).unwrap().deadline, 1 << 36);
309 assert_eq!(
310 level.next_expiration((1 << 30) + 10).unwrap().deadline,
311 1 << 36
312 );
313 }
314}