Mercurial > hg
diff rust/hg-core/src/revlog/index.rs @ 51234:59183a19954e
rust-index: use interior mutability in head revs and caches
For upcoming changes in `hg-cpython` switching to the `hg-core` index in
ancestors iterators, we will need to avoid excessive mutability, restricting
the use of mutable references on `hg::index::Index` to methods that actually
logically mutate it, whereas the maintenance of caches such as `head_revs`
clearly does not. We illustrate that immediately by switching to immutable
borrows in the corresponding methods of `hg-cpython::MixedIndex`
author | Georges Racinet on incendie.racinet.fr <georges@racinet.fr> |
---|---|
date | Fri, 27 Oct 2023 21:48:45 +0200 |
parents | ca81cd96000a |
children | 456e0fe702e8 |
line wrap: on
line diff
--- a/rust/hg-core/src/revlog/index.rs Thu Oct 26 15:26:19 2023 +0200 +++ b/rust/hg-core/src/revlog/index.rs Fri Oct 27 21:48:45 2023 +0200 @@ -261,12 +261,13 @@ offsets: RwLock<Option<Vec<usize>>>, uses_generaldelta: bool, is_inline: bool, - /// Cache of the head revisions in this index, kept in sync. Should + /// Cache of (head_revisions, filtered_revisions) + /// + /// The head revisions in this index, kept in sync. Should /// be accessed via the [`Self::head_revs`] method. - head_revs: Vec<Revision>, - /// Cache of the last filtered revisions in this index, used to make sure + /// The last filtered revisions in this index, used to make sure /// we haven't changed filters when returning the cached `head_revs`. - filtered_revs: HashSet<Revision>, + head_revs: RwLock<(Vec<Revision>, HashSet<Revision>)>, } impl Debug for Index { @@ -366,8 +367,7 @@ offsets: RwLock::new(Some(offsets)), uses_generaldelta, is_inline: true, - head_revs: vec![], - filtered_revs: HashSet::new(), + head_revs: RwLock::new((vec![], HashSet::new())), }) } else { Err(HgError::corrupted("unexpected inline revlog length")) @@ -378,8 +378,7 @@ offsets: RwLock::new(None), uses_generaldelta, is_inline: false, - head_revs: vec![], - filtered_revs: HashSet::new(), + head_revs: RwLock::new((vec![], HashSet::new())), }) } } @@ -532,17 +531,27 @@ } /// Return the head revisions of this index - pub fn head_revs(&mut self) -> Result<Vec<Revision>, GraphError> { + pub fn head_revs(&self) -> Result<Vec<Revision>, GraphError> { self.head_revs_filtered(&HashSet::new()) } /// Return the head revisions of this index pub fn head_revs_filtered( - &mut self, + &self, filtered_revs: &HashSet<Revision>, ) -> Result<Vec<Revision>, GraphError> { - if !self.head_revs.is_empty() && filtered_revs == &self.filtered_revs { - return Ok(self.head_revs.to_owned()); + { + let guard = self + .head_revs + .read() + .expect("RwLock on Index.head_revs should not be poisoned"); + let self_head_revs = &guard.0; + let self_filtered_revs = &guard.1; + if !self_head_revs.is_empty() + && filtered_revs == self_filtered_revs + { + return Ok(self_head_revs.to_owned()); + } } let mut revs: HashSet<Revision, RandomState> = if filtered_revs.is_empty() { @@ -570,8 +579,11 @@ let mut as_vec: Vec<Revision> = revs.into_iter().map(Into::into).collect(); as_vec.sort_unstable(); - self.head_revs = as_vec.to_owned(); - self.filtered_revs = filtered_revs.to_owned(); + *self + .head_revs + .write() + .expect("RwLock on Index.head_revs should not be poisoned") = + (as_vec.to_owned(), filtered_revs.to_owned()); Ok(as_vec) } @@ -651,6 +663,14 @@ Ok(()) } + fn clear_head_revs(&self) { + self.head_revs + .write() + .expect("RwLock on Index.head_revs should not be poisoined") + .0 + .clear() + } + /// TODO move this to the trait probably, along with other things pub fn append( &mut self, @@ -662,7 +682,7 @@ offsets.push(new_offset) } self.bytes.added.extend(revision_data.into_v1().as_bytes()); - self.head_revs.clear(); + self.clear_head_revs(); Ok(()) } @@ -676,16 +696,19 @@ if let Some(offsets) = &mut *self.get_offsets_mut() { offsets.truncate(rev.0 as usize) } - self.head_revs.clear(); + self.clear_head_revs(); Ok(()) } - pub fn clear_caches(&mut self) { + pub fn clear_caches(&self) { // We need to get the 'inline' value from Python at init and use this // instead of offsets to determine whether we're inline since we might // clear caches. This implies re-populating the offsets on-demand. - self.offsets = RwLock::new(None); - self.head_revs.clear(); + *self + .offsets + .write() + .expect("RwLock on Index.offsets should not be poisoed") = None; + self.clear_head_revs(); } /// Unchecked version of `is_snapshot`.