Layers: a document opened after a close keeps its thumbnail jobs

Closing a document told the worker to forget every row whose document was
not open at that moment. The worker keeps that test until it is idle, so
while a job still ran, a document opened afterward had its rows dropped
and they stayed without thumbnails. The worker now forgets only the
documents that closed.
This commit is contained in:
dotCooCoo
2026-11-09 00:25:58 +07:00
committed by bflatastic
parent cd8e98c6cd
commit 122b10143c
+35 -5
View File
@@ -266,18 +266,17 @@ impl Thumbs {
return;
}
let open: HashSet<u64> = uids.iter().copied().collect();
let closed = self.open_docs.as_ref().is_some_and(|before| before.iter().any(|uid| !open.contains(uid)));
let closed: HashSet<u64> = self.open_docs.as_ref().map(|before| before.difference(&open).copied().collect()).unwrap_or_default();
let stale =
self.entries.keys().chain(self.pending.keys()).any(|key| !open.contains(&key.0)) || self.showing.is_some_and(|uid| !open.contains(&uid));
self.entries.retain(|key, _| open.contains(&key.0));
self.pending.retain(|key, _| open.contains(&key.0));
self.showing = self.showing.filter(|uid| open.contains(uid));
if closed {
if !closed.is_empty() {
// Images finished before now are dropped with the rest of the queue: the open
// documents' rows are asked for again.
// documents' rows are asked for again. Documents opened later keep their jobs.
if let Some(w) = &self.worker {
let open = open.clone();
w.forget(move |key| !open.contains(&key.0));
w.forget(move |key| closed.contains(&key.0));
if w.poll().is_none() {
self.worker = None;
}
@@ -536,6 +535,37 @@ mod tests {
assert_eq!(done.key.0, next, "no worker keeps the closed document's image");
}
#[test]
fn a_document_opened_while_a_job_runs_after_a_close_gets_its_thumbnails() {
let ctx = egui::Context::default();
let Some(worker) = ThumbWorker::start(&ctx, "test") else { return };
let mut thumbs = Thumbs { worker: Some(worker), worker_started: true, ..Default::default() };
thumbs.retain_open(&[1, 2]);
// A job of document 2 is running.
let (started, running) = std::sync::mpsc::channel::<()>();
let (release, held) = std::sync::mpsc::channel::<()>();
let blocker = (2, NodeId(1));
let job: crate::thumb_worker::JobFn = Box::new(move |_| {
started.send(()).ok();
held.recv().ok();
None
});
assert!(thumbs.worker.as_ref().is_some_and(|w| w.submit(blocker, (blocker, Weak::new(), 1), job)));
running.recv().expect("the job starts");
// Document 1 closes, then document 3 opens and one of its rows is queued.
thumbs.retain_open(&[2]);
thumbs.retain_open(&[2, 3]);
let opened = (3, NodeId(2));
assert!(thumbs.worker.as_ref().is_some_and(|w| w.submit(opened, (opened, Weak::new(), 1), Box::new(|_| None))));
// A job of document 2 queued after it marks the end of the queue.
let last = (2, NodeId(3));
assert!(thumbs.worker.as_ref().is_some_and(|w| w.submit(last, (last, Weak::new(), 1), Box::new(|_| None))));
release.send(()).ok();
let worker = thumbs.worker.as_ref().expect("the worker is kept");
let order: Vec<(u64, NodeId)> = (0..3).map_while(|_| worker.wait().map(|d| d.key.0)).take_while(|key| *key != last).collect();
assert_eq!(order, [blocker, opened], "document 3's job comes back before the last one");
}
/// An open zigzag of `anchors` anchors.
fn line(anchors: usize) -> PathData {
let pts: Vec<Point> = (0..anchors).map(|i| Point::new(i as f64 * 0.01, (i % 2) as f64 * 50.0)).collect();