From 7ca7b15c8a5a2aaad62d55339c468afc8119a1f4 Mon Sep 17 00:00:00 2001 From: DarkBreakpoint <243206744+DarkBreakpoint@users.noreply.github.com> Date: Fri, 14 Nov 2025 08:33:29 -0600 Subject: [PATCH 1/4] Avoid blocking the runtime when warming cache --- backend/src/api/model/app_state.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/src/api/model/app_state.rs b/backend/src/api/model/app_state.rs index 7287862d7..d4bb28321 100644 --- a/backend/src/api/model/app_state.rs +++ b/backend/src/api/model/app_state.rs @@ -20,6 +20,7 @@ use std::sync::atomic::AtomicI8; use std::sync::Arc; use std::time::Duration; use tokio::sync::Mutex; +use tokio::task; use tokio_util::sync::CancellationToken; use crate::repository::storage::get_geoip_path; use crate::utils::GeoIp; @@ -210,8 +211,11 @@ pub fn create_cache(config: &Config) -> Option>> { let cache = Arc::new(Mutex::new(res_cache)); let cache_scanner = Arc::clone(&cache); tokio::spawn(async move { - let mut c = cache_scanner.lock().await; - if let Err(err) = (*c).scan() { + let scan_result = { + let mut cache = cache_scanner.lock().await; + task::block_in_place(|| cache.scan()) + }; + if let Err(err) = scan_result { error!("Failed to scan cache {err}"); } }); @@ -485,4 +489,4 @@ pub struct HdHomerunAppState { pub app_state: Arc, pub device: Arc, pub hd_scan_state: Arc, -} \ No newline at end of file +} From c7a48e0d3db24b05b7f20ce0000d0fddd03188b7 Mon Sep 17 00:00:00 2001 From: DarkBreakpoint <243206744+DarkBreakpoint@users.noreply.github.com> Date: Fri, 14 Nov 2025 08:38:38 -0600 Subject: [PATCH 2/4] =?UTF-8?q?=20FileLockManager=20previously=20keyed=20l?= =?UTF-8?q?ocks=20by=20the=20raw=20PathBuf=20supplied=20by=20callers,=20so?= =?UTF-8?q?=20the=20same=20file=20accessed=20via=20different=20relative=20?= =?UTF-8?q?paths=20or=20symlinks=20acquired=20distinct=20locks=E2=80=94all?= =?UTF-8?q?owing=20concurrent=20writers=20and=20risking=20corruption.=20By?= =?UTF-8?q?=20canonicalizing/cleaning=20the=20path=20before=20indexing,=20?= =?UTF-8?q?all=20callers=20now=20share=20the=20same=20mutex=20and=20mutual?= =?UTF-8?q?=20exclusion=20is=20preserved.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/utils/file/file_lock_manager.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/backend/src/utils/file/file_lock_manager.rs b/backend/src/utils/file/file_lock_manager.rs index f604641f5..30e23d567 100644 --- a/backend/src/utils/file/file_lock_manager.rs +++ b/backend/src/utils/file/file_lock_manager.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; +use std::env; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::{fmt, io}; use tokio::sync::{Mutex, RwLock}; use tokio::sync::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard}; use shared::error::str_to_io_error; +use path_clean::PathClean; #[derive(Clone)] pub struct FileLockManager { @@ -44,14 +46,15 @@ impl FileLockManager { // Helper function: retrieves or creates a lock for a file. async fn get_or_create_lock(&self, path: &Path) -> Arc> { + let normalized_path = normalize_path(path); let mut locks = self.locks.lock().await; - if let Some(lock) = locks.get(path) { + if let Some(lock) = locks.get(&normalized_path) { return lock.clone(); } let file_lock = Arc::new(RwLock::new(())); - locks.insert(path.to_path_buf(), file_lock.clone()); + locks.insert(normalized_path, file_lock.clone()); drop(locks); file_lock } @@ -94,3 +97,17 @@ impl FileWriteGuard { Self { _guard: guard } } } + +fn normalize_path(path: &Path) -> PathBuf { + if let Ok(canonical) = path.canonicalize() { + return canonical; + } + + let base = if path.is_absolute() { + path.to_path_buf() + } else { + env::current_dir().unwrap_or_else(|_| PathBuf::from("./")).join(path) + }; + + base.clean() +} From 3e91e218e5cf702c40dcc250f96f576328a879fe Mon Sep 17 00:00:00 2001 From: euzu Date: Fri, 14 Nov 2025 15:53:10 +0100 Subject: [PATCH 3/4] dev build 3.2.1 --- CHANGELOG.md | 5 ++++- Cargo.lock | 23 ++++++++--------------- backend/Cargo.toml | 2 +- frontend/Cargo.toml | 4 ++-- shared/Cargo.toml | 2 +- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 978520af9..cbf82e914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog -# 3.1.9 (2025-11-xx) +# 3.3.0 (2025-11-xx) +- Avoid blocking the runtime when warming cache + +# 3.2.0 (2025-11-14) - Added `name` attribute to Staged Input. - Real-time active provider connection monitoring (dashboard + websocket) - Source editor: block selection, batch-mode UI and automatic layout diff --git a/Cargo.lock b/Cargo.lock index ba233aafb..324362a07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1102,7 +1102,7 @@ dependencies = [ [[package]] name = "frontend" -version = "3.1.8" +version = "3.2.1" dependencies = [ "anyhow", "base64", @@ -1114,9 +1114,9 @@ dependencies = [ "futures-signals", "gloo-render 0.2.0", "gloo-storage 0.3.0", - "gloo-timers 0.3.0", + "gloo-timers 0.2.6", "gloo-utils 0.2.0", - "implicit-clone 0.6.0", + "implicit-clone", "js-sys", "log", "prost", @@ -1627,6 +1627,8 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ + "futures-channel", + "futures-core", "js-sys", "wasm-bindgen", ] @@ -2158,15 +2160,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "implicit-clone" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1689b939ee35e3a075b0834b5672efd43aec8a6e81a1c6002b76a5ca2f211ae0" -dependencies = [ - "implicit-clone-derive", -] - [[package]] name = "implicit-clone-derive" version = "0.1.2" @@ -3789,7 +3782,7 @@ dependencies = [ [[package]] name = "shared" -version = "3.1.8" +version = "3.3.0" dependencies = [ "base64", "bitflags 2.10.0", @@ -4338,7 +4331,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tuliprox" -version = "3.1.8" +version = "3.2.1" dependencies = [ "arc-swap", "async-compression", @@ -5055,7 +5048,7 @@ dependencies = [ "console_error_panic_hook", "futures", "gloo 0.10.0", - "implicit-clone 0.4.9", + "implicit-clone", "indexmap", "js-sys", "prokio", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 65e343c38..8e8b975de 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tuliprox" -version = "3.1.8" +version = "3.2.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 0529548c2..60c334277 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "frontend" -version = "3.1.8" +version = "3.2.1" edition = "2021" [dependencies] -shared = { version = "3.1.8", path = "../shared" } +shared = { version = "3.3.0", path = "../shared" } chrono = "0" yew = "0.21" yew-router = "0.18" diff --git a/shared/Cargo.toml b/shared/Cargo.toml index e93768d7c..e94bc1039 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shared" -version = "3.1.8" +version = "3.2.1" edition = "2021" [dependencies] From a74eb2ca3248211f3631891d92bc98731d0e70ad Mon Sep 17 00:00:00 2001 From: euzu Date: Fri, 14 Nov 2025 16:12:37 +0100 Subject: [PATCH 4/4] Dark Breakpoint changes cahcceh warmup file lock normalize --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96bd2376b..da124823e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog # 3.3.0 (2025-11-xx) - Avoid blocking the runtime when warming cache +- Normalize FileLockManager paths so aliases share the same lock # 3.2.0 (2025-11-14) - Added `name` attribute to Staged Input.