comparison rust/hg-core/src/repo.rs @ 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 81aedf1fc897
children 21d25e9ee58e
comparison
equal deleted inserted replaced
47985:d44740725b95 47986:fc208d6faed3
21 store: PathBuf, 21 store: PathBuf,
22 requirements: HashSet<String>, 22 requirements: HashSet<String>,
23 config: Config, 23 config: Config,
24 // None means not known/initialized yet 24 // None means not known/initialized yet
25 dirstate_parents: Cell<Option<DirstateParents>>, 25 dirstate_parents: Cell<Option<DirstateParents>>,
26 dirstate_map: RefCell<Option<OwningDirstateMap>>, 26 dirstate_map: LazyCell<OwningDirstateMap, DirstateError>,
27 } 27 }
28 28
29 #[derive(Debug, derive_more::From)] 29 #[derive(Debug, derive_more::From)]
30 pub enum RepoError { 30 pub enum RepoError {
31 NotFound { 31 NotFound {
194 working_directory, 194 working_directory,
195 store: store_path, 195 store: store_path,
196 dot_hg, 196 dot_hg,
197 config: repo_config, 197 config: repo_config,
198 dirstate_parents: Cell::new(None), 198 dirstate_parents: Cell::new(None),
199 dirstate_map: RefCell::new(None), 199 dirstate_map: LazyCell::new(Self::new_dirstate_map),
200 }; 200 };
201 201
202 requirements::check(&repo)?; 202 requirements::check(&repo)?;
203 203
204 Ok(repo) 204 Ok(repo)
300 } 300 }
301 301
302 pub fn dirstate_map( 302 pub fn dirstate_map(
303 &self, 303 &self,
304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> { 304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> {
305 let mut borrowed = self.dirstate_map.borrow(); 305 self.dirstate_map.get_or_init(self)
306 }
307
308 pub fn dirstate_map_mut(
309 &self,
310 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
311 self.dirstate_map.get_mut_or_init(self)
312 }
313 }
314
315 /// Lazily-initialized component of `Repo` with interior mutability
316 ///
317 /// This differs from `OnceCell` in that the value can still be "deinitialized"
318 /// later by setting its inner `Option` to `None`.
319 struct LazyCell<T, E> {
320 value: RefCell<Option<T>>,
321 // `Fn`s that don’t capture environment are zero-size, so this box does
322 // not allocate:
323 init: Box<dyn Fn(&Repo) -> Result<T, E>>,
324 }
325
326 impl<T, E> LazyCell<T, E> {
327 fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self {
328 Self {
329 value: RefCell::new(None),
330 init: Box::new(init),
331 }
332 }
333
334 fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> {
335 let mut borrowed = self.value.borrow();
306 if borrowed.is_none() { 336 if borrowed.is_none() {
307 drop(borrowed); 337 drop(borrowed);
308 // Only use `borrow_mut` if it is really needed to avoid panic in 338 // Only use `borrow_mut` if it is really needed to avoid panic in
309 // case there is another outstanding borrow but mutation is not 339 // case there is another outstanding borrow but mutation is not
310 // needed. 340 // needed.
311 *self.dirstate_map.borrow_mut() = Some(self.new_dirstate_map()?); 341 *self.value.borrow_mut() = Some((self.init)(repo)?);
312 borrowed = self.dirstate_map.borrow() 342 borrowed = self.value.borrow()
313 } 343 }
314 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap())) 344 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
315 } 345 }
316 346
317 pub fn dirstate_map_mut( 347 pub fn get_mut_or_init(&self, repo: &Repo) -> Result<RefMut<T>, E> {
318 &self, 348 let mut borrowed = self.value.borrow_mut();
319 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
320 let mut borrowed = self.dirstate_map.borrow_mut();
321 if borrowed.is_none() { 349 if borrowed.is_none() {
322 *borrowed = Some(self.new_dirstate_map()?); 350 *borrowed = Some((self.init)(repo)?);
323 } 351 }
324 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap())) 352 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
325 } 353 }
326 } 354 }
327 355