1#[cfg(test)]
4mod chains;
5
6use std::cell::RefCell;
7use std::collections::{HashMap, HashSet};
8use std::sync::{Arc, Mutex, MutexGuard};
9
10use sva_formula::Hash;
11use sva_samples::{Buffer, Extent, Label};
12
13use super::stored::{Header, Samples};
14use super::{Entry, Expected, Payload, Stored, joined};
15
16pub const DEFAULT_CACHE_BYTES: u64 = 2 << 30;
17
18pub const DEFAULT_MARK_EVERY: usize = 16_384;
20
21#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
23pub struct Counters {
24 pub disk_lookups: u64,
25 pub disk_reads: u64,
26 pub disk_read_bytes: u64,
27 pub promotions: u64,
29 pub writebacks: u64,
30 pub hits: u64,
32 pub probation_evictions: u64,
33 pub protected_evictions: u64,
34}
35
36impl Counters {
37 pub fn since(self, then: Counters) -> Counters {
38 Counters {
39 disk_lookups: self.disk_lookups - then.disk_lookups,
40 disk_reads: self.disk_reads - then.disk_reads,
41 disk_read_bytes: self.disk_read_bytes - then.disk_read_bytes,
42 promotions: self.promotions - then.promotions,
43 writebacks: self.writebacks - then.writebacks,
44 hits: self.hits - then.hits,
45 probation_evictions: self.probation_evictions - then.probation_evictions,
46 protected_evictions: self.protected_evictions - then.protected_evictions,
47 }
48 }
49
50 pub fn evictions(self) -> u64 {
51 self.probation_evictions + self.protected_evictions
52 }
53}
54
55pub const BYTES_PER_FLOP: u128 = 64;
57
58#[derive(Clone, Copy, Debug)]
60pub(crate) struct Facts {
61 pub(crate) slot: Option<Hash>,
62 pub(crate) settled: bool,
63 pub(crate) target: bool,
64 pub(crate) samples: u64,
65}
66
67const PROTECTED: (u64, u64) = (3, 4);
70
71#[derive(Clone, Copy, Debug)]
72pub(crate) struct Stamp {
73 pub slot: Option<Hash>,
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub(crate) enum Kept {
79 Held,
80 Replaced,
81 Refused,
82}
83
84pub(crate) enum Known {
87 Hit(Arc<Stored>),
88 Miss,
89 Unknown,
90}
91
92#[derive(Clone, Debug, PartialEq)]
95pub(crate) enum Offered {
96 Values {
97 keys: Vec<(i64, Hash)>,
98 by: i64,
99 },
100 Moves {
101 of: Hash,
102 by: i64,
103 },
104 Held(Vec<Arc<Buffer>>),
106}
107
108pub(crate) struct Writeback {
109 pub(crate) key: Hash,
110 pub(crate) head: Header,
111 pub(crate) parts: Vec<Arc<Buffer>>,
112}
113
114enum Item {
115 Value {
116 payload: Payload,
117 label: Option<Label>,
118 slot: Option<Hash>,
119 },
120 Node {
121 source: Source,
122 slot: Option<Hash>,
123 dirty: bool,
125 settled: bool,
126 bound: bool,
127 },
128}
129
130enum Source {
131 Disk {
132 head: Box<Header>,
133 chunks: Vec<Arc<Buffer>>,
134 },
135 Offered {
136 stored: Box<Stored>,
137 offered: Offered,
138 },
139}
140
141impl Source {
142 fn stored(&self) -> &Stored {
143 match self {
144 Source::Disk { head, .. } => head.stored(),
145 Source::Offered { stored, .. } => stored,
146 }
147 }
148}
149
150struct Held {
151 item: Item,
152 read: u64,
153 since: u64,
154 hit_round: u64,
155 protected: bool,
156}
157
158impl Held {
159 fn admitted(item: Item, at: u64) -> Held {
160 Held {
161 item,
162 read: at,
163 since: at,
164 hit_round: 0,
165 protected: false,
166 }
167 }
168}
169
170fn planes(b: &Buffer) -> u64 {
171 (b.len() * b.width * size_of::<f64>()) as u64
172}
173
174impl Held {
175 fn bytes(&self) -> u64 {
176 match &self.item {
177 Item::Value { payload, .. } => payload.bytes() as u64,
178 Item::Node {
179 source:
180 Source::Disk { chunks, .. }
181 | Source::Offered {
182 offered: Offered::Held(chunks),
183 ..
184 },
185 ..
186 } => chunks.iter().map(|c| planes(c)).sum(),
187 Item::Node { .. } => 0,
188 }
189 }
190
191 fn slot(&self) -> Option<Hash> {
192 match &self.item {
193 Item::Value { slot, .. } | Item::Node { slot, .. } => *slot,
194 }
195 }
196}
197
198#[derive(Default)]
199struct State {
200 entries: HashMap<Hash, Held>,
201 slots: HashMap<Hash, Hash>,
202 misses: HashMap<Hash, u64>,
203 pending: Vec<Writeback>,
204 bytes: u64,
205 max_bytes: u64,
206 clock: u64,
207 round: u64,
208 mark_every: usize,
209 counters: Counters,
210 disk: bool,
211 failed: (u64, Option<String>),
213 links: RefCell<Links>,
214 #[cfg(test)]
215 walked: std::cell::Cell<u64>,
216}
217
218#[derive(Clone, Copy)]
220struct Link {
221 end: Hash,
222 by: i64,
223 unbound: bool,
224 looped: bool,
225}
226
227#[derive(Default)]
229struct Links {
230 found: HashMap<Hash, (Link, Hash)>,
232 through: HashMap<Hash, HashSet<Hash>>,
233}
234
235impl Links {
236 fn keep(&mut self, at: Hash, next: Hash, link: Link) {
237 self.found.insert(at, (link, next));
238 self.through.entry(next).or_default().insert(at);
239 }
240
241 fn evict(&mut self, key: Hash) {
242 let mut open = vec![key];
243 while let Some(at) = open.pop() {
244 if let Some((_, next)) = self.found.remove(&at)
245 && let Some(readers) = self.through.get_mut(&next)
246 {
247 readers.remove(&at);
248 if readers.is_empty() {
249 self.through.remove(&next);
250 }
251 }
252 open.extend(self.through.remove(&at).into_iter().flatten());
253 }
254 }
255}
256
257impl State {
258 fn tick(&mut self) -> u64 {
259 self.clock += 1;
260 self.clock
261 }
262
263 fn stands(&self, key: Hash) -> bool {
264 let bound = |at: &Hash| {
265 let held = self.entries.get(at).map(|held| &held.item);
266 matches!(held, Some(Item::Node { bound: true, .. }))
267 };
268 self.doomed(key).iter().skip(1).any(bound)
269 }
270
271 fn unsettled(&self, key: Hash) -> bool {
272 matches!(
273 self.entries.get(&key),
274 Some(Held {
275 item: Item::Node { settled: false, .. },
276 ..
277 })
278 )
279 }
280
281 fn doomed(&self, key: Hash) -> Vec<Hash> {
283 let mut out = vec![key];
284 let mut k = 0;
285 while k < out.len() {
286 let gone = out[k];
287 for (at, held) in &self.entries {
288 let Item::Node {
289 source: Source::Offered { offered, .. },
290 ..
291 } = &held.item
292 else {
293 continue;
294 };
295 let reads = match offered {
296 Offered::Values { keys, .. } => keys.iter().any(|(_, key)| *key == gone),
297 Offered::Moves { of, .. } => *of == gone,
298 Offered::Held(_) => false,
299 };
300 if reads && !out.contains(at) {
301 out.push(*at);
302 }
303 }
304 k += 1;
305 }
306 out
307 }
308
309 fn remove(&mut self, key: Hash) -> bool {
312 if !self.entries.contains_key(&key) {
313 return false;
314 }
315 let doomed = self.doomed(key);
316 for at in &doomed {
317 self.flush(*at);
318 }
319 for at in doomed {
320 if at == key || !self.unsettled(at) {
321 self.discard(at);
322 }
323 }
324 true
325 }
326
327 fn discard(&mut self, key: Hash) {
328 let Some(gone) = self.entries.remove(&key) else {
329 return;
330 };
331 self.links.get_mut().evict(key);
332 self.bytes -= gone.bytes();
333 let value = matches!(gone.item, Item::Value { .. });
334 if let Some(slot) = gone.slot().filter(|_| value)
335 && self.slots.get(&slot) == Some(&key)
336 {
337 self.slots.remove(&slot);
338 }
339 }
340
341 fn flush(&mut self, key: Hash) {
344 let Some(Held {
345 item:
346 Item::Node {
347 dirty: dirty @ true,
348 settled,
349 ..
350 },
351 ..
352 }) = self.entries.get_mut(&key)
353 else {
354 return;
355 };
356 *dirty = !*settled;
357 if let Some(back) = self.written(key) {
358 self.pending.push(back);
359 }
360 }
361
362 fn written(&self, key: Hash) -> Option<Writeback> {
363 let Item::Node {
364 source: Source::Offered { stored, offered },
365 ..
366 } = &self.entries.get(&key)?.item
367 else {
368 return None;
369 };
370 let head = |samples| Header::new((**stored).clone(), samples);
371 let referred = match offered {
372 Offered::Moves { of, by } => match self.referred(*of, *by) {
373 Some(found) => Some(found),
374 None if self.unbound(*of) => None,
375 None => return None,
376 },
377 _ => None,
378 };
379 Some(match referred {
380 Some((of, by)) => Writeback {
381 key,
382 head: head(Samples::Of { key: of, by }),
383 parts: Vec::new(),
384 },
385 None => Writeback {
386 key,
387 head: head(Samples::None),
388 parts: self
389 .offered_parts(offered, Extent::EVERYWHERE)
390 .unwrap_or_default(),
391 },
392 })
393 }
394
395 fn moving(&self, key: Hash) -> Option<(Hash, i64, bool)> {
396 match &self.entries.get(&key)?.item {
397 Item::Node {
398 source:
399 Source::Offered {
400 offered: Offered::Moves { of, by },
401 ..
402 },
403 bound,
404 ..
405 } => Some((*of, *by, *bound)),
406 _ => None,
407 }
408 }
409
410 fn link(&self, key: Hash) -> Link {
412 let (mut walked, mut seen, mut at) = (Vec::new(), HashSet::new(), key);
413 let mut link = loop {
414 if let Some((held, _)) = self.links.borrow().found.get(&at) {
415 break *held;
416 }
417 let looped = !seen.insert(at);
418 match self.moving(at) {
419 Some((of, by, bound)) if !looped => {
420 #[cfg(test)]
421 self.walked.set(self.walked.get() + 1);
422 walked.push((at, of, by, !bound));
423 at = of;
424 }
425 moving => {
426 break Link {
427 end: at,
428 by: 0,
429 unbound: false,
430 looped: moving.is_some(),
431 };
432 }
433 }
434 };
435 for (at, of, by, unbound) in walked.into_iter().rev() {
436 link.by += by;
437 link.unbound |= unbound;
438 if !link.looped {
439 self.links.borrow_mut().keep(at, of, link);
440 }
441 }
442 link
443 }
444
445 fn admit(&mut self, key: Hash, held: Held) -> Option<Held> {
446 self.links.get_mut().evict(key);
447 self.entries.insert(key, held)
448 }
449
450 fn foot(&self, key: Hash) -> Option<(Hash, i64)> {
451 let link = self.link(key);
452 (!link.looped).then_some((link.end, link.by))
453 }
454
455 fn unbound(&self, key: Hash) -> bool {
456 let link = self.link(key);
457 let end = self.entries.get(&link.end).map(|held| &held.item);
458 link.unbound || matches!(end, Some(Item::Node { bound: false, .. }))
459 }
460
461 fn referred(&self, of: Hash, by: i64) -> Option<(Hash, i64)> {
462 let link = self.link(of);
463 if link.looped || link.unbound {
464 return None;
465 }
466 let more = link.by;
467 match &self.entries.get(&link.end)?.item {
468 Item::Node {
469 source: Source::Disk { head, .. },
470 ..
471 } => match head.samples() {
472 Samples::Entry { file, shift, .. } => Some((*file, by + more - shift)),
473 _ => None,
474 },
475 Item::Node { bound: false, .. } | Item::Value { .. } => None,
476 Item::Node { .. } => Some((link.end, by + more)),
477 }
478 }
479
480 fn shares(&self, keys: &[(i64, Hash)]) -> Option<Vec<(Arc<Buffer>, Extent)>> {
482 let mut out = Vec::new();
483 for (k, (start, key)) in keys.iter().enumerate() {
484 let end = keys.get(k + 1).map_or(i64::MAX, |(next, _)| *next);
485 let within = Extent::new(*start, end);
486 let parts = match &self.entries.get(key).map(|held| &held.item) {
487 Some(Item::Value {
488 payload: Payload::Segments(parts),
489 ..
490 }) => parts.clone(),
491 Some(Item::Value {
492 payload: Payload::Run(run),
493 ..
494 }) => vec![Arc::clone(&run.samples)],
495 _ => Vec::new(),
496 };
497 let met = |part: Arc<Buffer>| {
498 let met = part.extent().intersect(within);
499 (!met.is_empty()).then_some((part, met))
500 };
501 out.extend(parts.into_iter().filter_map(met));
502 }
503 let held = keys.iter().any(|(_, key)| self.entries.contains_key(key));
504 held.then_some(out)
505 }
506
507 fn offered_parts(&self, offered: &Offered, over: Extent) -> Option<Vec<Arc<Buffer>>> {
508 let (parts, by): (Vec<Arc<Buffer>>, i64) = match offered {
509 Offered::Values { keys, by } => {
510 let within = over.shifted(*by);
511 let met = self.shares(keys)?.into_iter();
512 let met = met.filter(|(_, held)| !held.intersect(within).is_empty());
513 (met.map(|(part, held)| clipped(&part, held)).collect(), *by)
514 }
515 Offered::Moves { of, by } => {
516 let (foot, more) = self.foot(*of)?;
517 (self.resident(foot, over.shifted(by + more))?.0, by + more)
518 }
519 Offered::Held(parts) => (parts.clone(), 0),
520 };
521 let over = over.shifted(by);
522 let meets = |b: &&Arc<Buffer>| !b.extent().intersect(over).is_empty();
523 Some(parts.iter().filter(meets).map(|p| moved(p, -by)).collect())
524 }
525
526 fn resident(&self, key: Hash, over: Extent) -> Option<Resident> {
529 let Item::Node { source, .. } = &self.entries.get(&key)?.item else {
530 return None;
531 };
532 match source {
533 Source::Offered { offered, .. } => Some((self.offered_parts(offered, over)?, None)),
534 Source::Disk { head, chunks } => {
535 let meets = |b: &&Arc<Buffer>| !b.extent().intersect(over).is_empty();
536 let parts: Vec<Arc<Buffer>> = chunks.iter().filter(meets).cloned().collect();
537 let mut lacks = Vec::new();
538 for held in head.stored().extents() {
539 let mut asked = vec![held.intersect(over)];
540 for part in chunks {
541 asked = asked
542 .into_iter()
543 .flat_map(|a| minus(a, part.extent()))
544 .collect();
545 }
546 lacks.extend(asked.into_iter().filter(|a| !a.is_empty()));
547 }
548 let hull = lacks.iter().fold(Extent::NOWHERE, |h, e| h.hull(*e));
549 Some((parts, (!hull.is_empty()).then(|| ((**head).clone(), hull))))
550 }
551 }
552 }
553
554 fn coverage(&self, key: Hash) -> Option<Vec<Extent>> {
555 let (foot, by) = self.foot(key)?;
556 let Item::Node { source, .. } = &self.entries.get(&foot)?.item else {
557 return None;
558 };
559 let held = match source {
560 Source::Disk { head, .. } => head.stored().extents().to_vec(),
561 Source::Offered {
562 offered: Offered::Values { keys, by },
563 ..
564 } => {
565 let shares = self.shares(keys)?.into_iter();
566 shares.map(|(_, held)| held.shifted(-by)).collect()
567 }
568 Source::Offered {
569 offered: Offered::Held(parts),
570 ..
571 } => parts.iter().map(|p| p.extent()).collect(),
572 Source::Offered {
573 offered: Offered::Moves { .. },
574 ..
575 } => return None,
576 };
577 Some(held.into_iter().map(|e| e.shifted(-by)).collect())
578 }
579
580 fn evict(&mut self, key: Hash) {
581 let Some(held) = self.entries.get_mut(&key) else {
582 return;
583 };
584 let protected = held.protected;
585 let gone = match &mut held.item {
586 Item::Node {
587 source: Source::Disk { chunks, .. },
588 ..
589 } => {
590 let freed: u64 = std::mem::take(chunks).iter().map(|c| planes(c)).sum();
591 self.bytes -= freed;
592 true
593 }
594 _ => self.remove(key),
595 };
596 match (gone, protected) {
597 (false, _) => {}
598 (true, false) => self.counters.probation_evictions += 1,
599 (true, true) => self.counters.protected_evictions += 1,
600 }
601 }
602
603 fn hit(&mut self, key: Hash, round: Option<u64>) {
606 let tick = self.tick();
607 let mut promoted = false;
608 for at in self.under(key) {
609 let Some(held) = self.entries.get_mut(&at) else {
610 continue;
611 };
612 held.read = tick;
613 if round.is_some_and(|round| held.hit_round == round) {
614 continue;
615 }
616 held.hit_round = round.unwrap_or(0);
617 promoted |= !std::mem::replace(&mut held.protected, true);
618 self.counters.hits += 1;
619 }
620 if promoted {
621 self.shared();
622 }
623 }
624
625 fn under(&self, key: Hash) -> Vec<Hash> {
626 let mut out = vec![key];
627 let mut k = 0;
628 while k < out.len() {
629 if let Some(Item::Node {
630 source: Source::Offered { offered, .. },
631 ..
632 }) = self.entries.get(&out[k]).map(|held| &held.item)
633 {
634 let more: Vec<Hash> = match offered {
635 Offered::Values { keys, .. } => keys.iter().map(|(_, key)| *key).collect(),
636 Offered::Moves { of, .. } => vec![*of],
637 Offered::Held(_) => Vec::new(),
638 };
639 for at in more {
640 if !out.contains(&at) {
641 out.push(at);
642 }
643 }
644 }
645 k += 1;
646 }
647 out
648 }
649
650 fn shared(&mut self) {
652 let most =
653 (u128::from(self.max_bytes) * u128::from(PROTECTED.0) / u128::from(PROTECTED.1)) as u64;
654 let mut protected: Vec<(u64, Hash, u64)> = self
655 .entries
656 .iter()
657 .filter(|(_, held)| held.protected)
658 .map(|(key, held)| (held.read, *key, held.bytes()))
659 .collect();
660 let mut bytes: u64 = protected.iter().map(|(_, _, b)| b).sum();
661 protected.sort_unstable();
662 for (_, key, held) in protected {
663 if bytes <= most {
664 break;
665 }
666 let tick = self.tick();
667 if let Some(entry) = self.entries.get_mut(&key) {
668 entry.protected = false;
669 entry.since = tick;
670 bytes -= held;
671 }
672 }
673 }
674
675 fn bounded(&mut self) {
678 if self.bytes <= self.max_bytes {
679 return;
680 }
681 self.shared();
682 let max = self.max_bytes;
683 let mut order: Vec<(u8, u64, Hash)> = self
684 .entries
685 .iter()
686 .filter(|(_, held)| held.bytes() > 0)
687 .map(|(key, held)| match (held.bytes() > max, held.protected) {
688 (true, _) => (0, held.since, *key),
689 (false, false) => (1, held.since, *key),
690 (false, true) => (2, held.read, *key),
691 })
692 .collect();
693 order.sort_unstable();
694 for (_, _, key) in order {
695 if self.bytes <= self.max_bytes {
696 break;
697 }
698 self.evict(key);
699 }
700 }
701}
702
703type Resident = (Vec<Arc<Buffer>>, Option<(Header, Extent)>);
704
705fn minus(e: Extent, cut: Extent) -> Vec<Extent> {
706 let met = e.intersect(cut);
707 if met.is_empty() {
708 return vec![e];
709 }
710 vec![Extent::new(e.start, met.start), Extent::new(met.end, e.end)]
711}
712
713fn clipped(part: &Arc<Buffer>, met: Extent) -> Arc<Buffer> {
714 let held = part.extent();
715 match met == held {
716 true => Arc::clone(part),
717 false => Arc::new(part.over(met, held)),
718 }
719}
720
721fn moved(part: &Arc<Buffer>, by: i64) -> Arc<Buffer> {
722 if by == 0 {
723 return Arc::clone(part);
724 }
725 let mut out = (**part).clone();
726 out.start += by;
727 Arc::new(out)
728}
729
730#[derive(Clone)]
733pub(crate) struct Memory {
734 state: Arc<Mutex<State>>,
735}
736
737impl Default for Memory {
738 fn default() -> Memory {
739 Memory::holding(DEFAULT_CACHE_BYTES)
740 }
741}
742
743impl Memory {
744 pub(crate) fn holding(max_bytes: u64) -> Memory {
745 Memory {
746 state: Arc::new(Mutex::new(State {
747 max_bytes,
748 mark_every: DEFAULT_MARK_EVERY,
749 ..State::default()
750 })),
751 }
752 }
753
754 pub(crate) fn over_disk(max_bytes: u64) -> Memory {
756 let memory = Memory::holding(max_bytes);
757 memory.locked().disk = true;
758 memory
759 }
760
761 fn locked(&self) -> MutexGuard<'_, State> {
762 self.state.lock().unwrap_or_else(|poisoned| {
763 let mut state = poisoned.into_inner();
764 state.entries.clear();
765 *state.links.get_mut() = Links::default();
766 state.slots.clear();
767 state.bytes = 0;
768 self.state.clear_poison();
769 state
770 })
771 }
772
773 pub(crate) fn max_bytes(&self) -> u64 {
774 self.locked().max_bytes
775 }
776
777 pub(crate) fn set_max_bytes(&self, max_bytes: u64) {
778 let mut state = self.locked();
779 state.max_bytes = max_bytes;
780 state.bounded();
781 }
782
783 pub(crate) fn bytes(&self) -> u64 {
784 self.locked().bytes
785 }
786
787 pub(crate) fn entries(&self) -> usize {
788 self.locked().entries.len()
789 }
790
791 pub(crate) fn holds(&self, key: Hash) -> bool {
792 self.locked().entries.contains_key(&key)
793 }
794
795 pub(crate) fn counters(&self) -> Counters {
796 self.locked().counters
797 }
798
799 pub(crate) fn count(&self, by: impl FnOnce(&mut Counters)) {
800 by(&mut self.locked().counters);
801 }
802
803 pub(crate) fn mark_every(&self) -> usize {
804 self.locked().mark_every
805 }
806
807 pub(crate) fn set_mark_every(&self, samples: usize) {
808 self.locked().mark_every = samples.max(1);
809 }
810
811 pub(crate) fn keeps(&self) -> bool {
813 self.locked().max_bytes > 0
814 }
815
816 pub(crate) fn begin(&self) -> u64 {
818 let mut state = self.locked();
819 state.round += 1;
820 let round = state.round;
821 state.misses.retain(|_, met| *met + 1 >= round);
822 round
823 }
824
825 pub(crate) fn answer(&self, key: Hash, round: u64) -> Known {
826 let mut state = self.locked();
827 let covered = state.coverage(key);
828 if let Some(held) = covered.clone().filter(|held| !held.is_empty()) {
829 state.hit(key, Some(round));
830 let Some(Item::Node { source, .. }) = state.entries.get(&key).map(|held| &held.item)
831 else {
832 unreachable!("only a node covers");
833 };
834 return Known::Hit(Arc::new(source.stored().holding(held)));
835 }
836 let node = matches!(
837 state.entries.get(&key),
838 Some(Held {
839 item: Item::Node { .. },
840 ..
841 })
842 );
843 match (node, covered) {
844 (true, None) => {
845 state.remove(key);
846 }
847 (true, Some(_)) => return Known::Miss,
848 (false, _) => {}
849 }
850 match state.misses.get(&key) {
851 Some(met) if *met >= round => Known::Miss,
852 _ => Known::Unknown,
853 }
854 }
855
856 pub(crate) fn miss(&self, key: Hash, round: u64) {
857 let mut state = self.locked();
858 let met = state.misses.entry(key).or_insert(round);
859 *met = (*met).max(round);
860 }
861
862 pub(crate) fn promote(&self, head: Header) {
864 let mut state = self.locked();
865 let (key, read) = (head.stored().key, state.tick());
866 if state.entries.contains_key(&key) {
867 return;
868 }
869 state.misses.remove(&key);
870 state.counters.promotions += 1;
871 let item = Item::Node {
872 source: Source::Disk {
873 head: Box::new(head),
874 chunks: Vec::new(),
875 },
876 slot: None,
877 dirty: false,
878 settled: true,
879 bound: true,
880 };
881 state.admit(key, Held::admitted(item, read));
882 }
883
884 pub(crate) fn promote_samples(&self, key: Hash, read: Vec<Buffer>) -> Vec<Arc<Buffer>> {
885 let read: Vec<Arc<Buffer>> = read.into_iter().map(Arc::new).collect();
886 let mut state = self.locked();
887 let tick = state.tick();
888 let Some(held) = state.entries.get_mut(&key) else {
889 return read;
890 };
891 let Item::Node {
892 source: Source::Disk { chunks, .. },
893 ..
894 } = &mut held.item
895 else {
896 return read;
897 };
898 let mut added = 0;
899 for part in &read {
900 let covered = chunks
901 .iter()
902 .any(|c| c.extent().intersect(part.extent()) == part.extent());
903 if !covered {
904 added += planes(part);
905 chunks.push(Arc::clone(part));
906 }
907 }
908 chunks.sort_by_key(|c| c.start);
909 held.read = tick;
910 state.bytes += added;
911 state.counters.promotions += 1;
912 state.bounded();
913 read
914 }
915
916 pub(crate) fn resident(&self, key: Hash, over: Extent) -> Resident {
918 let mut state = self.locked();
919 let tick = state.tick();
920 if let Some(held) = state.entries.get_mut(&key) {
921 held.read = tick;
922 }
923 state.resident(key, over).unwrap_or_default()
924 }
925
926 pub(crate) fn forget(&self, key: Hash) {
927 self.locked().remove(key);
928 }
929
930 pub(crate) fn offer(&self, stored: Stored, offered: Offered, facts: Facts) {
935 let mut state = self.locked();
936 let Facts { slot, settled, .. } = facts;
937 let bound = state.disk && writes(&stored, facts);
938 let key = stored.key;
939 let read = state.tick();
940 let earned = state.entries.get(&key);
941 let (since, protected) = earned.map_or((read, false), |held| (held.since, held.protected));
942 state.discard(key);
943 let slot = slot.map(|slot| super::mixed(slot, &[0x6e_6f_64_65]));
944 let item = Item::Node {
945 source: Source::Offered {
946 stored: Box::new(stored),
947 offered,
948 },
949 slot,
950 dirty: bound,
951 settled,
952 bound,
953 };
954 let held = Held {
955 since,
956 protected,
957 ..Held::admitted(item, read)
958 };
959 state.bytes += held.bytes();
960 state.admit(key, held);
961 state.misses.remove(&key);
962 let covered = state.coverage(key).is_some_and(|held| !held.is_empty());
963 if settled && !covered && !bound {
964 state.discard(key);
965 return;
966 }
967 if let Some(last) = slot.and_then(|slot| state.slots.insert(slot, key))
968 && last != key
969 {
970 state.remove(last);
971 }
972 state.bounded();
973 }
974
975 pub(crate) fn flush(&self) {
977 let mut state = self.locked();
978 let keys: Vec<Hash> = state.entries.keys().copied().collect();
979 for key in keys {
980 state.flush(key);
981 }
982 state.failed.1 = None;
983 }
984
985 pub(crate) fn pending(&self) -> Vec<Writeback> {
987 let mut state = self.locked();
988 match state.failed.1 {
989 Some(_) => Vec::new(),
990 None => std::mem::take(&mut state.pending),
991 }
992 }
993
994 pub(crate) fn failed(&self, why: String, left: Vec<Writeback>) {
996 let mut state = self.locked();
997 state.failed.0 += 1;
998 state.failed.1 = Some(why);
999 let mut more = std::mem::take(&mut state.pending);
1000 state.pending = left;
1001 state.pending.append(&mut more);
1002 }
1003
1004 pub(crate) fn written(&self) {
1005 self.locked().counters.writebacks += 1;
1006 }
1007
1008 pub(crate) fn failures(&self) -> (u64, Option<String>) {
1009 self.locked().failed.clone()
1010 }
1011
1012 pub(crate) fn blocked(&self) -> bool {
1013 self.locked().failed.1.is_some()
1014 }
1015
1016 pub(crate) fn committed(&self) {
1019 let mut state = self.locked();
1020 let staged: Vec<Hash> = state
1021 .entries
1022 .iter()
1023 .filter(|(_, held)| {
1024 let Item::Node {
1025 source: Source::Disk { head, .. },
1026 ..
1027 } = &held.item
1028 else {
1029 return false;
1030 };
1031 matches!(head.samples(), Samples::Staged { .. })
1032 })
1033 .map(|(key, _)| *key)
1034 .collect();
1035 for key in staged {
1036 state.discard(key);
1037 }
1038 }
1039
1040 pub(crate) fn load(&self, key: Hash, expected: Expected) -> Option<Entry> {
1042 let mut state = self.locked();
1043 let held = state.entries.get_mut(&key)?;
1044 let Item::Value { payload, label, .. } = &held.item else {
1045 return None;
1046 };
1047 if !payload.answers(expected) {
1048 state.remove(key);
1049 return None;
1050 }
1051 let entry = Entry {
1052 payload: payload.clone(),
1053 label: label.clone(),
1054 };
1055 state.hit(key, None);
1056 Some(entry)
1057 }
1058
1059 pub(crate) fn merge(
1062 &self,
1063 key: Hash,
1064 payload: Payload,
1065 label: Option<&Label>,
1066 stamp: Stamp,
1067 ) -> Kept {
1068 let mut state = self.locked();
1069 let tick = state.tick();
1070 let joined = match (state.entries.get_mut(&key), payload) {
1071 (Some(held), payload) if held.slot() == stamp.slot => {
1072 let before = held.bytes();
1073 let Item::Value { payload: had, .. } = &mut held.item else {
1074 unreachable!("a value key holds a value");
1075 };
1076 let payload = match (had, payload) {
1077 (Payload::Segments(parts), Payload::Segments(more)) => {
1078 joined(parts, more);
1079 None
1080 }
1081 (Payload::Run(run), Payload::Run(more)) if overlaps(run, &more) => {
1082 let from = (run.end() - more.samples.start).max(0) as usize;
1083 let run = Arc::make_mut(run);
1084 let samples = Arc::make_mut(&mut run.samples);
1085 for (held, more) in samples.planes.iter_mut().zip(&more.samples.planes) {
1086 held.extend_from_slice(&more[from.min(more.len())..]);
1087 }
1088 run.marks
1089 .extend(more.marks.iter().map(|(at, m)| (*at, m.clone())));
1090 None
1091 }
1092 (_, payload) => Some(payload),
1093 };
1094 match payload {
1095 None => {
1096 held.read = tick;
1097 let after = held.bytes();
1098 Ok((before, after))
1099 }
1100 Some(payload) => Err(payload),
1101 }
1102 }
1103 (_, payload) => Err(payload),
1104 };
1105 match joined {
1106 Ok((before, after)) => {
1107 state.bytes = state.bytes - before + after;
1108 state.bounded();
1109 Kept::Held
1110 }
1111 Err(payload) => {
1112 drop(state);
1113 self.store(key, payload, label, stamp)
1114 }
1115 }
1116 }
1117
1118 pub(crate) fn store(
1121 &self,
1122 key: Hash,
1123 payload: Payload,
1124 label: Option<&Label>,
1125 stamp: Stamp,
1126 ) -> Kept {
1127 let mut state = self.locked();
1128 let bytes = payload.bytes() as u64;
1129 if bytes > state.max_bytes && !state.stands(key) {
1130 return Kept::Refused;
1131 }
1132 let read = state.tick();
1133 let replaced = match stamp.slot.and_then(|slot| state.slots.insert(slot, key)) {
1134 Some(last) if last != key => state.remove(last),
1135 _ => false,
1136 };
1137 let item = Item::Value {
1138 payload,
1139 label: label.cloned(),
1140 slot: stamp.slot,
1141 };
1142 let held = Held::admitted(item, read);
1143 if let Some(old) = state.admit(key, held) {
1144 state.bytes -= old.bytes();
1145 }
1146 state.bytes += bytes;
1147 state.bounded();
1148 match replaced {
1149 true => Kept::Replaced,
1150 false => Kept::Held,
1151 }
1152 }
1153}
1154
1155fn writes(stored: &Stored, facts: Facts) -> bool {
1157 let bytes = u128::from(facts.samples) * u128::from(stored.width) * size_of::<f64>() as u128;
1158 (stored.readable || facts.target) && stored.priced * BYTES_PER_FLOP >= bytes
1159}
1160
1161fn overlaps(held: &super::Run, more: &super::Run) -> bool {
1164 let (a, b) = (held.samples.start, held.end());
1165 a <= more.samples.start && more.samples.start <= b
1166}
1167
1168#[cfg(test)]
1169mod tests {
1170 use super::*;
1171
1172 #[test]
1174 fn an_entry_that_does_not_answer_what_was_asked_is_a_miss_and_goes() {
1175 let memory = Memory::default();
1176 let key = Hash(7, 11);
1177 let stamp = Stamp { slot: None };
1178 let four = Payload::Segments(vec![Arc::new(Buffer::mono(8_000, vec![0.25; 4]))]);
1179 for (rate, width) in [(48_000, 1), (8_000, 2)] {
1180 memory.store(key, four.clone(), None, stamp);
1181 let asked = Expected::Segments { rate, width };
1182 assert!(memory.load(key, asked).is_none());
1183 assert!(!memory.holds(key));
1184 assert_eq!(memory.bytes(), 0);
1185 }
1186 }
1187
1188 #[test]
1189 fn a_load_shares_the_samples_it_holds() {
1190 let memory = Memory::default();
1191 let key = Hash(3, 5);
1192 let stamp = Stamp { slot: None };
1193 let part = Arc::new(Buffer::mono(8_000, vec![0.5; 64]));
1194 memory.store(key, Payload::Segments(vec![Arc::clone(&part)]), None, stamp);
1195 let asked = Expected::Segments {
1196 rate: 8_000,
1197 width: 1,
1198 };
1199 for _ in 0..2 {
1200 let loaded = memory.load(key, asked).expect("a hit");
1201 let Payload::Segments(parts) = loaded.payload else {
1202 panic!("segments were stored");
1203 };
1204 assert!(Arc::ptr_eq(&parts[0], &part), "the stored part itself");
1205 }
1206 }
1207}