diff --git a/src/derived.rs b/src/derived.rs index 52e9660..9a08856 100644 --- a/src/derived.rs +++ b/src/derived.rs @@ -226,14 +226,16 @@ where { fn invalidate(&self, db: &mut DB, key: &Q::Key) { db.salsa_runtime_mut() - .with_incremented_revision(&mut |_new_revision, guard| { + .with_incremented_revision(&mut |_new_revision| { let map_read = self.slot_map.read(); if let Some(slot) = map_read.get(key) { if let Some(durability) = slot.invalidate() { - guard.mark_durability_as_changed(durability); + return Some(durability); } } + + None }) } } diff --git a/src/input.rs b/src/input.rs index e8896dc..867f03c 100644 --- a/src/input.rs +++ b/src/input.rs @@ -193,7 +193,7 @@ where // case doesn't generally seem worth optimizing for. let mut value = Some(value); db.salsa_runtime_mut() - .with_incremented_revision(&mut |next_revision, guard| { + .with_incremented_revision(&mut |next_revision| { let mut slots = self.slots.write(); // Do this *after* we acquire the lock, so that we are not @@ -209,8 +209,9 @@ where match slots.entry(key.clone()) { Entry::Occupied(entry) => { let mut slot_stamped_value = entry.get().stamped_value.write(); - guard.mark_durability_as_changed(slot_stamped_value.durability); + let old_durability = slot_stamped_value.durability; *slot_stamped_value = stamped_value; + Some(old_durability) } Entry::Vacant(entry) => { @@ -225,6 +226,7 @@ where database_key_index, stamped_value: RwLock::new(stamped_value), })); + None } } }); diff --git a/src/runtime.rs b/src/runtime.rs index 7796194..2165cdc 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -143,9 +143,7 @@ where /// will block until that snapshot is dropped -- if that snapshot /// is owned by the current thread, this could trigger deadlock. pub fn synthetic_write(&mut self, durability: Durability) { - self.with_incremented_revision(&mut |_next_revision, guard| { - guard.mark_durability_as_changed(durability); - }); + self.with_incremented_revision(&mut |_next_revision| Some(durability)); } /// Default implementation for `Database::sweep_all`. @@ -282,20 +280,29 @@ where /// Acquires the **global query write lock** (ensuring that no queries are /// executing) and then increments the current revision counter; invokes - /// `op` with the global query write lock still held. The `op` closure is - /// given the new revision as an argument, and it should actually perform - /// the writes. + /// `op` with the global query write lock still held. /// /// While we wait to acquire the global query write lock, this method will /// also increment `pending_revision_increments`, thus signalling to queries /// that their results are "canceled" and they should abort as expeditiously /// as possible. /// + /// The `op` closure should actually perform the writes needed. It is given + /// the new revision as an argument, and its return value indicates whether + /// any pre-existing value was modified: + /// + /// - returning `None` means that no pre-existing value was modified (this + /// could occur e.g. when setting some key on an input that was never set + /// before) + /// - returning `Some(d)` indicates that a pre-existing value was modified + /// and it had the durability `d`. This will update the records for when + /// values with each durability were modified. + /// /// Note that, given our writer model, we can assume that only one thread is /// attempting to increment the global revision at a time. pub(crate) fn with_incremented_revision( &mut self, - op: &mut dyn FnMut(Revision, &DatabaseWriteLockGuard<'_, DB>), + op: &mut dyn FnMut(Revision) -> Option, ) { log::debug!("increment_revision()"); @@ -318,13 +325,11 @@ where debug!("increment_revision: incremented to {:?}", new_revision); - op( - new_revision, - &DatabaseWriteLockGuard { - runtime: self, - new_revision, - }, - ) + if let Some(d) = op(new_revision) { + for rev in &self.shared_state.revisions[1..=d.index()] { + rev.store(new_revision); + } + } } pub(crate) fn permits_increment(&self) -> bool { @@ -528,34 +533,6 @@ where } } -/// Temporary guard that indicates that the database write-lock is -/// held. You can get one of these by invoking -/// `with_incremented_revision`. It gives access to the new revision -/// and a few other operations that only make sense to do while an -/// update is happening. -pub(crate) struct DatabaseWriteLockGuard<'db, DB> -where - DB: Database, -{ - runtime: &'db mut Runtime, - new_revision: Revision, -} - -impl DatabaseWriteLockGuard<'_, DB> -where - DB: Database, -{ - /// Indicates that this update modified an input marked as - /// "constant". This will force re-evaluation of anything that was - /// dependent on constants (which otherwise might not get - /// re-evaluated). - pub(crate) fn mark_durability_as_changed(&self, d: Durability) { - for rev in &self.runtime.shared_state.revisions[1..=d.index()] { - rev.store(self.new_revision); - } - } -} - /// State that will be common to all threads (when we support multiple threads) struct SharedState { storage: DB::DatabaseStorage,