From 95c798954c6cdf73e0505d4cdd8d4cd0121a4287 Mon Sep 17 00:00:00 2001 From: Serkyo Date: Tue, 28 Jul 2026 00:22:37 +0200 Subject: [PATCH] perf(server): generate chunk baselines outside the cache lock --- crates/server/src/chunk_cache.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/crates/server/src/chunk_cache.rs b/crates/server/src/chunk_cache.rs index 89d752e..e555630 100644 --- a/crates/server/src/chunk_cache.rs +++ b/crates/server/src/chunk_cache.rs @@ -26,18 +26,30 @@ impl ChunkCache { } /// Returns the baseline for `pos`, generating and caching it on a miss. + /// + /// Generation runs outside the lock, so concurrent callers do not serialise on a cache miss. Two callers racing on the same position may each generate a baseline; generation is deterministic and side-effect-free, so the duplicated work is redundant rather than incorrect, and is far cheaper than serialising every miss behind the store. #[must_use] pub fn get_or_generate(&self, pos: ChunkPos, generator: &VoxelGenerator) -> Chunk { - // A poisoned lock cannot yield a usable cache; recovering the guard lets generation proceed rather than propagating a panic across every worker that shares this cache. - let mut guard = self - .inner - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(hit) = guard.get(&pos) { - return hit.clone(); + // Probe under the lock, then release it before generating. + { + // A poisoned lock cannot yield a usable cache; recovering the guard lets generation proceed rather than propagating a panic across every worker that shares this cache. + let mut guard = self + .inner + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(hit) = guard.get(&pos) { + return hit.clone(); + } } + let chunk = generator.generate_chunk(pos); - guard.put(pos, chunk.clone()); + + // Retake the lock only to publish. A concurrent caller may have inserted the same position in the meantime; overwriting is harmless because both baselines are identical. + self.inner + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .put(pos, chunk.clone()); + chunk } }