1pub const EMPTY: usize = usize::MAX;
26
27#[derive(Copy, Clone)]
29pub struct Slot {
30 pub region: usize,
32 pub len: usize,
34 used: u32,
35}
36
37impl Slot {
38 const fn new() -> Self {
39 Self {
40 region: EMPTY,
41 len: 0,
42 used: 0,
43 }
44 }
45}
46
47pub struct SlotResidency<const SLOTS: usize, const WANTED: usize> {
50 pub slots: [Slot; SLOTS],
52 pub current: usize,
54 protected: usize,
55 loading: [usize; SLOTS],
56 wanted: [usize; WANTED],
57 clock: u32,
58 decode_ready: usize,
59}
60
61impl<const SLOTS: usize, const WANTED: usize> Default for SlotResidency<SLOTS, WANTED> {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl<const SLOTS: usize, const WANTED: usize> SlotResidency<SLOTS, WANTED> {
68 pub const fn new() -> Self {
70 Self {
71 slots: [Slot::new(); SLOTS],
72 current: 0,
73 protected: EMPTY,
74 loading: [EMPTY; SLOTS],
75 wanted: [EMPTY; WANTED],
76 clock: 0,
77 decode_ready: EMPTY,
78 }
79 }
80
81 pub fn find(&self, region: usize) -> Option<usize> {
83 if region == EMPTY {
84 None
85 } else {
86 self.slots
87 .iter()
88 .position(|s| s.region == region && s.len != 0)
89 }
90 }
91
92 pub fn find_stored(&self, region: usize) -> Option<usize> {
96 if region == EMPTY {
97 None
98 } else {
99 self.slots
100 .iter()
101 .position(|s| s.region == region && s.len == 0)
102 }
103 }
104
105 fn refresh_decode(&mut self) {
106 self.decode_ready = self
107 .wanted
108 .iter()
109 .find_map(|&r| self.find_stored(r))
110 .unwrap_or(EMPTY);
111 }
112
113 pub fn next_decode(&self, demand: usize) -> Option<usize> {
116 self.find_stored(demand).or(if self.decode_ready == EMPTY {
117 None
118 } else {
119 Some(self.decode_ready)
120 })
121 }
122
123 pub fn next_decode_during_read(&self, demand: usize, reading: usize) -> Option<usize> {
127 let slot = self.next_decode(demand)?;
128 if self.slots[slot].region == demand || !self.is_loading(reading) {
129 return Some(slot);
130 }
131 if demand != EMPTY && self.find(demand).is_none() {
134 None
135 } else {
136 Some(slot)
137 }
138 }
139
140 pub fn park_completed(&mut self, i: usize, region: usize) {
144 assert_eq!(self.loading[i], region);
145 assert_ne!(region, EMPTY);
146 assert_ne!(i, self.current);
147 assert_ne!(i, self.protected);
148 self.loading[i] = EMPTY;
149 self.slots[i].region = region;
150 self.slots[i].len = 0;
151 self.touch(i);
152 self.refresh_decode();
153 }
154
155 pub fn claim_stored(&mut self, i: usize) -> usize {
158 let region = self.slots[i].region;
159 assert_ne!(region, EMPTY);
160 assert_eq!(self.slots[i].len, 0);
161 assert_eq!(self.loading[i], EMPTY);
162 assert_ne!(i, self.current);
163 assert_ne!(i, self.protected);
164 self.slots[i] = Slot::new();
165 self.loading[i] = region;
166 self.refresh_decode();
167 region
168 }
169
170 pub fn protect_upload(&mut self, region: Option<usize>) -> bool {
174 match region {
175 None => {
176 self.protected = EMPTY;
177 true
178 }
179 Some(region) => match self.find(region) {
180 Some(i) => {
181 self.protected = i;
182 true
183 }
184 None => false,
185 },
186 }
187 }
188
189 pub fn set_wanted(&mut self, regions: &[usize]) {
192 let previous = self.wanted;
193 self.wanted = [EMPTY; WANTED];
194 let mut count = 0;
195 for ®ion in regions {
196 if region == EMPTY
197 || region == self.slots[self.current].region
198 || self.wanted.contains(®ion)
199 {
200 continue;
201 }
202 self.wanted[count] = region;
203 count += 1;
204 if count == self.wanted.len() {
205 break;
206 }
207 }
208 if self.wanted != previous {
209 self.refresh_decode();
210 }
211 }
212
213 pub fn evict_unwanted(&mut self, keep: &[usize]) -> usize {
217 let mut removed = 0;
218 for i in 0..SLOTS {
219 if i == self.current || i == self.protected || self.loading[i] != EMPTY {
220 continue;
221 }
222 let region = self.slots[i].region;
223 if region == EMPTY || keep.contains(®ion) {
224 continue;
225 }
226 self.slots[i] = Slot::new();
227 removed += 1;
228 }
229 if removed != 0 {
230 self.refresh_decode();
231 }
232 removed
233 }
234
235 pub fn is_loading(&self, region: usize) -> bool {
237 region != EMPTY && self.loading.contains(®ion)
238 }
239
240 pub fn release(&mut self, i: usize) {
243 self.loading[i] = EMPTY;
244 }
245
246 pub fn next_request(&self) -> Option<usize> {
249 self.wanted.iter().copied().find(|&r| {
250 r != EMPTY
251 && self.find(r).is_none()
252 && self.find_stored(r).is_none()
253 && !self.is_loading(r)
254 && self.victim(r, false).is_some()
255 })
256 }
257
258 fn touch(&mut self, i: usize) {
259 self.clock = self.clock.saturating_add(1);
261 if self.clock == u32::MAX {
262 for slot in &mut self.slots {
263 slot.used = slot.used.saturating_sub(u32::MAX / 2);
264 }
265 self.clock -= u32::MAX / 2;
266 }
267 self.slots[i].used = self.clock;
268 }
269
270 pub fn select(&mut self, region: usize) -> Option<bool> {
274 let i = self.find(region)?;
275 let changed = i != self.current;
276 self.current = i;
277 self.touch(i);
278 Some(changed)
279 }
280
281 fn victim(&self, region: usize, urgent: bool) -> Option<usize> {
285 if self.is_loading(region) || self.find_stored(region).is_some() {
286 return None;
287 }
288 let priority = self
289 .wanted
290 .iter()
291 .position(|&r| r == region)
292 .unwrap_or(usize::MAX);
293 let mut victim = None;
294 for i in 0..SLOTS {
295 if i == self.current || i == self.protected || self.loading[i] != EMPTY {
296 continue;
297 }
298 let s = &self.slots[i];
299 if !urgent
302 && s.region != EMPTY
303 && self
304 .wanted
305 .iter()
306 .position(|&r| r == s.region)
307 .is_some_and(|p| p <= priority)
308 {
309 continue;
310 }
311 let score = (s.region != EMPTY, self.wanted.contains(&s.region), s.used);
312 if victim.is_none_or(|(_, best)| score < best) {
313 victim = Some((i, score));
314 }
315 }
316 victim.map(|(i, _)| i)
317 }
318
319 pub fn can_reserve(&self, region: usize, urgent: bool) -> bool {
321 self.victim(region, urgent).is_some()
322 }
323
324 pub fn reserve(&mut self, region: usize, urgent: bool) -> Option<usize> {
327 let i = self.victim(region, urgent)?;
328 self.slots[i] = Slot::new();
329 self.loading[i] = region;
330 self.refresh_decode();
331 Some(i)
332 }
333
334 pub fn admit(&mut self, i: usize, region: usize, len: usize) {
336 assert_eq!(self.loading[i], region);
337 assert_ne!(len, 0);
338 self.loading[i] = EMPTY;
339 self.slots[i].region = region;
340 self.slots[i].len = len;
341 self.touch(i);
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 type Residency = SlotResidency<5, 4>;
350
351 fn load(r: &mut Residency, region: usize) -> usize {
352 let i = r.reserve(region, true).unwrap();
353 r.admit(i, region, 100);
354 i
355 }
356
357 #[test]
358 fn decode_admission_only_waits_for_an_urgent_unread_demand_with_live_transport() {
359 let mut r = Residency::new();
360 load(&mut r, 10);
361 r.select(10);
362 let stored = r.reserve(12, true).unwrap();
363 r.park_completed(stored, 12);
364 r.set_wanted(&[11, 12]);
365 assert_eq!(r.next_decode_during_read(EMPTY, EMPTY), Some(stored));
368 assert_eq!(r.next_decode_during_read(EMPTY, 11), Some(stored));
369 let read = r.reserve(11, true).unwrap();
370 assert_eq!(r.next_decode_during_read(EMPTY, 11), Some(stored));
371 assert_eq!(r.next_decode_during_read(11, 11), None);
372 assert_eq!(r.next_decode_during_read(12, 11), Some(stored));
373 r.set_wanted(&[12, 11]);
374 assert_eq!(r.next_decode_during_read(EMPTY, 11), Some(stored));
375 assert_eq!(r.next_decode_during_read(13, 11), None);
377 r.release(read);
378 assert_eq!(r.next_decode_during_read(13, EMPTY), Some(stored));
379 r.reserve(11, true).unwrap();
381 r.set_wanted(&[12]);
382 assert_eq!(r.next_decode_during_read(EMPTY, 11), Some(stored));
383 }
384
385 #[test]
386 fn obsolete_completed_payload_is_evictable_but_never_visible_as_a_room() {
387 let mut r = Residency::new();
388 load(&mut r, 10);
389 r.select(10).unwrap();
390 load(&mut r, 11);
391 load(&mut r, 12);
392 load(&mut r, 13);
393 r.protect_upload(Some(12));
394 let old = r.reserve(14, true).unwrap();
395 r.set_wanted(&[11, 12, 13, 15]);
396 assert_eq!(r.next_request(), None);
397 r.park_completed(old, 14);
398 assert!(!r.is_loading(14));
399 assert_eq!(r.find_stored(14), Some(old));
400 assert!(r.find(14).is_none());
401 assert_eq!(r.select(14), None);
402 assert!(!r.protect_upload(Some(14)));
403 assert_eq!(r.next_decode(EMPTY), None);
404 assert_eq!(r.next_request(), Some(15));
405 let next = r.reserve(15, false).unwrap();
406 assert_eq!(next, old);
407 assert!(r.find_stored(14).is_none());
408 assert_eq!(r.next_decode(EMPTY), None);
409 for id in [10, 11, 12, 13] {
410 assert!(r.find(id).is_some());
411 }
412 }
413
414 #[test]
415 fn brief_wishlist_reversal_reuses_completed_bytes_without_another_cd_request() {
416 let mut r = Residency::new();
417 load(&mut r, 10);
418 r.select(10).unwrap();
419 let read = r.reserve(11, true).unwrap();
420 let decoder = r.reserve(12, true).unwrap();
421 r.set_wanted(&[13]);
422 r.park_completed(read, 11);
423 assert!(r.is_loading(12));
424 assert_eq!(r.next_decode(EMPTY), None);
425 r.set_wanted(&[11, 13]);
426 assert_eq!(r.next_decode(EMPTY), Some(read));
427 assert_eq!(r.next_request(), Some(13));
428 assert!(!r.can_reserve(11, true));
429 assert_eq!(r.claim_stored(read), 11);
430 assert!(r.find_stored(11).is_none());
431 assert!(r.is_loading(11) && r.is_loading(12));
432 assert_eq!(r.next_decode(EMPTY), None);
433 assert!(r.find(11).is_none());
435 r.admit(read, 11, 100);
436 assert!(r.find(11).is_some());
437 r.release(decoder);
438 assert!(r.find(12).is_none());
439 }
440
441 #[test]
442 fn decoder_priority_follows_demand_then_latest_wishlist_without_reordering_cd_leases() {
443 let mut r = Residency::new();
444 load(&mut r, 10);
445 r.select(10).unwrap();
446 let a = r.reserve(11, true).unwrap();
447 let b = r.reserve(12, true).unwrap();
448 r.park_completed(a, 11);
449 r.park_completed(b, 12);
450 r.set_wanted(&[11, 12]);
451 assert_eq!(r.next_decode(EMPTY), Some(a));
452 assert_eq!(r.next_decode(12), Some(b));
453 assert_eq!(r.claim_stored(b), 12);
454 assert_eq!(r.next_decode(EMPTY), Some(a));
455 r.set_wanted(&[13]);
456 assert_eq!(r.next_decode(EMPTY), None);
457 assert_eq!(r.next_decode(11), Some(a)); r.release(b);
459 assert!(r.find(12).is_none() && r.find_stored(12).is_none());
460 assert_eq!(r.next_request(), Some(13));
461 }
462
463 #[test]
464 fn wanted_completed_payload_survives_lower_priority_capacity_pressure() {
465 let mut r = Residency::new();
466 load(&mut r, 10);
467 r.select(10).unwrap();
468 let pinned = load(&mut r, 99);
469 r.protect_upload(Some(99));
470 let decode = r.reserve(12, true).unwrap();
471 let read = r.reserve(11, true).unwrap();
472 load(&mut r, 13);
473 r.set_wanted(&[11, 12, 13, 14]);
474 r.park_completed(read, 11);
475 assert_eq!(r.next_request(), None);
476 assert!(!r.can_reserve(14, false));
477 assert_eq!(r.next_decode(EMPTY), Some(read));
478 r.protect_upload(None);
479 let next = r.reserve(14, false).unwrap();
480 assert_eq!(next, pinned);
481 assert_ne!(next, read);
482 assert_ne!(next, decode);
483 assert_eq!(r.next_decode(EMPTY), Some(read));
484 }
485
486 #[test]
487 fn concurrent_decode_and_read_keep_distinct_private_arenas() {
488 let mut r = Residency::new();
489 load(&mut r, 10);
490 r.select(10).unwrap();
491 load(&mut r, 11);
492 r.protect_upload(Some(11));
493 load(&mut r, 14);
494 r.set_wanted(&[14, 12, 13, 15]);
495 let decode = r.reserve(12, false).unwrap();
496 let read = r.reserve(13, false).unwrap();
497 assert_ne!(decode, read);
498 assert!(r.is_loading(12) && r.is_loading(13));
499 assert_eq!(r.reserve(12, true), None);
500 assert!(!r.can_reserve(13, true));
501 assert!(r.find(12).is_none() && r.find(13).is_none());
502 assert_eq!(r.next_request(), None);
503 assert_eq!(r.reserve(15, false), None);
504 assert!(r.can_reserve(15, true)); r.admit(decode, 12, 100);
506 assert!(r.find(12).is_some());
507 assert!(r.is_loading(13));
508 r.release(read);
509 assert!(!r.is_loading(13));
510 assert_eq!(r.next_request(), Some(13));
511 assert!(r.find(10).is_some() && r.find(11).is_some());
512 }
513
514 #[test]
515 fn cancelled_read_cannot_release_completed_decode_or_gpu_pin() {
516 let mut r = Residency::new();
517 load(&mut r, 10);
518 r.select(10).unwrap();
519 let decode = r.reserve(11, true).unwrap();
520 let read = r.reserve(12, true).unwrap();
521 r.admit(decode, 11, 100);
522 r.protect_upload(Some(11));
523 r.release(read);
524 let replacement = r.reserve(13, true).unwrap();
525 assert_ne!(replacement, decode);
526 assert!(r.find(11).is_some());
527 assert!(r.is_loading(13));
528 }
529
530 #[test]
531 fn keeps_current_and_background_upload_while_loading_two_ahead() {
532 let mut r = Residency::new();
533 load(&mut r, 10);
534 r.select(10).unwrap();
535 load(&mut r, 11);
536 assert!(r.protect_upload(Some(11)));
537 load(&mut r, 12);
538 load(&mut r, 13);
539 load(&mut r, 14);
540 load(&mut r, 15);
541 assert!(r.find(10).is_some());
542 assert!(r.find(11).is_some());
543 assert!(r.find(12).is_none());
544 assert!(r.find(13).is_some());
545 assert!(r.find(14).is_some());
546 }
547
548 #[test]
549 fn queue_deduplicates_and_skips_already_resident_neighbors() {
550 let mut r = Residency::new();
551 load(&mut r, 10);
552 r.select(10).unwrap();
553 r.set_wanted(&[10, 11, 11, 12, 13, 14]);
554 assert_eq!(r.next_request(), Some(11));
555 load(&mut r, 11);
556 assert_eq!(r.next_request(), Some(12));
557 load(&mut r, 12);
558 assert_eq!(r.next_request(), Some(13));
559 load(&mut r, 13);
560 assert_eq!(r.next_request(), Some(14));
561 load(&mut r, 14);
562 assert_eq!(r.next_request(), None);
563 }
564
565 #[test]
566 fn replaces_unwanted_newer_neighbor_before_requested_older_one() {
567 let mut r = Residency::new();
568 load(&mut r, 10);
569 r.select(10).unwrap();
570 load(&mut r, 11);
571 load(&mut r, 12);
572 load(&mut r, 13);
573 load(&mut r, 14);
574 r.set_wanted(&[11, 12, 15]);
575 load(&mut r, 15);
576 assert!(r.find(11).is_some());
577 assert!(r.find(12).is_some());
578 assert!(r.find(13).is_none());
579 }
580
581 #[test]
582 fn protected_stale_upload_cannot_make_requested_neighbors_thrash() {
583 let mut r = Residency::new();
584 load(&mut r, 10);
585 r.select(10).unwrap();
586 load(&mut r, 11);
587 r.protect_upload(Some(11));
588 r.set_wanted(&[12, 13, 14, 15]);
589 for expected in [12, 13, 14] {
590 assert_eq!(r.next_request(), Some(expected));
591 let i = r.reserve(expected, false).unwrap();
592 r.admit(i, expected, 100);
593 }
594 assert_eq!(r.next_request(), None);
595 assert!(r.reserve(15, false).is_none());
596 r.protect_upload(None);
597 assert_eq!(r.next_request(), Some(15));
598 let i = r.reserve(15, false).unwrap();
599 r.admit(i, 15, 100);
600 for region in [10, 12, 13, 14, 15] {
601 assert!(r.find(region).is_some());
602 }
603 }
604
605 #[test]
606 fn missing_protection_request_preserves_prior_pin() {
607 let mut r = Residency::new();
608 load(&mut r, 10);
609 r.select(10).unwrap();
610 load(&mut r, 11);
611 assert!(r.protect_upload(Some(11)));
612 assert!(!r.protect_upload(Some(99)));
613 for region in 12..20 {
614 load(&mut r, region);
615 }
616 assert!(r.find(11).is_some());
617 assert!(r.protect_upload(None));
618 for region in 20..24 {
619 load(&mut r, region);
620 }
621 assert!(r.find(11).is_none());
622 }
623
624 #[test]
625 fn directed_eviction_keeps_current_upload_and_requested_payloads() {
626 let mut r = Residency::new();
627 load(&mut r, 10);
628 r.select(10).unwrap();
629 load(&mut r, 11);
630 load(&mut r, 12);
631 let parked = r.reserve(13, true).unwrap();
632 r.park_completed(parked, 13);
633 r.protect_upload(Some(11));
634 assert_eq!(r.evict_unwanted(&[12]), 1);
635 assert!(r.find(10).is_some());
636 assert!(r.find(11).is_some());
637 assert!(r.find(12).is_some());
638 assert!(r.find_stored(13).is_none());
639 }
640
641 #[test]
645 fn parked_payload_is_published_only_by_admit_and_a_failed_decode_leaves_nothing() {
646 for fail in [false, true] {
647 let mut r = Residency::new();
648 let current = r.reserve(0, true).unwrap();
649 r.admit(current, 0, 92);
650 r.select(0);
651 let slot = r.reserve(1, true).unwrap();
652 r.set_wanted(&[2]);
653 r.park_completed(slot, 1);
654 assert_eq!(r.next_decode(EMPTY), None);
655 assert_eq!(r.select(1), None);
656 assert!(!r.protect_upload(Some(1)));
657 assert!(r.find(1).is_none());
658 r.set_wanted(&[1, 2]);
660 assert_eq!(r.next_decode(EMPTY), Some(slot));
661 assert!(!r.can_reserve(1, true));
662 assert_eq!(r.claim_stored(slot), 1);
663 assert!(r.find(1).is_none() && r.is_loading(1));
664 if fail {
665 r.release(slot);
666 assert!(r.find(1).is_none() && r.find_stored(1).is_none());
667 assert!(r.find(0).is_some());
668 } else {
669 r.admit(slot, 1, 92);
670 assert_eq!(r.select(1), Some(true));
671 }
672 }
673 }
674}