tokio_util/time/wheel/
level.rs1use crate::time::wheel::Stack;
2
3use std::fmt;
4
5pub(crate) struct Level<T> {
7 level: usize,
8
9 occupied: u64,
17
18 slot: [T; LEVEL_MULT],
20}
21
22#[derive(Debug)]
24pub(crate) struct Expiration {
25 pub(crate) level: usize,
27
28 pub(crate) slot: usize,
30
31 pub(crate) deadline: u64,
33}
34
35const LEVEL_MULT: usize = 64;
39
40impl<T: Stack> 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 pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> {
52 let slot = match self.next_occupied_slot(now) {
55 Some(slot) => slot,
56 None => return None,
57 };
58
59 let level_range = level_range(self.level);
63 let slot_range = slot_range(self.level);
64
65 let level_start = now - (now % level_range);
67 let mut deadline = level_start + slot as u64 * slot_range;
68 if deadline < now {
69 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
106 fn next_occupied_slot(&self, now: u64) -> Option<usize> {
107 if self.occupied == 0 {
108 return None;
109 }
110
111 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
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
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 debug_assert!(self.occupied & occupied_bit(slot) != 0);
135
136 self.occupied ^= occupied_bit(slot);
138 }
139 }
140
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 debug_assert!(self.occupied & occupied_bit(slot) != 0);
147
148 self.occupied ^= occupied_bit(slot);
149 }
150
151 ret
152 }
153
154 pub(crate) fn peek_entry_slot(&self, slot: usize) -> Option<T::Owned> {
155 self.slot[slot].peek()
156 }
157}
158
159impl<T> fmt::Debug for Level<T> {
160 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
161 fmt.debug_struct("Level")
162 .field("occupied", &self.occupied)
163 .finish()
164 }
165}
166
167fn occupied_bit(slot: usize) -> u64 {
168 1 << slot
169}
170
171fn slot_range(level: usize) -> u64 {
172 LEVEL_MULT.pow(level as u32) as u64
173}
174
175fn level_range(level: usize) -> u64 {
176 LEVEL_MULT as u64 * slot_range(level)
177}
178
179fn 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)))]
185mod test {
186 use super::*;
187
188 #[test]
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}