Pop the next task according to the 8:4:2 drain ratio. Each call to `pop_next` pulls one task from whichever tier has remaining budget in the current cycle. Once a tier's budget for the cycle is exhausted the next lower tier is tried; if *that* is also exhausted or empty the remaining budget cascades further down. Cycle state is maintained via the mutable `cycle` counter passed by the caller (re
(&mut self, cycle: &mut usize)
| 188 | /// |
| 189 | /// Returns `None` when all tiers are empty. |
| 190 | pub fn pop_next(&mut self, cycle: &mut usize) -> Option<QueuedTask> { |
| 191 | // Within a 14-slot window: slots 0–7 = Critical, 8–11 = High, 12–13 = Low. |
| 192 | // When a preferred tier is empty its slots go to the next lower tier. |
| 193 | |
| 194 | const CYCLE_LEN: usize = BUDGET_CRITICAL + BUDGET_HIGH + BUDGET_LOW; // 14 |
| 195 | |
| 196 | let pos = *cycle % CYCLE_LEN; |
| 197 | |
| 198 | // Determine preferred tier based on cycle position. |
| 199 | let preferred = if pos < BUDGET_CRITICAL { |
| 200 | TierPref::Critical |
| 201 | } else if pos < BUDGET_CRITICAL + BUDGET_HIGH { |
| 202 | TierPref::High |
| 203 | } else { |
| 204 | TierPref::Low |
| 205 | }; |
| 206 | |
| 207 | let task = match preferred { |
| 208 | TierPref::Critical => self |
| 209 | .critical |
| 210 | .pop_front() |
| 211 | .or_else(|| self.high.pop_front()) |
| 212 | .or_else(|| self.low.pop_front()), |
| 213 | TierPref::High => self |
| 214 | .high |
| 215 | .pop_front() |
| 216 | .or_else(|| self.critical.pop_front()) |
| 217 | .or_else(|| self.low.pop_front()), |
| 218 | TierPref::Low => self |
| 219 | .low |
| 220 | .pop_front() |
| 221 | .or_else(|| self.high.pop_front()) |
| 222 | .or_else(|| self.critical.pop_front()), |
| 223 | }; |
| 224 | |
| 225 | if task.is_some() { |
| 226 | *cycle = cycle.wrapping_add(1); |
| 227 | } |
| 228 | |
| 229 | task |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | impl Default for PriorityQueues { |