1use std::collections::BTreeSet;
2use std::sync::Arc;
3use std::sync::atomic::{AtomicU32, Ordering};
4
5use crate::accumulate::{ACCUMULATED_COMPUTATION_ID, Accumulate, Accumulated};
6use crate::cell::CellData;
7use crate::storage::StorageFor;
8use crate::{Cell, Computation, Storage};
9
10pub mod debug_with_db;
11mod handle;
12mod serialize;
13mod tests;
14
15pub use handle::DbHandle;
16use parking_lot::Mutex;
17use rustc_hash::FxHashSet;
18
19const START_VERSION: u32 = 1;
20
21pub struct Db<Storage> {
26 cells: dashmap::DashMap<Cell, CellData, rustc_hash::FxBuildHasher>,
27 version: AtomicU32,
28 next_cell: AtomicU32,
29 storage: Storage,
30
31 cell_locks: dashmap::DashMap<u32, Arc<Mutex<()>>, rustc_hash::FxBuildHasher>,
34}
35
36impl<Storage: Default> Db<Storage> {
37 pub fn new() -> Self {
39 Self::with_storage(Storage::default())
40 }
41}
42
43impl<S: Default> Default for Db<S> {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49pub trait DbGet<C: Computation> {
52 fn get(&self, key: C) -> C::Output;
55}
56
57impl<S, C> DbGet<C> for Db<S>
58where
59 C: Computation,
60 S: Storage + StorageFor<C>,
61{
62 fn get(&self, key: C) -> C::Output {
63 self.get(key)
64 }
65}
66
67impl<S> Db<S> {
68 pub fn with_storage(storage: S) -> Self {
70 Self {
71 cells: Default::default(),
72 version: AtomicU32::new(START_VERSION),
73 next_cell: AtomicU32::new(0),
74 cell_locks: Default::default(),
75 storage,
76 }
77 }
78
79 pub fn storage(&self) -> &S {
81 &self.storage
82 }
83
84 pub fn storage_mut(&mut self) -> &mut S {
89 &mut self.storage
90 }
91}
92
93impl<S: Storage> Db<S> {
94 fn get_cell<C: Computation>(&self, computation: &C) -> Option<Cell>
98 where
99 S: StorageFor<C>,
100 {
101 self.storage.get_cell_for_computation(computation)
102 }
103
104 pub(crate) fn get_or_insert_cell<C>(&self, input: C) -> Cell
105 where
106 C: Computation,
107 S: StorageFor<C>,
108 {
109 if let Some(cell) = self.get_cell(&input) {
110 return cell;
111 }
112
113 let computation_id = C::computation_id();
115 let lock = self.cell_locks.entry(computation_id).or_default().clone();
116 let _guard = lock.lock();
117
118 if let Some(cell) = self.get_cell(&input) {
121 cell
122 } else {
123 let cell_id = self.next_cell.fetch_add(1, Ordering::Relaxed);
126 let new_cell = Cell::new(cell_id);
127
128 self.cells.insert(new_cell, CellData::new(computation_id));
129 self.storage.insert_new_cell(new_cell, input);
130 new_cell
131 }
132 }
133
134 fn handle(&self, cell: Cell) -> DbHandle<'_, S> {
135 DbHandle::new(self, cell)
136 }
137
138 #[cfg(test)]
139 #[allow(unused)]
140 pub(crate) fn with_cell_data<C: Computation>(&self, input: &C, f: impl FnOnce(&CellData))
141 where
142 S: StorageFor<C>,
143 {
144 let cell = self
145 .get_cell(input)
146 .unwrap_or_else(|| panic!("unwrap_cell_value: Expected cell to exist"));
147
148 self.cells.get(&cell).map(|value| f(&value)).unwrap()
149 }
150
151 pub fn version(&self) -> u32 {
152 self.version.load(Ordering::SeqCst)
153 }
154
155 pub fn gc(&mut self, version: u32) {
156 let used_cells: std::collections::HashSet<Cell> = self
157 .cells
158 .iter()
159 .filter_map(|entry| {
160 if entry.value().last_verified_version >= version {
161 Some(entry.key().clone())
162 } else {
163 None
164 }
165 })
166 .collect();
167
168 self.storage.gc(&used_cells);
169 }
170}
171
172impl<S: Storage> Db<S> {
173 pub fn update_input<C>(&mut self, input: C, new_value: C::Output)
181 where
182 C: Computation,
183 S: StorageFor<C>,
184 {
185 let cell_id = self.get_or_insert_cell(input);
186 assert!(
187 self.is_input(cell_id),
188 "`update_input` given a non-input value. Inputs must have 0 dependencies",
189 );
190
191 let changed = self.storage.update_output(cell_id, new_value);
192 let mut cell = self.cells.get_mut(&cell_id).unwrap();
193
194 if changed {
195 let version = self.version.fetch_add(1, Ordering::SeqCst) + 1;
196 cell.last_updated_version = version;
197 cell.last_verified_version = version;
198 } else {
199 cell.last_verified_version = self.version.load(Ordering::SeqCst);
200 }
201 }
202
203 fn is_input(&self, cell: Cell) -> bool {
204 self.with_cell(cell, |cell| {
205 cell.dependencies.is_empty() && cell.input_dependencies.is_empty()
206 })
207 }
208
209 pub fn is_stale<C: Computation>(&self, input: &C) -> bool
214 where
215 S: StorageFor<C>,
216 {
217 let Some(cell) = self.get_cell(input) else {
219 return true;
220 };
221 self.is_stale_cell(cell)
222 }
223
224 fn is_stale_cell(&self, cell: Cell) -> bool {
228 let state = self.with_cell(cell, |data| {
229 (!self.storage.output_is_unset(cell, data.computation_id)).then(|| {
230 (
231 data.computation_id,
232 data.last_verified_version,
233 data.input_dependencies.clone(),
234 data.dependencies.clone(),
235 )
236 })
237 });
238
239 let Some((computation_id, last_verified, inputs, dependencies)) = state else {
240 return true;
241 };
242
243 let inputs_changed = inputs.into_iter().any(|input_id| {
246 self.with_cell(input_id, |input| input.last_updated_version > last_verified)
249 });
250
251 inputs_changed
255 && dependencies.into_iter().any(|dependency_id| {
256 self.update_cell(dependency_id);
257 self.with_cell(dependency_id, |dependency| {
258 if computation_id == ACCUMULATED_COMPUTATION_ID {
259 dependency.last_run_version > last_verified
260 } else {
261 dependency.last_updated_version > last_verified
262 }
263 })
264 })
265 }
266
267 fn run_compute_function(&self, cell_id: Cell) {
271 let computation_id = self.with_cell(cell_id, |data| data.computation_id);
272 self.storage.clear_accumulated_for_cell(cell_id);
273 let handle = self.handle(cell_id);
274 let changed = S::run_computation(&handle, cell_id, computation_id);
275
276 let version = self.version.load(Ordering::SeqCst);
277 let mut cell = self.cells.get_mut(&cell_id).unwrap();
278 cell.last_verified_version = version;
279 cell.last_run_version = version;
280
281 if changed {
282 cell.last_updated_version = version;
283 }
284 }
285
286 fn update_cell(&self, cell_id: Cell) {
289 let last_verified_version = self.with_cell(cell_id, |data| data.last_verified_version);
290 let version = self.version.load(Ordering::SeqCst);
291
292 if last_verified_version != version {
293 if self.is_stale_cell(cell_id) {
295 let lock = self.with_cell(cell_id, |cell| cell.lock.clone());
296
297 match lock.try_lock() {
298 Some(guard) => {
299 self.run_compute_function(cell_id);
300 drop(guard);
301 }
302 None => {
303 self.check_for_cycle(cell_id);
307
308 drop(lock.lock());
310 }
311 }
312 } else {
313 let mut cell = self.cells.get_mut(&cell_id).unwrap();
314 cell.last_verified_version = version;
315 }
316 }
317 }
318
319 fn check_for_cycle(&self, starting_cell: Cell) {
321 let mut visited = FxHashSet::default();
322 let mut path = Vec::new();
323
324 let mut stack = Vec::new();
329 stack.push(Action::Traverse(starting_cell));
330
331 enum Action {
332 Traverse(Cell),
333 Pop(Cell),
334 }
335
336 while let Some(action) = stack.pop() {
337 match action {
338 Action::Pop(expected) => assert_eq!(path.pop(), Some(expected)),
340 Action::Traverse(cell) => {
341 if path.contains(&cell) {
342 path.push(cell);
344 self.cycle_error(&path);
345 }
346
347 if visited.insert(cell) {
348 path.push(cell);
349 stack.push(Action::Pop(cell));
350 self.with_cell(cell, |cell| {
351 for dependency in cell.dependencies.iter() {
352 stack.push(Action::Traverse(*dependency));
353 }
354 });
355 }
356 }
357 }
358 }
359 }
360
361 fn cycle_error(&self, cycle: &[Cell]) {
363 let mut error = String::new();
364 for (i, cell) in cycle.iter().enumerate() {
365 error += &format!(
366 "\n {}. {}",
367 i + 1,
368 self.storage.input_debug_string(self, *cell)
369 );
370 }
371 panic!("inc-complete: Cycle Detected!\n\nCycle:{error}")
372 }
373
374 pub fn get<C: Computation>(&self, compute: C) -> C::Output
382 where
383 S: StorageFor<C>,
384 {
385 let cell_id = self.get_or_insert_cell(compute);
386 self.get_with_cell::<C>(cell_id)
387 }
388
389 pub(crate) fn get_with_cell<Concrete: Computation>(&self, cell_id: Cell) -> Concrete::Output
390 where
391 S: StorageFor<Concrete>,
392 {
393 self.update_cell(cell_id);
394
395 self.storage
396 .get_output(cell_id)
397 .expect("cell result should have been computed already")
398 }
399
400 fn with_cell<R>(&self, cell: Cell, f: impl FnOnce(&CellData) -> R) -> R {
401 f(&self.cells.get(&cell).unwrap())
402 }
403
404 pub fn get_accumulated<Item, C>(&self, compute: C) -> BTreeSet<Item>
416 where
417 S: StorageFor<C> + StorageFor<Accumulated<Item>>,
418 C: Computation,
419 Item: 'static,
420 {
421 let cell_id = self.get_or_insert_cell(compute);
422 self.update_cell(cell_id);
423 self.get(Accumulated::<Item>::new(cell_id))
424 }
425
426 pub fn get_accumulated_uncached<Item, C>(&mut self, compute: C) -> BTreeSet<Item>
435 where
436 S: StorageFor<C> + StorageFor<Accumulated<Item>> + Accumulate<Item>,
437 C: Computation,
438 Item: 'static + Ord,
439 {
440 let cell_id = self.get_or_insert_cell(compute);
441 self.update_cell(cell_id);
442
443 let mut items = BTreeSet::new();
444 let mut visited = BTreeSet::new();
445 let mut queue = vec![cell_id];
446
447 while let Some(cell) = queue.pop() {
448 if visited.insert(cell) {
449 self.with_cell(cell, |data| queue.extend_from_slice(&data.dependencies));
450 items.extend(self.storage().get_accumulated::<Vec<Item>>(cell));
451 }
452 }
453
454 items
455 }
456}