rust-dirstate: remember the data file uuid dirstate was loaded with stable
authorRaphaël Gomès <rgomes@octobus.net>
Mon, 12 Dec 2022 17:08:12 +0100
branchstable
changeset 50243 6cce0afc1454
parent 50242 379a78001d8e
child 50244 07d030b38097
rust-dirstate: remember the data file uuid dirstate was loaded with This will be used in the next patch to fix a race condition.
mercurial/dirstatemap.py
rust/hg-core/src/dirstate_tree/dirstate_map.rs
rust/hg-core/src/dirstate_tree/on_disk.rs
rust/hg-core/src/dirstate_tree/owning.rs
rust/hg-core/src/repo.rs
rust/hg-cpython/src/dirstate/dirstate_map.rs
--- a/mercurial/dirstatemap.py	Wed Mar 01 02:38:20 2023 +0100
+++ b/mercurial/dirstatemap.py	Mon Dec 12 17:08:12 2022 +0100
@@ -573,11 +573,15 @@
                 testing.wait_on_cfg(self._ui, b'dirstate.post-docket-read-file')
                 if not self.docket.uuid:
                     data = b''
+                    self._map = rustmod.DirstateMap.new_empty()
                 else:
                     data = self._read_v2_data()
-                self._map = rustmod.DirstateMap.new_v2(
-                    data, self.docket.data_size, self.docket.tree_metadata
-                )
+                    self._map = rustmod.DirstateMap.new_v2(
+                        data,
+                        self.docket.data_size,
+                        self.docket.tree_metadata,
+                        self.docket.uuid,
+                    )
                 parents = self.docket.parents
             else:
                 self._set_identity()
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs	Wed Mar 01 02:38:20 2023 +0100
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs	Mon Dec 12 17:08:12 2022 +0100
@@ -66,9 +66,16 @@
     pub(super) unreachable_bytes: u32,
 
     /// Size of the data used to first load this `DirstateMap`. Used in case
-    /// we need to write some new metadata, but no new data on disk.
+    /// we need to write some new metadata, but no new data on disk,
+    /// as well as to detect writes that have happened in another process
+    /// since first read.
     pub(super) old_data_size: usize,
 
+    /// UUID used when first loading this `DirstateMap`. Used to check if
+    /// the UUID has been changed by another process since first read.
+    /// Can be `None` if using dirstate v1 or if it's a brand new dirstate.
+    pub(super) old_uuid: Option<Vec<u8>>,
+
     pub(super) dirstate_version: DirstateVersion,
 
     /// Controlled by config option `devel.dirstate.v2.data_update_mode`
@@ -460,6 +467,7 @@
             ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
             unreachable_bytes: 0,
             old_data_size: 0,
+            old_uuid: None,
             dirstate_version: DirstateVersion::V1,
             write_mode: DirstateMapWriteMode::Auto,
         }
@@ -470,9 +478,10 @@
         on_disk: &'on_disk [u8],
         data_size: usize,
         metadata: &[u8],
+        uuid: Vec<u8>,
     ) -> Result<Self, DirstateError> {
         if let Some(data) = on_disk.get(..data_size) {
-            Ok(on_disk::read(data, metadata)?)
+            Ok(on_disk::read(data, metadata, uuid)?)
         } else {
             Err(DirstateV2ParseError::new("not enough bytes on disk").into())
         }
@@ -1843,6 +1852,7 @@
             packed,
             packed_len,
             metadata.as_bytes(),
+            vec![],
         )?;
 
         // Check that everything is accounted for
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs	Wed Mar 01 02:38:20 2023 +0100
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs	Mon Dec 12 17:08:12 2022 +0100
@@ -290,6 +290,7 @@
 pub(super) fn read<'on_disk>(
     on_disk: &'on_disk [u8],
     metadata: &[u8],
