Autosave ids are unique within the process: an app restarted in the same second (the scenario harness crashes and restarts in one process) took the id {seconds}-{pid} of the crashed app, locked its entry again and so offered nothing to recover. The id now carries a per-process sequence number. Test: a restart in the same second still offers the crashed app's work

This commit is contained in:
CypherNaugh_0x authored and bherbruck committed 2026-10-10 13:24:08 -04:00
1 parent 524ef6b8c2
commit 4a41de72ec
1 file changed
+20 -1
+20 -1
View File
@@ -140,7 +140,11 @@ impl Autosaver {
let _ = fs::remove_file(&p);
}
}
let id = format!("{}-{}", unix_now(), std::process::id());
// Unique within the process too: an app started again in the same second (tests that
// "crash" and restart) must not take over the crashed app's entry.
static SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let seq = SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let id = format!("{}-{}-{seq}", unix_now(), std::process::id());
let lock = OpenOptions::new().create(true).truncate(false).write(true).open(file(dir, &id, "lock")).map_err(io)?;
lock.lock().map_err(io)?;
Ok(Autosaver { dir: dir.to_path_buf(), id, interval, lock: Some(lock), last: None, last_at: Instant::now(), has_entry: false })
@@ -225,6 +229,21 @@ mod tests {
let _ = fs::remove_dir_all(&d);
}
#[test]
fn an_app_restarted_in_the_same_second_still_offers_the_crashed_ones_work() {
let d = dir("restart");
let mut s = Session::default();
let mut a = Autosaver::new(&d, Duration::from_secs(3600)).unwrap();
s.execute("solid.box", &json!({"length": 10, "width": 10, "height": 10})).unwrap();
assert!(a.save(&s).unwrap());
drop(a);
let b = Autosaver::new(&d, Duration::from_secs(3600)).unwrap();
let found = orphans(&d);
assert_eq!(found.len(), 1, "the crashed app's entry is offered");
assert_ne!(found[0].id, b.id);
let _ = fs::remove_dir_all(&d);
}
#[test]
fn saving_drops_the_entry_and_a_clean_close_leaves_nothing() {
let d = dir("clean");