Skip to main content

inc_complete/db/
handle.rs

1use crate::{
2    accumulate::Accumulate, storage::StorageFor, Cell, Db, Computation, 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(&current_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: Computation>(&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: Computation>(&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        // TODO: Check if we should filter out already-inserted dependencies
67        cell.dependencies.push(dependency);
68        if C::IS_INPUT {
69            cell.input_dependencies.insert(dependency);
70        }
71
72        drop(cell);
73
74        // Run the computation to update its dependencies before we query them afterward
75        self.db.update_cell(dependency);
76
77        let dependency = self.db.cells.get(&dependency).unwrap();
78        let dependency_inputs = dependency.input_dependencies.clone();
79        drop(dependency);
80
81        let mut cell = self.db.cells.get_mut(&self.current_operation).unwrap();
82        for input in dependency_inputs {
83            cell.input_dependencies.insert(input);
84        }
85    }
86
87    /// Accumulate an item in the current computation. This item can be retrieved along
88    /// with all other accumulated items in this computation and its dependencies via
89    /// a call to `get_accumulated`.
90    ///
91    /// This is most often used for operations like pushing diagnostics or logs.
92    pub fn accumulate<Item>(&self, item: Item) where
93        S: Accumulate<Item>
94    {
95        self.storage().accumulate(self.current_operation, item);
96    }
97
98    /// Retrieve an accumulated value in a container of the user's choice.
99    /// This will return all the accumulated items after the given computation.
100    ///
101    /// Note that although this method will not re-perform the given computation,
102    /// it will re-collect all the required accumulated items each time it is called,
103    /// which may be costly for large dependency trees.
104    ///
105    /// This is most often used for operations like retrieving diagnostics or logs.
106    ///
107    /// FIXME: This method is private to the crate until the bug in tracking
108    /// accumulated values is fixed (see src/db/tests/accumulated.rs). Use the
109    /// version on a full `Db` in the meantime which does not require dependency tracking.
110    #[allow(unused)]
111    pub(crate) fn get_accumulated<Container, Item, C>(&self, compute: C) -> Container where
112        Container: FromIterator<Item>,
113        S: Accumulate<Item> + StorageFor<C>,
114        C: Computation
115    {
116        // Ensure the dependency is registered.
117        let cell_id = self.db.get_or_insert_cell(compute);
118        let _ = self.update_and_register_dependency(cell_id);
119
120        let cells = self.db.collect_all_dependencies(cell_id);
121        self.storage().get_accumulated(&cells)
122    }
123}
124
125impl<'db, S, C> DbGet<C> for DbHandle<'db, S>
126where
127    C: Computation,
128    S: Storage + StorageFor<C>,
129{
130    fn get(&self, key: C) -> C::Output {
131        self.get(key)
132    }
133}