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 = 64;
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)) as usize;
116 let occupied = self.occupied.rotate_right(now_slot as u32);
117 let zeros = occupied.trailing_zeros() as usize;
118 let slot = (zeros + now_slot) % LEVEL_MULT;
119
120 Some(slot)
121 }
122
123 pub(crate) unsafe fn add_entry(&mut self, item: TimerHandle) {
124 let slot = slot_for(unsafe { item.registered_when() }, self.level);
125
126 self.slot[slot].push_front(item);
127
128 self.occupied |= occupied_bit(slot);
129 }
130
131 pub(crate) unsafe fn remove_entry(&mut self, item: NonNull<TimerShared>) {
132 let slot = slot_for(unsafe { item.as_ref().registered_when() }, self.level);
133
134 unsafe { self.slot[slot].remove(item) };
135 if self.slot[slot].is_empty() {
136 debug_assert!(self.occupied & occupied_bit(slot) != 0);
138
139 self.occupied ^= occupied_bit(slot);
141 }
142 }
143
144 pub(crate) fn take_slot(&mut self, slot: usize) -> LinkedList<TimerShared> {
145 self.occupied &= !occupied_bit(slot);
146
147 std::mem::take(&mut self.slot[slot])
148 }
149}
150
151impl fmt::Debug for Level {
152 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
153 fmt.debug_struct("Level")
154 .field("occupied", &self.occupied)
155 .finish()
156 }
157}
158
159fn occupied_bit(slot: usize) -> u64 {
160 1 << slot
161}
162
163fn slot_range(level: usize) -> u64 {
164 LEVEL_MULT.pow(level as u32) as u64
165}
166
167fn level_range(level: usize) -> u64 {
168 LEVEL_MULT as u64 * slot_range(level)
169}
170
171fn slot_for(duration: u64, level: usize) -> usize {
173 ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize
174}
175
176#[cfg(all(test, not(loom)))]
177mod test {
178 use super::*;
179
180 #[test]
181 fn test_slot_for() {
182 for pos in 0..64 {
183 assert_eq!(pos as usize, slot_for(pos, 0));
184 }
185
186 for level in 1..5 {
187 for pos in level..64 {
188 let a = pos * 64_usize.pow(level as u32);
189 assert_eq!(pos, slot_for(a as u64, level));
190 }
191 }
192 }
193}