Skip to content

Commit 0d594da

Browse files
committed
fix(tarball): use rwlock instead of dashmap to avoid potential deadlock
1 parent 078359e commit 0d594da

File tree

5 files changed

+9
-23
lines changed

5 files changed

+9
-23
lines changed

Cargo.lock

-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ async-recursion = { version = "1.0.5" }
3131
clap = { version = "4", features = ["derive", "string"] }
3232
command-extra = { version = "1.0.0" }
3333
base64 = { version = "0.21.5" }
34-
dashmap = { version = "5.5.3" }
3534
derive_more = { version = "1.0.0-beta.3", features = ["full"] }
3635
dunce = { version = "1.0.4" }
3736
home = { version = "0.5.5" }

crates/cli/src/state.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use pacquet_package_manifest::{PackageManifest, PackageManifestError};
66
use pacquet_tarball::MemCache;
77
use pipe_trait::Pipe;
88
use reqwest::Client;
9+
use std::collections::HashMap;
910
use std::path::PathBuf;
1011

1112
/// Application state when running `pacquet run` or `pacquet install`.
@@ -44,7 +45,7 @@ impl State {
4445
lockfile: call_load_lockfile(config.lockfile, Lockfile::load_from_current_dir)
4546
.map_err(InitStateError::LoadLockfile)?,
4647
http_client: Client::new(),
47-
tarball_mem_cache: MemCache::new(),
48+
tarball_mem_cache: MemCache::new(HashMap::new()),
4849
})
4950
}
5051
}

crates/tarball/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ pacquet-fs = { workspace = true }
1616
pacquet-store-dir = { workspace = true }
1717

1818
base64 = { workspace = true }
19-
dashmap = { workspace = true }
2019
derive_more = { workspace = true }
2120
miette = { workspace = true }
2221
pipe-trait = { workspace = true }

crates/tarball/src/lib.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use std::{
55
path::PathBuf,
66
sync::Arc,
77
time::UNIX_EPOCH,
8+
mem::drop,
89
};
910

1011
use base64::{engine::general_purpose::STANDARD as BASE64_STD, Engine};
11-
use dashmap::DashMap;
1212
use derive_more::{Display, Error, From};
1313
use miette::Diagnostic;
1414
use pacquet_fs::file_mode;
@@ -88,7 +88,7 @@ pub enum CacheValue {
8888
/// Internal in-memory cache of tarballs.
8989
///
9090
/// The key of this hashmap is the url of each tarball.
91-
pub type MemCache = DashMap<String, Arc<RwLock<CacheValue>>>;
91+
pub type MemCache = RwLock<HashMap<String, Arc<RwLock<CacheValue>>>>;
9292

9393
#[instrument(skip(gz_data), fields(gz_data_len = gz_data.len()))]
9494
fn decompress_gzip(gz_data: &[u8], unpacked_size: Option<usize>) -> Result<Vec<u8>, TarballError> {
@@ -130,9 +130,9 @@ impl<'a> DownloadTarballToStore<'a> {
130130

131131
// QUESTION: I see no copying from existing store_dir, is there such mechanism?
132132
// TODO: If it's not implemented yet, implement it
133-
134-
if let Some(cache_lock) = mem_cache.get(package_url) {
135-
let notify = match &*cache_lock.write().await {
133+
let mem_cache_reader = mem_cache.read().await;
134+
if let Some(cache_lock) = mem_cache_reader.get(package_url) {
135+
let notify = match &*cache_lock.read().await {
136136
CacheValue::Available(cas_paths) => {
137137
return Ok(Arc::clone(cas_paths));
138138
}
@@ -146,13 +146,14 @@ impl<'a> DownloadTarballToStore<'a> {
146146
}
147147
unreachable!("Failed to get or compute tarball data for {package_url:?}");
148148
} else {
149+
drop(mem_cache_reader);
149150
let notify = Arc::new(Notify::new());
150151
let cache_lock = notify
151152
.pipe_ref(Arc::clone)
152153
.pipe(CacheValue::InProgress)
153154
.pipe(RwLock::new)
154155
.pipe(Arc::new);
155-
if mem_cache.insert(package_url.to_string(), Arc::clone(&cache_lock)).is_some() {
156+
if mem_cache.write().await.insert(package_url.to_string(), Arc::clone(&cache_lock)).is_some() {
156157
tracing::warn!(target: "pacquet::download", ?package_url, "Race condition detected when writing to cache");
157158
}
158159
let cas_paths = self.run_without_mem_cache().await?.pipe(Arc::new);

0 commit comments

Comments
 (0)