rust/hg-core/src/repo.rs
branchstable
changeset 50239 491f3dd080eb
parent 50234 2be6d5782728
child 50243 6cce0afc1454
equal deleted inserted replaced
50238:c9066fc609ef 50239:491f3dd080eb
    22 use std::io::Seek;
    22 use std::io::Seek;
    23 use std::io::SeekFrom;
    23 use std::io::SeekFrom;
    24 use std::io::Write as IoWrite;
    24 use std::io::Write as IoWrite;
    25 use std::path::{Path, PathBuf};
    25 use std::path::{Path, PathBuf};
    26 
    26 
       
    27 const V2_MAX_READ_ATTEMPTS: usize = 5;
       
    28 
    27 /// A repository on disk
    29 /// A repository on disk
    28 pub struct Repo {
    30 pub struct Repo {
    29     working_directory: PathBuf,
    31     working_directory: PathBuf,
    30     dot_hg: PathBuf,
    32     dot_hg: PathBuf,
    31     store: PathBuf,
    33     store: PathBuf,
   305         }
   307         }
   306     }
   308     }
   307 
   309 
   308     fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
   310     fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
   309         if self.has_dirstate_v2() {
   311         if self.has_dirstate_v2() {
   310             self.read_docket_and_data_file()
   312             // The v2 dirstate is split into a docket and a data file.
       
   313             // Since we don't always take the `wlock` to read it
       
   314             // (like in `hg status`), it is susceptible to races.
       
   315             // A simple retry method should be enough since full rewrites
       
   316             // only happen when too much garbage data is present and
       
   317             // this race is unlikely.
       
   318             let mut tries = 0;
       
   319 
       
   320             while tries < V2_MAX_READ_ATTEMPTS {
       
   321                 tries += 1;
       
   322                 match self.read_docket_and_data_file() {
       
   323                     Ok(m) => {
       
   324                         return Ok(m);
       
   325                     }
       
   326                     Err(e) => match e {
       
   327                         DirstateError::Common(HgError::RaceDetected(
       
   328                             context,
       
   329                         )) => {
       
   330                             log::info!(
       
   331                                 "dirstate read race detected {} (retry {}/{})",
       
   332                                 context,
       
   333                                 tries,
       
   334                                 V2_MAX_READ_ATTEMPTS,
       
   335                             );
       
   336                             continue;
       
   337                         }
       
   338                         _ => return Err(e.into()),
       
   339                     },
       
   340                 }
       
   341             }
       
   342             let error = HgError::abort(
       
   343                 format!("dirstate read race happened {tries} times in a row"),
       
   344                 255,
       
   345                 None,
       
   346             );
       
   347             return Err(DirstateError::Common(error));
   311         } else {
   348         } else {
   312             debug_wait_for_file_or_print(
   349             debug_wait_for_file_or_print(
   313                 self.config(),
   350                 self.config(),
   314                 "dirstate.pre-read-file",
   351                 "dirstate.pre-read-file",
   315             );
   352             );
   316             let dirstate_file_contents = self.dirstate_file_contents()?;
   353             let dirstate_file_contents = self.dirstate_file_contents()?;
   317             if dirstate_file_contents.is_empty() {
   354             return if dirstate_file_contents.is_empty() {
   318                 self.dirstate_parents.set(DirstateParents::NULL);
   355                 self.dirstate_parents.set(DirstateParents::NULL);
   319                 Ok(OwningDirstateMap::new_empty(Vec::new()))
   356                 Ok(OwningDirstateMap::new_empty(Vec::new()))
   320             } else {
   357             } else {
   321                 let (map, parents) =
   358                 let (map, parents) =
   322                     OwningDirstateMap::new_v1(dirstate_file_contents)?;
   359                     OwningDirstateMap::new_v1(dirstate_file_contents)?;
   323                 self.dirstate_parents.set(parents);
   360                 self.dirstate_parents.set(parents);
   324                 Ok(map)
   361                 Ok(map)
   325             }
   362             };
   326         }
   363         }
   327     }
   364     }
   328 
   365 
   329     fn read_docket_and_data_file(
   366     fn read_docket_and_data_file(
   330         &self,
   367         &self,
   345         );
   382         );
   346         self.dirstate_parents.set(docket.parents());
   383         self.dirstate_parents.set(docket.parents());
   347         self.dirstate_data_file_uuid
   384         self.dirstate_data_file_uuid
   348             .set(Some(docket.uuid.to_owned()));
   385             .set(Some(docket.uuid.to_owned()));
   349         let data_size = docket.data_size();
   386         let data_size = docket.data_size();
       
   387 
       
   388         let context = "between reading dirstate docket and data file";
       
   389         let race_error = HgError::RaceDetected(context.into());
   350         let metadata = docket.tree_metadata();
   390         let metadata = docket.tree_metadata();
       
   391 
   351         let mut map = if crate::vfs::is_on_nfs_mount(docket.data_filename()) {
   392         let mut map = if crate::vfs::is_on_nfs_mount(docket.data_filename()) {
   352             // Don't mmap on NFS to prevent `SIGBUS` error on deletion
   393             // Don't mmap on NFS to prevent `SIGBUS` error on deletion
   353             OwningDirstateMap::new_v2(
   394             let contents = self.hg_vfs().read(docket.data_filename());
   354                 self.hg_vfs().read(docket.data_filename())?,
   395             let contents = match contents {
   355                 data_size,
   396                 Ok(c) => c,
   356                 metadata,
   397                 Err(HgError::IoError { error, context }) => {
   357             )
   398                     match error.raw_os_error().expect("real os error") {
   358         } else if let Some(data_mmap) = self
   399                         // 2 = ENOENT, No such file or directory
   359             .hg_vfs()
   400                         // 116 = ESTALE, Stale NFS file handle
   360             .mmap_open(docket.data_filename())
   401                         //
   361             .io_not_found_as_none()?
   402                         // TODO match on `error.kind()` when
   362         {
   403                         // `ErrorKind::StaleNetworkFileHandle` is stable.
   363             OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
   404                         2 | 116 => {
   364         } else {
   405                             // Race where the data file was deleted right after
   365             OwningDirstateMap::new_v2(Vec::new(), data_size, metadata)
   406                             // we read the docket, try again
       
   407                             return Err(race_error.into());
       
   408                         }
       
   409                         _ => {
       
   410                             return Err(
       
   411                                 HgError::IoError { error, context }.into()
       
   412                             )
       
   413                         }
       
   414                     }
       
   415                 }
       
   416                 Err(e) => return Err(e.into()),
       
   417             };
       
   418             OwningDirstateMap::new_v2(contents, data_size, metadata)
       
   419         } else {
       
   420             match self
       
   421                 .hg_vfs()
       
   422                 .mmap_open(docket.data_filename())
       
   423                 .io_not_found_as_none()
       
   424             {
       
   425                 Ok(Some(data_mmap)) => {
       
   426                     OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
       
   427                 }
       
   428                 Ok(None) => {
       
   429                     // Race where the data file was deleted right after we
       
   430                     // read the docket, try again
       
   431                     return Err(race_error.into());
       
   432                 }
       
   433                 Err(e) => return Err(e.into()),
       
   434             }
   366         }?;
   435         }?;
   367 
   436 
   368         let write_mode_config = self
   437         let write_mode_config = self
   369             .config()
   438             .config()
   370             .get_str(b"devel", b"dirstate.v2.data_update_mode")
   439             .get_str(b"devel", b"dirstate.v2.data_update_mode")