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 = self.next_occupied_slot(now)?;
55
56 let level_range = level_range(self.level);
60 let slot_range = slot_range(self.level);
61
62 let level_start = now - (now % level_range);
64 let mut deadline = level_start + slot as u64 * slot_range;
65 if deadline < now {
66 debug_assert_eq!(self.level, super::NUM_LEVELS - 1);
83
84 deadline += level_range;
85 }
86 debug_assert!(
87 deadline >= now,
88 "deadline={:016X}; now={:016X}; level={}; slot={}; occupied={:b}",
89 deadline,
90 now,
91 self.level,
92 slot,
93 self.occupied
94 );
95
96 Some(Expiration {
97 level: self.level,
98 slot,
99 deadline,
100 })
101 }
102
103 fn next_occupied_slot(&self, now: u64) -> Option<usize> {
104 if self.occupied == 0 {
105 return None;
106 }
107
108 let now_slot = (now / slot_range(self.level)) as usize;
110 let occupied = self.occupied.rotate_right(now_slot as u32);
111 let zeros = occupied.trailing_zeros() as usize;
112 let slot = (zeros + now_slot) % 64;
113
114 Some(slot)
115 }
116
117 pub(crate) fn add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store) {
118 let slot = slot_for(when, self.level);
119
120 self.slot[slot].push(item, store);
121 self.occupied |= occupied_bit(slot);
122 }
123
124 pub(crate) fn remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store) {
125 let slot = slot_for(when, self.level);
126
127 self.slot[slot].remove(item, store);
128
129 if self.slot[slot].is_empty() {
130 debug_assert!(self.occupied & occupied_bit(slot) != 0);
132
133 self.occupied ^= occupied_bit(slot);
135 }
136 }
137
138 pub(crate) fn pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option<T::Owned> {
139 let ret = self.slot[slot].pop(store);
140
141 if ret.is_some() && self.slot[slot].is_empty() {
142 debug_assert!(self.occupied & occupied_bit(slot) != 0);
144
145 self.occupied ^= occupied_bit(slot);
146 }
147
148 ret
149 }
150
151 pub(crate) fn peek_entry_slot(&self, slot: usize) -> Option<T::Owned> {
152 self.slot[slot].peek()
153 }
154}
155
156impl<T> fmt::Debug for Level<T> {
157 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
158 fmt.debug_struct("Level")
159 .field("occupied", &self.occupied)
160 .finish()
161 }
162}
163
164fn occupied_bit(slot: usize) -> u64 {
165 1 << slot
166}
167
168fn slot_range(level: usize) -> u64 {
169 LEVEL_MULT.pow(level as u32) as u64
170}
171
172fn level_range(level: usize) -> u64 {
173 LEVEL_MULT as u64 * slot_range(level)
174}
175
176fn slot_for(duration: u64, level: usize) -> usize {
178 ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize
179}
180
181#[cfg(all(test, not(loom)))]
182mod test {
183 use super::*;
184
185 #[test]
186 fn test_slot_for() {
187 for pos in 0..64 {
188 assert_eq!(pos as usize, slot_for(pos, 0));
189 }
190
191 for level in 1..5 {
192 for pos in level..64 {
193 let a = pos * 64_usize.pow(level as u32);
194 assert_eq!(pos, slot_for(a as u64, level));
195 }
196 }
197 }
198}