sva_engine/cache/
slots.rs1use std::collections::HashMap;
4use std::sync::{Mutex, MutexGuard};
5
6use sva_formula::Hash;
7use sva_samples::{FilterTrace, Label};
8
9use super::{Entry, Expected, Payload, Tier};
10
11pub const DEFAULT_SLOT_BYTES: u64 = 64 << 20;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum Put {
15 Slotted,
16 Replaced,
17 Refused,
19}
20
21struct Slotted {
22 key: Hash,
23 payload: Payload,
24 traces: Vec<FilterTrace>,
25 label: Option<Label>,
26 read: u64,
27}
28
29impl Slotted {
30 fn bytes(&self) -> u64 {
31 self.payload.bytes() as u64
32 }
33}
34
35struct State {
36 held: HashMap<Hash, Slotted>,
37 bytes: u64,
38 max_bytes: u64,
39 clock: u64,
40}
41
42impl State {
43 fn evict(&mut self, keep: Option<Hash>) {
44 while self.bytes > self.max_bytes {
45 let oldest = self
46 .held
47 .iter()
48 .filter(|(slot, _)| Some(**slot) != keep)
49 .min_by_key(|(_, held)| held.read)
50 .map(|(slot, _)| *slot);
51 let Some(slot) = oldest else { return };
52 if let Some(gone) = self.held.remove(&slot) {
53 self.bytes -= gone.bytes();
54 }
55 }
56 }
57}
58
59pub struct Slots {
62 state: Mutex<State>,
63}
64
65impl Default for Slots {
66 fn default() -> Slots {
67 Slots::holding(DEFAULT_SLOT_BYTES)
68 }
69}
70
71impl Slots {
72 pub fn holding(max_bytes: u64) -> Slots {
73 Slots {
74 state: Mutex::new(State {
75 held: HashMap::new(),
76 bytes: 0,
77 max_bytes,
78 clock: 0,
79 }),
80 }
81 }
82
83 fn locked(&self) -> MutexGuard<'_, State> {
84 self.state.lock().unwrap_or_else(|poisoned| {
85 let mut state = poisoned.into_inner();
86 state.held.clear();
87 state.bytes = 0;
88 self.state.clear_poison();
89 state
90 })
91 }
92
93 pub fn get(&self, slot: Hash, key: Hash, node: &str, expected: Expected) -> Option<Entry> {
94 let mut state = self.locked();
95 let tick = state.clock;
96 state.clock += 1;
97 let held = state.held.get_mut(&slot)?;
98 if held.key != key || !held.payload.answers(expected) {
99 return None;
100 }
101 held.read = tick;
102 Some(Entry {
103 payload: held.payload.clone(),
104 traces: super::renamed(&held.traces, node),
105 label: held.label.clone(),
106 tier: Tier::Volatile,
107 })
108 }
109
110 pub fn put(
111 &self,
112 slot: Hash,
113 key: Hash,
114 payload: &Payload,
115 traces: &[FilterTrace],
116 label: Option<&Label>,
117 ) -> Put {
118 let mut state = self.locked();
119 let bytes = payload.bytes() as u64;
120 if bytes > state.max_bytes {
121 return Put::Refused;
122 }
123 let read = state.clock;
124 state.clock += 1;
125 let before = state.held.insert(
126 slot,
127 Slotted {
128 key,
129 payload: payload.clone(),
130 traces: traces.to_vec(),
131 label: label.cloned(),
132 read,
133 },
134 );
135 state.bytes += bytes;
136 if let Some(old) = &before {
137 state.bytes -= old.bytes();
138 }
139 state.evict(Some(slot));
140 match before {
141 Some(_) => Put::Replaced,
142 None => Put::Slotted,
143 }
144 }
145
146 pub fn held_bytes(&self) -> u64 {
147 self.locked().bytes
148 }
149
150 pub fn max_bytes(&self) -> u64 {
151 self.locked().max_bytes
152 }
153
154 pub fn slots(&self) -> usize {
155 self.locked().held.len()
156 }
157
158 pub fn bound(&self, max_bytes: u64) {
160 let mut state = self.locked();
161 state.max_bytes = max_bytes;
162 state.evict(None);
163 }
164
165 pub fn clear(&self) {
166 let mut state = self.locked();
167 state.held.clear();
168 state.bytes = 0;
169 }
170}