changeset 51251:0409bd6ba663

rust-revlog: add invalidation detection to `NodeTree` class This will be useful for callers, such as `scmutil` who reuse a `NodeTree` instance as a cache. They would otherwise get hard errors if any mutation of the index occurred since instantiation. This is something the C index does not provide.
author Georges Racinet <georges.racinet@octobus.net>
date Mon, 30 Oct 2023 22:36:30 +0100
parents a8ca22119385
children fd1aa5e18f75
files rust/hg-cpython/src/revlog.rs tests/test-rust-revlog.py
diffstat 2 files changed, 22 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-cpython/src/revlog.rs	Thu Nov 02 15:50:13 2023 +0100
+++ b/rust/hg-cpython/src/revlog.rs	Mon Oct 30 22:36:30 2023 +0100
@@ -979,6 +979,24 @@
         Self::create_instance(py, RefCell::new(nt), RefCell::new(index))
     }
 
+    /// Tell whether the NodeTree is still valid
+    ///
+    /// In case of mutation of the index, the given results are not
+    /// guaranteed to be correct, and in fact, the methods borrowing
+    /// the inner index would fail because of `PySharedRef` poisoning
+    /// (generation-based guard), same as iterating on a `dict` that has
+    /// been meanwhile mutated.
+    def is_invalidated(&self) -> PyResult<bool> {
+        let leaked = self.index(py).borrow();
+        let result = unsafe { leaked.try_borrow(py) };
+        // two cases for result to be an error:
+        // - the index has previously been mutably borrowed
+        // - there is currently a mutable borrow
+        // in both cases this amounts for previous results related to
+        // the index to still be valid.
+        Ok(result.is_err())
+    }
+
     def insert(&self, rev: PyRevision) -> PyResult<PyObject> {
         let leaked = self.index(py).borrow();
         let index = &*unsafe { leaked.try_borrow(py)? };
--- a/tests/test-rust-revlog.py	Thu Nov 02 15:50:13 2023 +0100
+++ b/tests/test-rust-revlog.py	Mon Oct 30 22:36:30 2023 +0100
@@ -87,6 +87,10 @@
             self.assertEqual(shortest, expected)
             self.assertEqual(nt.prefix_rev_lookup(hex_node[:shortest]), i)
 
+        # test invalidation (generation poisoning) detection
+        del idx[3]
+        self.assertTrue(nt.is_invalidated())
+
 
 if __name__ == '__main__':
     import silenttestrunner