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