Skip to main content

tokio/runtime/time/wheel/
level.rs

1use crate::runtime::time::{TimerHandle, TimerShared};
2use crate::util::linked_list::LinkedList;
3
4use std::{array, fmt, ptr::NonNull};
5
6/// Wheel for a single level in the timer. This wheel contains 64 slots.
7pub(crate) struct Level {
8    level: usize,
9
10    /// Bit field tracking which slots currently contain entries.
11    ///
12    /// Using a bit field to track slots that contain entries allows avoiding a
13    /// scan to find entries. This field is updated when entries are added or
14    /// removed from a slot.
15    ///
16    /// The least-significant bit represents slot zero.
17    occupied: u64,
18
19    /// Slots. We access these via the EntryInner `current_list` as well, so this needs to be an `UnsafeCell`.
20    slot: [LinkedList<TimerShared>; LEVEL_MULT],
21}
22
23/// Indicates when a slot must be processed next.
24#[derive(Debug)]
25pub(crate) struct Expiration {
26    /// The level containing the slot.
27    pub(crate) level: usize,
28
29    /// The slot index.
30    pub(crate) slot: usize,
31
32    /// The instant at which the slot needs to be processed.
33    pub(crate) deadline: u64,
34}
35
36/// Level multiplier.
37///
38/// Being a power of 2 is very important.
39const 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    /// Finds the slot that needs to be processed next and returns the slot and
51    /// `Instant` at which this slot must be processed.
52    pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> {
53        // Use the `occupied` bit field to get the index of the next slot that
54        // needs to be processed.
55        let slot = self.next_occupied_slot(now)?;
56
57        // From the slot index, calculate the `Instant` at which it needs to be
58        // processed. This value *must* be in the future with respect to `now`.
59
60        let level_range = level_range(self.level);
61        let slot_range = slot_range(self.level);
62
63        // Compute the start date of the current level by masking the low bits
64        // of `now` (`level_range` is a power of 2).
65        let level_start = now & !(level_range - 1);
66        let mut deadline = level_start + slot as u64 * slot_range;
67
68        if deadline <= now {
69            // A timer is in a slot "prior" to the current time. This can occur
70            // because we do not have an infinite hierarchy of timer levels, and
71            // eventually a timer scheduled for a very distant time might end up
72            // being placed in a slot that is beyond the end of all of the
73            // arrays.
74            //
75            // To deal with this, we first limit timers to being scheduled no
76            // more than MAX_DURATION ticks in the future; that is, they're at
77            // most one rotation of the top level away. Then, we force timers
78            // that logically would go into the top+1 level, to instead go into
79            // the top level's slots.
80            //
81            // What this means is that the top level's slots act as a
82            // pseudo-ring buffer, and we rotate around them indefinitely. If we
83            // compute a deadline before now, and it's the top level, it
84            // therefore means we're actually looking at a slot in the future.
85            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        // Add the +1 offset for the `now_slot` to ignore the slot that `now` fits in,
115        // since it's the farthest timer that could appear from `now`.
116        // This is mostly relevant for the top level because it acts as a
117        // pseudo-ring buffer: timers that would logically go past the top level are
118        // fudged into it by `level_for` and the `MAX_DURATION` cap, so the slot holding
119        // `now` can be occupied by an entry that is a whole rotation away.
120        // For the lower levels `level_for` always places an entry in a slot other
121        // than the one holding `now`, so `now_slot` is always empty there.
122        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            // The bit is currently set
149            debug_assert!(self.occupied & occupied_bit(slot) != 0);
150
151            // Unset the bit
152            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
183/// Converts a duration (milliseconds) and a level to a slot position.
184fn 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        // slot 10 of level 0, i.e. tick 10 of every 64-tick window
221        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        // `now` inside the slot itself, and past it
226        assert_eq!(level.next_occupied_slot(10), Some(10));
227        assert_eq!(level.next_occupied_slot(11), Some(10));
228        // `now` past the window: the slot is taken modulo 64
229        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        // nothing left ahead in this window, so the scan wraps to slot 10
239        assert_eq!(level.next_occupied_slot(41), Some(10));
240    }
241
242    #[test]
243    fn next_occupied_slot_skips_the_slot_holding_now() {
244        // The occurrence of slot 0 in this rotation has already started, so it
245        // can only be processed a full `level_range` later. Slot 1 is still
246        // ahead of `now` in this rotation and therefore expires first.
247        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        // The same holds for any later slot, not just the adjacent one.
253        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        // Nothing is ahead of `now`, so slot 0 is the earliest expiration even
261        // though it is reached only in the next rotation.
262        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        // slot 3 of level 1: slots are 64 ticks wide, so it starts at tick 192
279        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        // Slot 0 of the top level starts at tick 0, so its next occurrence is a
305        // full rotation of the level away.
306        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}