From e588d058ec3d13501edd0b4a3ac86604934c78c5 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Thu, 30 Jan 2025 15:47:05 +0100 Subject: fix cache deadlock --- base/src/cache.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'base') diff --git a/base/src/cache.rs b/base/src/cache.rs index 7888042..e2b540b 100644 --- a/base/src/cache.rs +++ b/base/src/cache.rs @@ -18,7 +18,7 @@ use std::{ io::Seek, path::PathBuf, sync::{ - atomic::{AtomicUsize, Ordering}, + atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, LazyLock, RwLock, }, time::Instant, @@ -92,13 +92,23 @@ where Ok(location) } +thread_local! { pub static WITHIN_CACHE_FILE: AtomicBool = AtomicBool::new(false); } + pub fn cache_file(seed: &[&str], mut generate: Fun) -> Result where Fun: FnMut(std::fs::File) -> Result<(), anyhow::Error>, { let (bucket, location) = cache_location(seed); // we need a lock even if it exists since somebody might be still in the process of writing. - let _guard = CACHE_GENERATION_LOCKS[bucket % CACHE_GENERATION_BUCKET_COUNT].blocking_lock(); + let already_within = WITHIN_CACHE_FILE.with(|a| a.swap(true, Ordering::Relaxed)); + let _guard = if already_within { + // TODO stupid hack to avoid deadlock for nested cache_file. proper solution needed + CACHE_GENERATION_LOCKS[bucket % CACHE_GENERATION_BUCKET_COUNT] + .try_lock() + .ok() + } else { + Some(CACHE_GENERATION_LOCKS[bucket % CACHE_GENERATION_BUCKET_COUNT].blocking_lock()) + }; if !location.abs().exists() { let temp_path = CONF.cache_path.join(format!("temp-{:x}", random::())); let f = std::fs::File::create(&temp_path).context("creating new cache file")?; @@ -112,6 +122,9 @@ where } rename(temp_path, location.abs()).context("rename cache")?; } + if !already_within { + WITHIN_CACHE_FILE.with(|a| a.swap(false, Ordering::Relaxed)); + } drop(_guard); Ok(location) } -- cgit v1.2.3-70-g09d2