Mercurial > hg-stable
changeset 47986:fc208d6faed3
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct
More components of `Repo` will be added following the same pattern.
Differential Revision: https://phab.mercurial-scm.org/D11405
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Mon, 13 Sep 2021 13:16:10 +0200 |
parents | d44740725b95 |
children | 21d25e9ee58e |
files | rust/hg-core/src/repo.rs |
diffstat | 1 files changed, 38 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/hg-core/src/repo.rs Mon Sep 13 13:01:25 2021 +0200 +++ b/rust/hg-core/src/repo.rs Mon Sep 13 13:16:10 2021 +0200 @@ -23,7 +23,7 @@ config: Config, // None means not known/initialized yet dirstate_parents: Cell<Option<DirstateParents>>, - dirstate_map: RefCell<Option<OwningDirstateMap>>, + dirstate_map: LazyCell<OwningDirstateMap, DirstateError>, } #[derive(Debug, derive_more::From)] @@ -196,7 +196,7 @@ dot_hg, config: repo_config, dirstate_parents: Cell::new(None), - dirstate_map: RefCell::new(None), + dirstate_map: LazyCell::new(Self::new_dirstate_map), }; requirements::check(&repo)?; @@ -302,24 +302,52 @@ pub fn dirstate_map( &self, ) -> Result<Ref<OwningDirstateMap>, DirstateError> { - let mut borrowed = self.dirstate_map.borrow(); + self.dirstate_map.get_or_init(self) + } + + pub fn dirstate_map_mut( + &self, + ) -> Result<RefMut<OwningDirstateMap>, DirstateError> { + self.dirstate_map.get_mut_or_init(self) + } +} + +/// Lazily-initialized component of `Repo` with interior mutability +/// +/// This differs from `OnceCell` in that the value can still be "deinitialized" +/// later by setting its inner `Option` to `None`. +struct LazyCell<T, E> { + value: RefCell<Option<T>>, + // `Fn`s that don’t capture environment are zero-size, so this box does + // not allocate: + init: Box<dyn Fn(&Repo) -> Result<T, E>>, +} + +impl<T, E> LazyCell<T, E> { + fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self { + Self { + value: RefCell::new(None), + init: Box::new(init), + } + } + + fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> { + let mut borrowed = self.value.borrow(); if borrowed.is_none() { drop(borrowed); // Only use `borrow_mut` if it is really needed to avoid panic in // case there is another outstanding borrow but mutation is not // needed. - *self.dirstate_map.borrow_mut() = Some(self.new_dirstate_map()?); - borrowed = self.dirstate_map.borrow() + *self.value.borrow_mut() = Some((self.init)(repo)?); + borrowed = self.value.borrow() } Ok(Ref::map(borrowed, |option| option.as_ref().unwrap())) } - pub fn dirstate_map_mut( - &self, - ) -> Result<RefMut<OwningDirstateMap>, DirstateError> { - let mut borrowed = self.dirstate_map.borrow_mut(); + pub fn get_mut_or_init(&self, repo: &Repo) -> Result<RefMut<T>, E> { + let mut borrowed = self.value.borrow_mut(); if borrowed.is_none() { - *borrowed = Some(self.new_dirstate_map()?); + *borrowed = Some((self.init)(repo)?); } Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap())) }