From 15e40461e545a70871e21bcd1e698748abd523ed Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 10 Apr 2026 06:08:21 +0200 Subject: [PATCH] fix: do not generate thumbnail if file is being written --- src/operation/mod.rs | 3 ++ src/operation/notifiers.rs | 58 ++++++++++++++++++++++++++++++++++++++ src/operation/recursive.rs | 2 ++ src/tab.rs | 4 +++ 4 files changed, 67 insertions(+) create mode 100644 src/operation/notifiers.rs diff --git a/src/operation/mod.rs b/src/operation/mod.rs index da4a83b..1daed41 100644 --- a/src/operation/mod.rs +++ b/src/operation/mod.rs @@ -22,6 +22,9 @@ use zip::AesMode::Aes256; pub use self::controller::{Controller, ControllerState}; pub mod controller; +pub use notifiers::*; +mod notifiers; + pub use self::reader::OpReader; pub mod reader; diff --git a/src/operation/notifiers.rs b/src/operation/notifiers.rs new file mode 100644 index 0000000..0e27ce9 --- /dev/null +++ b/src/operation/notifiers.rs @@ -0,0 +1,58 @@ +// Copyright 2026 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use std::path::{Path, PathBuf}; +use std::sync::{Arc, LazyLock, Mutex}; +use tokio::sync::Notify; + +/// Monitor files which are being written to. +pub struct FileWritingNotifier { + data: Vec, + notify: Arc, +} + +static ACTIVELY_WRITING: LazyLock> = LazyLock::new(|| { + Mutex::new(FileWritingNotifier { + data: Vec::new(), + notify: Arc::new(Notify::new()), + }) +}); + +/// Append path that is being written to. +pub fn actively_writing_add(path: PathBuf) { + ACTIVELY_WRITING.lock().unwrap().data.push(path); +} + +/// Remove path to file that has finished writing and notify waiters. +pub fn actively_writing_remove(path: &Path) { + let mut guard = ACTIVELY_WRITING.lock().unwrap(); + guard.data.retain(|p| p != path); + guard.notify.notify_waiters(); +} + +/// Wait until the actively-writing queue is empty or a file has been removed. +pub async fn actively_writing_tick() { + let notify = (|| { + let guard = ACTIVELY_WRITING.lock().unwrap(); + + if !guard.data.is_empty() { + return Some(guard.notify.clone()); + } + + None + })(); + + if let Some(notify) = notify { + notify.notified().await + } +} + +/// Check if a file is being written to. Avoid thumbnail generation until after it is finished. +pub fn is_actively_writing_to(path: &Path) -> bool { + ACTIVELY_WRITING + .lock() + .unwrap() + .data + .iter() + .any(|p| p == path) +} diff --git a/src/operation/recursive.rs b/src/operation/recursive.rs index bbe89d1..a38ea74 100644 --- a/src/operation/recursive.rs +++ b/src/operation/recursive.rs @@ -334,12 +334,14 @@ impl Op { } match self.kind { OpKind::Copy => { + crate::operation::actively_writing_add(self.to.clone()); let result = self.copy(ctx, progress).await; if result.is_err() { _ = compio::fs::remove_file(&self.to).await; } + crate::operation::actively_writing_remove(&self.to); return result; } OpKind::Move { cross_device_copy } => { diff --git a/src/tab.rs b/src/tab.rs index a0f95a0..9d9d2a2 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -6771,6 +6771,10 @@ impl Tab { stream::channel( 1, move |mut output: futures::channel::mpsc::Sender<_>| async move { + while crate::operation::is_actively_writing_to(&path) { + crate::operation::actively_writing_tick().await; + } + let message = { let path = path.clone();