mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-20 17:41:37 +00:00
Stop losing file changes on macOS when files are deleted (AI-assisted)
Vfs::commit_event calls StdBackend::unwatch for every removed path, but only the roots passed to watch() are watched. unwatch forwarded every path to notify anyway, and notify's FSEvents backend handles an unwatch by tearing down and restarting its event stream; whatever changed during the restart was never reported. Deleting files while others change (a git checkout, a branch switch) therefore dropped events, and the served tree went stale without any error. Only call the watcher's unwatch for paths that are actually watched. The new test deletes 100 files (calling unwatch on each, as commit_event does) while creating 100 others: before this change macOS reported 1 of the 100 creations, now all 100.
This commit is contained in:
parent
7e60406373
commit
1d2dc326a0
1 changed files with 57 additions and 2 deletions
|
|
@ -130,7 +130,62 @@ impl VfsBackend for StdBackend {
|
|||
}
|
||||
|
||||
fn unwatch(&mut self, path: &Path) -> io::Result<()> {
|
||||
self.watches.remove(path);
|
||||
self.watcher.unwatch(path).map_err(io::Error::other)
|
||||
// `Vfs::commit_event` calls this for every removed path, but only the roots passed to
|
||||
// `watch` are watched. Forwarding any other path to notify made its FSEvents backend on
|
||||
// macOS restart the event stream, and changes made during the restart were lost.
|
||||
if self.watches.remove(path) {
|
||||
self.watcher.unwatch(path).map_err(io::Error::other)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use std::time::Instant;
|
||||
|
||||
/// `Vfs::commit_event` calls `unwatch` for every removed path, though only the roots passed
|
||||
/// to `watch` are watched. Forwarding those paths to notify made its macOS (FSEvents) backend
|
||||
/// restart its event stream each time, and events that happened during a restart were never
|
||||
/// delivered: deleting files while others were being created lost some of the creations.
|
||||
#[test]
|
||||
fn unwatching_paths_that_were_never_watched_loses_no_events() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let root = dunce::canonicalize(dir.path()).unwrap();
|
||||
let (old, new) = (root.join("old"), root.join("new"));
|
||||
fs::create_dir(&old).unwrap();
|
||||
fs::create_dir(&new).unwrap();
|
||||
for i in 0..100 {
|
||||
fs::write(old.join(format!("{i}.txt")), "").unwrap();
|
||||
}
|
||||
|
||||
let mut backend = StdBackend::new().unwrap();
|
||||
backend.watch(&root).unwrap();
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
let events = backend.event_receiver();
|
||||
while events.try_recv().is_ok() {}
|
||||
|
||||
for i in 0..100 {
|
||||
let removed = old.join(format!("{i}.txt"));
|
||||
fs::remove_file(&removed).unwrap();
|
||||
let _ = backend.unwatch(&removed);
|
||||
fs::write(new.join(format!("{i}.txt")), "").unwrap();
|
||||
}
|
||||
|
||||
let mut created = HashSet::new();
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while created.len() < 100 && Instant::now() < deadline {
|
||||
if let Ok(VfsEvent::Create(path) | VfsEvent::Write(path)) =
|
||||
events.recv_timeout(Duration::from_millis(100))
|
||||
{
|
||||
if path.parent() == Some(new.as_path()) {
|
||||
created.insert(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(created.len(), 100, "file creations were not reported");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue