Skip to content

Commit

Permalink
fix: multi-process safe parallel filesystem capabilities probing (#1373)
Browse files Browse the repository at this point in the history
This is achieved by making filenames unique so they won't clash.
  • Loading branch information
Byron committed May 21, 2024
1 parent 00a1c47 commit bec648d
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 14 deletions.
22 changes: 12 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions gix-fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ gix-features = { version = "^0.38.1", path = "../gix-features", features = ["fs-
gix-utils = { version = "^0.1.12", path = "../gix-utils" }
serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"] }

# For `Capabilities` to assure parallel operation works.
fastrand = { version = "2.1.0", default-features = false, features = ["std"] }

[dev-dependencies]
tempfile = "3.5.0"
crossbeam-channel = "0.5.0"
11 changes: 7 additions & 4 deletions gix-fs/src/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ impl Capabilities {
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};

// test it exactly as we typically create executable files, not using chmod.
let test_path = root.join("_test_executable_bit");
let rand = fastrand::usize(..);
let test_path = root.join(format!("_test_executable_bit{rand}"));
let res = std::fs::OpenOptions::new()
.create_new(true)
.write(true)
Expand All @@ -87,8 +88,9 @@ impl Capabilities {
}

fn probe_precompose_unicode(root: &Path) -> std::io::Result<bool> {
let precomposed = "ä";
let decomposed = "a\u{308}";
let rand = fastrand::usize(..);
let precomposed = format!("ä{rand}");
let decomposed = format!("a\u{308}{rand}");

let precomposed = root.join(precomposed);
std::fs::OpenOptions::new()
Expand All @@ -101,7 +103,8 @@ impl Capabilities {
}

fn probe_symlink(root: &Path) -> std::io::Result<bool> {
let link_path = root.join("__file_link");
let rand = fastrand::usize(..);
let link_path = root.join(format!("__file_link{rand}"));
if crate::symlink::create("dangling".as_ref(), &link_path).is_err() {
return Ok(false);
}
Expand Down
26 changes: 26 additions & 0 deletions gix-fs/tests/capabilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,29 @@ fn probe() {
assert!(caps.executable_bit, "Unix should always honor executable bits");
}
}

#[test]
fn parallel_probe() {
let dir = tempfile::tempdir().unwrap();
std::fs::File::create(dir.path().join("config")).unwrap();
let baseline = gix_fs::Capabilities::probe(dir.path());

let (tx, rx) = crossbeam_channel::unbounded::<()>();
let threads: Vec<_> = (0..10)
.map(|_id| {
std::thread::spawn({
let dir = dir.path().to_owned();
let rx = rx.clone();
move || {
for _ in rx {}
let actual = gix_fs::Capabilities::probe(&dir);
assert_eq!(actual, baseline);
}
})
})
.collect();
drop((rx, tx));
for thread in threads {
thread.join().expect("no panic");
}
}

0 comments on commit bec648d

Please sign in to comment.