+    uuid: Vec<u8>,
 ) -> Result<DirstateMap<'on_disk>, DirstateV2ParseError> {
     if on_disk.is_empty() {
         let mut map = DirstateMap::empty(on_disk);
@@ -312,6 +313,7 @@
         ignore_patterns_hash: meta.ignore_patterns_hash,
         unreachable_bytes: meta.unreachable_bytes.get(),
         old_data_size: on_disk.len(),
+        old_uuid: Some(uuid),
         dirstate_version: DirstateVersion::V2,
         write_mode: DirstateMapWriteMode::Auto,
     };
--- a/rust/hg-core/src/dirstate_tree/owning.rs	Wed Mar 01 02:38:20 2023 +0100
+++ b/rust/hg-core/src/dirstate_tree/owning.rs	Mon Dec 12 17:08:12 2022 +0100
@@ -57,6 +57,7 @@
         on_disk: OnDisk,
         data_size: usize,
         metadata: &[u8],
+        uuid: Vec<u8>,
     ) -> Result<Self, DirstateError>
     where
         OnDisk: Deref<Target = [u8]> + Send + 'static,
@@ -66,7 +67,7 @@
         OwningDirstateMapTryBuilder {
             on_disk,
             map_builder: |bytes| {
-                DirstateMap::new_v2(&bytes, data_size, metadata)
+                DirstateMap::new_v2(&bytes, data_size, metadata, uuid)
             },
         }
         .try_build()
@@ -86,4 +87,12 @@
     pub fn on_disk(&self) -> &[u8] {
         self.borrow_on_disk()
     }
+
+    pub fn old_uuid(&self) -> Option<&[u8]> {
+        self.get_map().old_uuid.as_deref()
+    }
+
+    pub fn old_data_size(&self) -> usize {
+        self.get_map().old_data_size
+    }
 }
--- a/rust/hg-core/src/repo.rs	Wed Mar 01 02:38:20 2023 +0100
+++ b/rust/hg-core/src/repo.rs	Mon Dec 12 17:08:12 2022 +0100
@@ -383,6 +383,7 @@
         self.dirstate_parents.set(docket.parents());
         self.dirstate_data_file_uuid
             .set(Some(docket.uuid.to_owned()));
+        let uuid = docket.uuid.to_owned();
         let data_size = docket.data_size();
 
         let context = "between reading dirstate docket and data file";
@@ -415,16 +416,16 @@
                 }
                 Err(e) => return Err(e.into()),
             };
-            OwningDirstateMap::new_v2(contents, data_size, metadata)
+            OwningDirstateMap::new_v2(contents, data_size, metadata, uuid)
         } else {
             match self
                 .hg_vfs()
                 .mmap_open(docket.data_filename())
                 .io_not_found_as_none()
             {
-                Ok(Some(data_mmap)) => {
-                    OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
-                }
+                Ok(Some(data_mmap)) => OwningDirstateMap::new_v2(
+                    data_mmap, data_size, metadata, uuid,
+                ),
                 Ok(None) => {
                     // Race where the data file was deleted right after we
                     // read the docket, try again
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs	Wed Mar 01 02:38:20 2023 +0100
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs	Mon Dec 12 17:08:12 2022 +0100
@@ -66,18 +66,28 @@
         on_disk: PyBytes,
         data_size: usize,
         tree_metadata: PyBytes,
+        uuid: PyBytes,
     ) -> PyResult<PyObject> {
         let dirstate_error = |e: DirstateError| {
             PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
         };
         let on_disk = PyBytesDeref::new(py, on_disk);
+        let uuid = uuid.data(py);
         let map = OwningDirstateMap::new_v2(
-            on_disk, data_size, tree_metadata.data(py),
+            on_disk, data_size, tree_metadata.data(py), uuid.to_owned(),
         ).map_err(dirstate_error)?;
         let map = Self::create_instance(py, map)?;
         Ok(map.into_object())
     }
 
+    /// Returns an empty DirstateMap. Only used for a new dirstate.
+    @staticmethod
+    def new_empty() -> PyResult<PyObject> {
+        let map = OwningDirstateMap::new_empty(vec![]);
+        let map = Self::create_instance(py, map)?;
+        Ok(map.into_object())
+    }
+
     def clear(&self) -> PyResult<PyObject> {
         self.inner(py).borrow_mut().clear();
         Ok(py.None())