| 1 | use crate::time::wheel::Stack; |
| 2 | |
| 3 | use std::fmt; |
| 4 | |
| 5 | /// Wheel for a single level in the timer. This wheel contains 64 slots. |
| 6 | pub(crate) struct Level<T> { |
| 7 | level: usize, |
| 8 | |
| 9 | /// Bit field tracking which slots currently contain entries. |
| 10 | /// |
| 11 | /// Using a bit field to track slots that contain entries allows avoiding a |
| 12 | /// scan to find entries. This field is updated when entries are added or |
| 13 | /// removed from a slot. |
| 14 | /// |
| 15 | /// The least-significant bit represents slot zero. |
| 16 | occupied: u64, |
| 17 | |
| 18 | /// Slots |
| 19 | slot: [T; LEVEL_MULT], |
| 20 | } |
| 21 | |
| 22 | /// Indicates when a slot must be processed next. |
| 23 | #[derive(Debug)] |
| 24 | pub(crate) struct Expiration { |
| 25 | /// The level containing the slot. |
| 26 | pub(crate) level: usize, |
| 27 | |
| 28 | /// The slot index. |
| 29 | pub(crate) slot: usize, |
| 30 | |
| 31 | /// The instant at which the slot needs to be processed. |
| 32 | pub(crate) deadline: u64, |
| 33 | } |
| 34 | |
| 35 | /// Level multiplier. |
| 36 | /// |
| 37 | /// Being a power of 2 is very important. |
| 38 | const LEVEL_MULT: usize = 64; |
| 39 | |
| 40 | impl<T: Stack> Level<T> { |
| 41 | pub(crate) fn new(level: usize) -> Level<T> { |
| 42 | // Rust's derived implementations for arrays require that the value |
| 43 | // contained by the array be `Copy`. So, here we have to manually |
| 44 | // initialize every single slot. |
| 45 | macro_rules! s { |
| 46 | () => { |
| 47 | T::default() |
| 48 | }; |
| 49 | } |
| 50 | |
| 51 | Level { |
| 52 | level, |
| 53 | occupied: 0, |
| 54 | slot: [ |
| 55 | // It does not look like the necessary traits are |
| 56 | // derived for [T; 64]. |
| 57 | s!(), |
| 58 | s!(), |
| 59 | s!(), |
| 60 | s!(), |
| 61 | s!(), |
| 62 | s!(), |
| 63 | s!(), |
| 64 | s!(), |
| 65 | s!(), |
| 66 | s!(), |
| 67 | s!(), |
| 68 | s!(), |
| 69 | s!(), |
| 70 | s!(), |
| 71 | s!(), |
| 72 | s!(), |
| 73 | s!(), |
| 74 | s!(), |
| 75 | s!(), |
| 76 | s!(), |
| 77 | s!(), |
| 78 | s!(), |
| 79 | s!(), |
| 80 | s!(), |
| 81 | s!(), |
| 82 | s!(), |
| 83 | s!(), |
| 84 | s!(), |
| 85 | s!(), |
| 86 | s!(), |
| 87 | s!(), |
| 88 | s!(), |
| 89 | s!(), |
| 90 | s!(), |
| 91 | s!(), |
| 92 | s!(), |
| 93 | s!(), |
| 94 | s!(), |
| 95 | s!(), |
| 96 | s!(), |
| 97 | s!(), |
| 98 | s!(), |
| 99 | s!(), |
| 100 | s!(), |
| 101 | s!(), |
| 102 | s!(), |
| 103 | s!(), |
| 104 | s!(), |
| 105 | s!(), |
| 106 | s!(), |
| 107 | s!(), |
| 108 | s!(), |
| 109 | s!(), |
| 110 | s!(), |
| 111 | s!(), |
| 112 | s!(), |
| 113 | s!(), |
| 114 | s!(), |
| 115 | s!(), |
| 116 | s!(), |
| 117 | s!(), |
| 118 | s!(), |
| 119 | s!(), |
| 120 | s!(), |
| 121 | ], |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | /// Finds the slot that needs to be processed next and returns the slot and |
| 126 | /// `Instant` at which this slot must be processed. |
| 127 | pub(crate) fn next_expiration(&self, now: u64) -> Option<Expiration> { |
| 128 | // Use the `occupied` bit field to get the index of the next slot that |
| 129 | // needs to be processed. |
| 130 | let slot = match self.next_occupied_slot(now) { |
| 131 | Some(slot) => slot, |
| 132 | None => return None, |
| 133 | }; |
| 134 | |
| 135 | // From the slot index, calculate the `Instant` at which it needs to be |
| 136 | // processed. This value *must* be in the future with respect to `now`. |
| 137 | |
| 138 | let level_range = level_range(self.level); |
| 139 | let slot_range = slot_range(self.level); |
| 140 | |
| 141 | // TODO: This can probably be simplified w/ power of 2 math |
| 142 | let level_start = now - (now % level_range); |
| 143 | let mut deadline = level_start + slot as u64 * slot_range; |
| 144 | if deadline < now { |
| 145 | // A timer is in a slot "prior" to the current time. This can occur |
| 146 | // because we do not have an infinite hierarchy of timer levels, and |
| 147 | // eventually a timer scheduled for a very distant time might end up |
| 148 | // being placed in a slot that is beyond the end of all of the |
| 149 | // arrays. |
| 150 | // |
| 151 | // To deal with this, we first limit timers to being scheduled no |
| 152 | // more than MAX_DURATION ticks in the future; that is, they're at |
| 153 | // most one rotation of the top level away. Then, we force timers |
| 154 | // that logically would go into the top+1 level, to instead go into |
| 155 | // the top level's slots. |
| 156 | // |
| 157 | // What this means is that the top level's slots act as a |
| 158 | // pseudo-ring buffer, and we rotate around them indefinitely. If we |
| 159 | // compute a deadline before now, and it's the top level, it |
| 160 | // therefore means we're actually looking at a slot in the future. |
| 161 | debug_assert_eq!(self.level, super::NUM_LEVELS - 1); |
| 162 | |
| 163 | deadline += level_range; |
| 164 | } |
| 165 | debug_assert!( |
| 166 | deadline >= now, |
| 167 | "deadline={:016X}; now={:016X}; level={}; slot={}; occupied={:b}" , |
| 168 | deadline, |
| 169 | now, |
| 170 | self.level, |
| 171 | slot, |
| 172 | self.occupied |
| 173 | ); |
| 174 | |
| 175 | Some(Expiration { |
| 176 | level: self.level, |
| 177 | slot, |
| 178 | deadline, |
| 179 | }) |
| 180 | } |
| 181 | |
| 182 | fn next_occupied_slot(&self, now: u64) -> Option<usize> { |
| 183 | if self.occupied == 0 { |
| 184 | return None; |
| 185 | } |
| 186 | |
| 187 | // Get the slot for now using Maths |
| 188 | let now_slot = (now / slot_range(self.level)) as usize; |
| 189 | let occupied = self.occupied.rotate_right(now_slot as u32); |
| 190 | let zeros = occupied.trailing_zeros() as usize; |
| 191 | let slot = (zeros + now_slot) % 64; |
| 192 | |
| 193 | Some(slot) |
| 194 | } |
| 195 | |
| 196 | pub(crate) fn add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store) { |
| 197 | let slot = slot_for(when, self.level); |
| 198 | |
| 199 | self.slot[slot].push(item, store); |
| 200 | self.occupied |= occupied_bit(slot); |
| 201 | } |
| 202 | |
| 203 | pub(crate) fn remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store) { |
| 204 | let slot = slot_for(when, self.level); |
| 205 | |
| 206 | self.slot[slot].remove(item, store); |
| 207 | |
| 208 | if self.slot[slot].is_empty() { |
| 209 | // The bit is currently set |
| 210 | debug_assert!(self.occupied & occupied_bit(slot) != 0); |
| 211 | |
| 212 | // Unset the bit |
| 213 | self.occupied ^= occupied_bit(slot); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | pub(crate) fn pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option<T::Owned> { |
| 218 | let ret = self.slot[slot].pop(store); |
| 219 | |
| 220 | if ret.is_some() && self.slot[slot].is_empty() { |
| 221 | // The bit is currently set |
| 222 | debug_assert!(self.occupied & occupied_bit(slot) != 0); |
| 223 | |
| 224 | self.occupied ^= occupied_bit(slot); |
| 225 | } |
| 226 | |
| 227 | ret |
| 228 | } |
| 229 | |
| 230 | pub(crate) fn peek_entry_slot(&self, slot: usize) -> Option<T::Owned> { |
| 231 | self.slot[slot].peek() |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | impl<T> fmt::Debug for Level<T> { |
| 236 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 237 | fmt.debug_struct("Level" ) |
| 238 | .field("occupied" , &self.occupied) |
| 239 | .finish() |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | fn occupied_bit(slot: usize) -> u64 { |
| 244 | 1 << slot |
| 245 | } |
| 246 | |
| 247 | fn slot_range(level: usize) -> u64 { |
| 248 | LEVEL_MULT.pow(level as u32) as u64 |
| 249 | } |
| 250 | |
| 251 | fn level_range(level: usize) -> u64 { |
| 252 | LEVEL_MULT as u64 * slot_range(level) |
| 253 | } |
| 254 | |
| 255 | /// Convert a duration (milliseconds) and a level to a slot position |
| 256 | fn slot_for(duration: u64, level: usize) -> usize { |
| 257 | ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize |
| 258 | } |
| 259 | |
| 260 | #[cfg (all(test, not(loom)))] |
| 261 | mod test { |
| 262 | use super::*; |
| 263 | |
| 264 | #[test] |
| 265 | fn test_slot_for() { |
| 266 | for pos in 0..64 { |
| 267 | assert_eq!(pos as usize, slot_for(pos, 0)); |
| 268 | } |
| 269 | |
| 270 | for level in 1..5 { |
| 271 | for pos in level..64 { |
| 272 | let a = pos * 64_usize.pow(level as u32); |
| 273 | assert_eq!(pos as usize, slot_for(a as u64, level)); |
| 274 | } |
| 275 | } |
| 276 | } |
| 277 | } |
| 278 | |