rust-dirstate: remember the data file uuid dirstate was loaded with
This will be used in the next patch to fix a race condition.
--- 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())