inc_complete/db/handle.rs
1use crate::{
2 accumulate::Accumulate, storage::{ComputationId, StorageFor}, Cell, Db, OutputType, Storage
3};
4
5use super::DbGet;
6
7/// A handle to the database during some operation.
8///
9/// This wraps calls to the Db so that any `get` calls
10/// will be automatically registered as dependencies of
11/// the current operation.
12pub struct DbHandle<'db, S> {
13 db: &'db Db<S>,
14 current_operation: Cell,
15}
16
17impl<'db, S> DbHandle<'db, S> {
18 pub(crate) fn new(db: &'db Db<S>, current_operation: Cell) -> Self {
19 // We're re-running a cell so remove any past dependencies
20 let mut cell = db.cells.get_mut(¤t_operation).unwrap();
21
22 cell.dependencies.clear();
23 cell.input_dependencies.clear();
24
25 Self {
26 db,
27 current_operation,
28 }
29 }
30
31 /// Retrieve an immutable reference to this `Db`'s storage
32 ///
33 /// Note that any mutations made to the storage using this are _not_ tracked by the database!
34 /// Using this incorrectly may break correctness!
35 pub fn storage(&self) -> &S {
36 self.db.storage()
37 }
38}
39
40impl<S: Storage> DbHandle<'_, S> {
41 /// Locking behavior: This function locks the cell corresponding to the given computation. This
42 /// can cause a deadlock if the computation recursively depends on itself.
43 pub fn get<C: OutputType + ComputationId>(&self, compute: C) -> C::Output
44 where
45 S: StorageFor<C>,
46 {
47 // Register the dependency
48 let dependency = self.db.get_or_insert_cell(compute);
49 self.update_and_register_dependency(dependency);
50
51 // Fetch the current value of the dependency, running it if out of date
52 self.db.get_with_cell(dependency)
53 }
54
55 /// Registers the given cell as a dependency, running it and updating any required metadata
56 fn update_and_register_dependency<C: OutputType + ComputationId>(&self, dependency: Cell)
57 where
58 S: StorageFor<C>,
59 {
60 let mut cell = self.db.cells.get_mut(&self.current_operation).unwrap();
61
62 // If `dependency` is an input it must be remembered both as a dependency
63 // and as an input dependency. Otherwise we cannot differentiate between
64 // computations which directly depend on inputs and those that only indirectly
65 // depend on them.
66 cell.dependencies.push(dependency);
67 if C::IS_INPUT {
68 cell.input_dependencies.insert(dependency);
69 }
70
71 drop(cell);
72
73 // Run the computation to update its dependencies before we query them afterward
74 self.db.update_cell(dependency);
75
76 let dependency = self.db.cells.get(&dependency).unwrap();
77 let dependency_inputs = dependency.input_dependencies.clone();
78 drop(dependency);
79
80 let mut cell = self.db.cells.get_mut(&self.current_operation).unwrap();
81 for input in dependency_inputs {
82 cell.input_dependencies.insert(input);
83 }
84 }
85
86 /// Accumulate an item in the current computation. This item can be retrieved along
87 /// with all other accumulated items in this computation and its dependencies via
88 /// a call to `get_accumulated`.
89 ///
90 /// This is most often used for operations like pushing diagnostics or logs.
91 pub fn accumulate<Item>(&self, item: Item) where
92 S: Accumulate<Item>
93 {
94 self.storage().accumulate(self.current_operation, item);
95 }
96
97 /// Retrieve an accumulated value in a container of the user's choice.
98 /// This will return all the accumulated items after the given computation.
99 ///
100 /// Note that although this method will not re-perform the given computation,
101 /// it will re-collect all the required accumulated items each time it is called,
102 /// which may be costly for large dependency trees.
103 ///
104 /// This is most often used for operations like retrieving diagnostics or logs.
105 ///
106 /// FIXME: This method is private to the crate until the bug in tracking
107 /// accumulated values is fixed (see src/db/tests/accumulated.rs). Use the
108 /// version on a full `Db` in the meantime which does not require dependency tracking.
109 #[allow(unused)]
110 pub(crate) fn get_accumulated<Container, Item, C>(&self, compute: C) -> Container where
111 Container: FromIterator<Item>,
112 S: Accumulate<Item> + StorageFor<C>,
113 C: OutputType + ComputationId
114 {
115 // Ensure the dependency is registered.
116 let cell_id = self.db.get_or_insert_cell(compute);
117 let _ = self.update_and_register_dependency(cell_id);
118
119 let cells = self.db.collect_all_dependencies(cell_id);
120 self.storage().get_accumulated(&cells)
121 }
122}
123
124impl<'db, S, C> DbGet<C> for DbHandle<'db, S>
125where
126 C: OutputType + ComputationId,
127 S: Storage + StorageFor<C>,
128{
129 fn get(&self, key: C) -> C::Output {
130 self.get(key)
131 }
132}