From fb97405e0c47dadf656e5c92c76ddaff95d78222 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 22 Jun 2026 16:36:50 +0400 Subject: fix: decode payloads outside resource lock --- crates/fparkan-resource/src/lib.rs | 59 ++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 19 deletions(-) (limited to 'crates/fparkan-resource/src') diff --git a/crates/fparkan-resource/src/lib.rs b/crates/fparkan-resource/src/lib.rs index 0ab6b80..b09c946 100644 --- a/crates/fparkan-resource/src/lib.rs +++ b/crates/fparkan-resource/src/lib.rs @@ -210,7 +210,7 @@ struct ArchiveSlot { fingerprint: Sha256Digest, generation: u64, kind: ArchiveKind, - document: ArchiveDocument, + document: Arc, } enum ArchiveDocument { @@ -218,6 +218,11 @@ enum ArchiveDocument { Rsli(fparkan_rsli::RsliDocument), } +struct PayloadDecodeTask { + document: Arc, + key: ResourceKey, +} + #[derive(Debug, Default)] struct DecodedPayloadCache { max_entries: usize, @@ -320,7 +325,7 @@ impl ResourceRepository for CachedResourceRepository { ) -> Result, ResourceError> { let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let slot = state.archive(archive)?; - let local = match &slot.document { + let local = match slot.document.as_ref() { ArchiveDocument::Nres(document) => document.find_bytes(&name.0).map(|id| id.0), ArchiveDocument::Rsli(document) => document.find_bytes(&name.0).map(|id| id.0), }; @@ -334,7 +339,7 @@ impl ResourceRepository for CachedResourceRepository { fn first_entry(&self, archive: ArchiveId) -> Result, ResourceError> { let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let slot = state.archive(archive)?; - let local = match &slot.document { + let local = match slot.document.as_ref() { ArchiveDocument::Nres(document) => document.entries().first().map(|entry| entry.id().0), ArchiveDocument::Rsli(document) => document.entry(fparkan_rsli::EntryId(0)).map(|_| 0), }; @@ -346,21 +351,27 @@ impl ResourceRepository for CachedResourceRepository { } fn read(&self, entry: EntryHandle) -> Result { + let task = { + let mut state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; + if let Some(bytes) = state.payload_cache.get(entry) { + return Ok(ResourceBytes::Shared(bytes)); + } + state.payload_decode_task(entry)? + }; + let payload = + task.document + .read_payload(entry.local) + .map_err(|source| ResourceError::EntryRead { + key: task.key, + source, + })?; + let shared = Arc::from(payload.into_boxed_slice()); + let mut state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; if let Some(bytes) = state.payload_cache.get(entry) { return Ok(ResourceBytes::Shared(bytes)); } - - let payload = { - let slot = state.entry_archive(entry)?; - let key = slot.entry_key(entry.local)?; - slot.read_payload(entry.local) - .map_err(|source| ResourceError::EntryRead { - key: key.clone(), - source, - })? - }; - let shared = Arc::from(payload.into_boxed_slice()); + state.entry_archive(entry)?; state.payload_cache.insert(entry, Arc::clone(&shared)); Ok(ResourceBytes::Shared(shared)) } @@ -368,7 +379,7 @@ impl ResourceRepository for CachedResourceRepository { fn entry_info(&self, entry: EntryHandle) -> Result { let state = self.state.lock().map_err(|_| ResourceError::Poisoned)?; let slot = state.entry_archive(entry)?; - match &slot.document { + match slot.document.as_ref() { ArchiveDocument::Nres(document) => { let local = usize::try_from(entry.local).map_err(|_| ResourceError::InvalidHandle)?; @@ -512,11 +523,19 @@ impl RepositoryState { } Ok(slot) } + + fn payload_decode_task(&self, entry: EntryHandle) -> Result { + let slot = self.entry_archive(entry)?; + Ok(PayloadDecodeTask { + document: Arc::clone(&slot.document), + key: slot.entry_key(entry.local)?, + }) + } } impl ArchiveSlot { fn entry_key(&self, local: u32) -> Result { - match &self.document { + match self.document.as_ref() { ArchiveDocument::Nres(document) => { let local = usize::try_from(local).map_err(|_| ResourceError::InvalidHandle)?; let entry = document @@ -541,9 +560,11 @@ impl ArchiveSlot { } } } +} +impl ArchiveDocument { fn read_payload(&self, local: u32) -> Result, String> { - match &self.document { + match self { ArchiveDocument::Nres(document) => document .payload(fparkan_nres::EntryId(local)) .map(<[u8]>::to_vec) @@ -568,7 +589,7 @@ fn decode_archive( fingerprint, generation: 0, kind: ArchiveKind::Nres, - document: ArchiveDocument::Nres(document), + document: Arc::new(ArchiveDocument::Nres(document)), }); } if bytes.get(0..4) == Some(b"NL\0\x01") { @@ -579,7 +600,7 @@ fn decode_archive( fingerprint, generation: 0, kind: ArchiveKind::Rsli, - document: ArchiveDocument::Rsli(document), + document: Arc::new(ArchiveDocument::Rsli(document)), }); } Err(ResourceError::Format( -- cgit v1.2.3