diff --git a/crates/memofs/src/lib.rs b/crates/memofs/src/lib.rs index 966534a2..35935f2f 100644 --- a/crates/memofs/src/lib.rs +++ b/crates/memofs/src/lib.rs @@ -43,6 +43,8 @@ pub struct PrefetchCache { /// `true` = file, `false` = directory. Paths not in the map fall through /// to the backend (e.g. init-file probes for paths that don't exist). pub is_file: HashMap, + /// Directory path -> sorted child paths. Consumed once per directory. + pub children: HashMap>, } mod sealed { @@ -216,6 +218,19 @@ impl VfsInner { fn read_dir>(&mut self, path: P) -> io::Result { let path = path.as_ref(); + + if let Some(cache) = &mut self.prefetch_cache { + if let Some(child_paths) = cache.children.remove(path) { + if self.watch_enabled { + self.backend.watch(path)?; + } + let inner = child_paths.into_iter().map(|p| Ok(DirEntry { path: p })); + return Ok(ReadDir { + inner: Box::new(inner), + }); + } + } + let dir = self.backend.read_dir(path)?; if self.watch_enabled { @@ -765,6 +780,7 @@ mod test { .map(|(k, v)| (PathBuf::from(k), PathBuf::from(v))) .collect(), is_file: HashMap::new(), + children: HashMap::new(), } } @@ -883,6 +899,7 @@ mod test { files: cache_files, canonical: HashMap::new(), is_file: HashMap::new(), + children: HashMap::new(), }); let result = vfs.read(&file_path).unwrap(); @@ -954,6 +971,7 @@ mod test { files: cache_files, canonical: HashMap::new(), is_file: HashMap::new(), + children: HashMap::new(), }); let handles: Vec<_> = (0..100) @@ -998,6 +1016,7 @@ mod test { files: cache_files, canonical, is_file: HashMap::new(), + children: HashMap::new(), }); for i in 0..50 { diff --git a/crates/memofs/src/std_backend.rs b/crates/memofs/src/std_backend.rs index 0a17df4f..04e78548 100644 --- a/crates/memofs/src/std_backend.rs +++ b/crates/memofs/src/std_backend.rs @@ -82,6 +82,11 @@ impl StdBackend { ); false // keep watcher running } + WatcherCriticalError::ChannelSendFailed(_) => { + // The event receiver was dropped (VFS shutdown). This is + // normal during process exit -- stop the thread silently. + true + } _ => { log::error!("{}. File watching is no longer reliable.", err); std::process::exit(1); diff --git a/src/serve_session.rs b/src/serve_session.rs index c990ac80..e8cdfbc3 100644 --- a/src/serve_session.rs +++ b/src/serve_session.rs @@ -142,6 +142,7 @@ fn prefetch_project_files(project: &Project) -> io::Result { files: HashMap::new(), canonical: HashMap::new(), is_file: HashMap::new(), + children: HashMap::new(), }); } @@ -214,15 +215,38 @@ fn prefetch_project_files(project: &Project) -> io::Result { canon_elapsed, ); - let is_file: HashMap<_, _> = entries - .iter() - .map(|e| (e.path().to_path_buf(), e.file_type().is_file())) - .collect(); + let mut is_file_map: HashMap = HashMap::new(); + let mut children_map: HashMap> = HashMap::new(); + + for entry in &entries { + let path = entry.path().to_path_buf(); + is_file_map.insert(path.clone(), entry.file_type().is_file()); + + if entry.depth() > 0 { + if let Some(parent) = entry.path().parent() { + children_map + .entry(parent.to_path_buf()) + .or_default() + .push(path); + } + } + } + + for children in children_map.values_mut() { + children.sort_by(|a, b| a.file_name().cmp(&b.file_name())); + } + + log::info!( + "Prefetch maps: {} is_file + {} children dirs built", + is_file_map.len(), + children_map.len(), + ); Ok(PrefetchCache { files: file_data.into_iter().collect::>(), canonical: canonical_data.into_iter().collect::>(), - is_file, + is_file: is_file_map, + children: children_map, }) } diff --git a/tests/tests/build.rs b/tests/tests/build.rs index 7568421e..62bef5fe 100644 --- a/tests/tests/build.rs +++ b/tests/tests/build.rs @@ -258,6 +258,7 @@ fn parallel_snapshot_with_prefetch_cache() { files, canonical, is_file: std::collections::HashMap::new(), + children: std::collections::HashMap::new(), }); let snap_cached = librojo::snapshot_from_vfs(&ctx, &vfs_cached, &project_path)