mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-03 00:39:44 -06:00
Recovery: wait for the write in flight before an emergency save and before removing copies (#202)
This commit is contained in:
@@ -125,13 +125,21 @@ impl Recovery {
|
||||
let path = self.dir.join(format!("{}.figcraft", d.uid));
|
||||
let note = path.with_extension(ONLINE);
|
||||
if !needs_copy(d) {
|
||||
if self.written.remove(&d.uid).is_some() {
|
||||
if let Some((_, writer)) = self.written.remove(&d.uid) {
|
||||
// A write still running would bring the copy back after it was removed.
|
||||
if let Some(h) = writer {
|
||||
let _ = h.join();
|
||||
}
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let _ = std::fs::remove_file(¬e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let entry = self.written.entry(d.uid).or_insert((0, None));
|
||||
// An emergency save must copy the newest revision: let the write in flight finish first.
|
||||
if wait && let Some(h) = entry.1.take() {
|
||||
let _ = h.join();
|
||||
}
|
||||
let busy = entry.1.as_ref().is_some_and(|h| !h.is_finished());
|
||||
if entry.0 == d.revision || busy {
|
||||
continue;
|
||||
@@ -162,9 +170,12 @@ impl Recovery {
|
||||
// Closed documents.
|
||||
let open: Vec<u64> = s.docs.iter().map(|d| d.uid).collect();
|
||||
let dir = &self.dir;
|
||||
self.written.retain(|uid, _| {
|
||||
self.written.retain(|uid, w| {
|
||||
let keep = open.contains(uid);
|
||||
if !keep {
|
||||
if let Some(h) = w.1.take() {
|
||||
let _ = h.join();
|
||||
}
|
||||
let _ = std::fs::remove_file(dir.join(format!("{uid}.figcraft")));
|
||||
let _ = std::fs::remove_file(dir.join(format!("{uid}.{ONLINE}")));
|
||||
}
|
||||
@@ -172,6 +183,12 @@ impl Recovery {
|
||||
});
|
||||
}
|
||||
|
||||
/// Test hook: pretend a write of `uid` at `revision` is in flight in `writer`.
|
||||
#[cfg(test)]
|
||||
pub fn hold(&mut self, uid: u64, revision: u64, writer: JoinHandle<()>) {
|
||||
self.written.insert(uid, (revision, Some(writer)));
|
||||
}
|
||||
|
||||
/// A normal quit: the documents were saved or their changes discarded on purpose. (Earlier
|
||||
/// runs' copies still waiting for their outcome stay for the next start.)
|
||||
pub fn finish(self) {
|
||||
|
||||
@@ -62,3 +62,48 @@ fn copies_follow_saves_and_a_normal_quit_leaves_nothing() {
|
||||
assert!(!dir.exists(), "a normal quit removes the copies");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
fn slow_write(path: std::path::PathBuf) -> std::thread::JoinHandle<()> {
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_millis(150));
|
||||
let _ = std::fs::write(path, b"stale");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saving_or_closing_waits_for_a_slow_write_so_the_copy_does_not_come_back() {
|
||||
let root = root("slow-cleanup");
|
||||
let mut s = edited();
|
||||
let mut r = Recovery::start(&root).unwrap();
|
||||
let dir = std::fs::read_dir(&root).unwrap().flatten().next().unwrap().path();
|
||||
let uid = s.docs[0].uid;
|
||||
let copy = dir.join(format!("{uid}.figcraft"));
|
||||
r.hold(uid, 1, slow_write(copy.clone()));
|
||||
s.ds_mut().unwrap().mark_saved();
|
||||
r.write(&s, false);
|
||||
std::thread::sleep(std::time::Duration::from_millis(400));
|
||||
assert!(!copy.exists(), "a saved document's copy stays removed");
|
||||
// Closed: the same.
|
||||
let s = edited();
|
||||
let uid = s.docs[0].uid;
|
||||
let copy = dir.join(format!("{uid}.figcraft"));
|
||||
r.hold(uid, 1, slow_write(copy.clone()));
|
||||
r.write(&Session::empty(), false);
|
||||
std::thread::sleep(std::time::Duration::from_millis(400));
|
||||
assert!(!copy.exists(), "a closed document's copy stays removed");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_emergency_save_waits_for_the_write_in_flight_and_copies_the_newest_revision() {
|
||||
let root = root("slow-emergency");
|
||||
let s = edited();
|
||||
let mut r = Recovery::start(&root).unwrap();
|
||||
let dir = std::fs::read_dir(&root).unwrap().flatten().next().unwrap().path();
|
||||
let uid = s.docs[0].uid;
|
||||
let copy = dir.join(format!("{uid}.figcraft"));
|
||||
r.hold(uid, 0, slow_write(copy.clone()));
|
||||
r.write(&s, true);
|
||||
assert!(std::fs::read(©).is_ok_and(|b| b != b"stale"), "the newest revision replaced the slow write");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user