Skip to content

Commit

Permalink
wasm-bindgen-test-runner: Added a lock based handler to prevent tmpdi…
Browse files Browse the repository at this point in the history
…r concurrency conflict.
  • Loading branch information
spigaz committed Apr 22, 2024
1 parent b34dd33 commit 926432b
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 40 deletions.
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bin-dir = "wasm-bindgen-{ version }-{ target }/{ bin }{ binary-ext }"
docopt = "1.0"
env_logger = "0.8"
anyhow = "1.0"
fs2 = "0.4.3"
log = "0.4"
native-tls = { version = "0.2", default-features = false, optional = true }
rouille = { version = "3.0.0", default-features = false }
Expand Down
94 changes: 54 additions & 40 deletions crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
//! For more documentation about this see the `wasm-bindgen-test` crate README
//! and source code.

use crate::tmp_dir_lock::TmpDirLock;
use anyhow::{anyhow, bail, Context};
use docopt::Docopt;
use log::error;
use serde::Deserialize;
use std::env;
use std::fs;
Expand All @@ -26,6 +26,7 @@ mod headless;
mod node;
mod server;
mod shell;
mod tmp_dir_lock;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum TestMode {
Expand Down Expand Up @@ -57,13 +58,15 @@ impl TestMode {
}
}

struct TmpDirDeleteGuard(PathBuf);
struct TmpDirDeleteGuard<'a>(PathBuf, &'a TmpDirLock);

impl Drop for TmpDirDeleteGuard {
impl Drop for TmpDirDeleteGuard<'_> {
fn drop(&mut self) {
if let Err(e) = fs::remove_dir_all(&self.0) {
error!("failed to remove temporary directory: {}", e);
}
/*if self.1.try_upgrade_lock().is_ok() && self.0.exists() {
if let Err(e) = fs::remove_dir_all(&self.0) {
error!("failed to remove temporary directory: {}", e);
}
}*/
}
}

Expand Down Expand Up @@ -249,28 +252,7 @@ fn main() -> anyhow::Result<()> {
println!("Set timeout to {} seconds...", timeout);
}

// Make the generated bindings available for the tests to execute against.
let shell = shell::Shell::new();
shell.status("Executing bindgen...");
let mut b = Bindgen::new();
match test_mode {
TestMode::Node => b.nodejs(true)?,
TestMode::Deno => b.deno(true)?,
TestMode::Browser { .. }
| TestMode::DedicatedWorker { .. }
| TestMode::SharedWorker { .. }
| TestMode::ServiceWorker { .. } => {
if test_mode.no_modules() {
b.no_modules(true)?
} else {
b.web(true)?
}
}
};

if std::env::var("WASM_BINDGEN_SPLIT_LINKED_MODULES").is_ok() {
b.split_linked_modules(true);
}
let module = "wasm-bindgen-test";

// wasm_file_to_test may be
// - a cargo-like directory layout and generate output at
Expand All @@ -291,20 +273,52 @@ fn main() -> anyhow::Result<()> {
.map(|p| p.join(format!("wbg-tmp-{}", file_name)))
.ok_or_else(|| anyhow!("file to test doesn't follow the expected Cargo conventions"))?;

// Make sure there's no stale state from before
drop(fs::remove_dir_all(&tmpdir));
fs::create_dir(&tmpdir).context("creating temporary directory")?;
let _guard = TmpDirDeleteGuard(tmpdir.clone());
let shell = shell::Shell::new();

let module = "wasm-bindgen-test";
let lock = TmpDirLock::new(&tmpdir)?;
let _guard = TmpDirDeleteGuard(tmpdir.clone(), &lock);

if lock.try_lock_exclusive().is_ok() {
// Make sure there's no stale state from before
drop(fs::remove_dir_all(&tmpdir));
fs::create_dir(&tmpdir).context("creating temporary directory")?;

// Make the generated bindings available for the tests to execute against.
shell.status("Executing bindgen...");
let mut b = Bindgen::new();
match test_mode {
TestMode::Node => b.nodejs(true)?,
TestMode::Deno => b.deno(true)?,
TestMode::Browser { .. }
| TestMode::DedicatedWorker { .. }
| TestMode::SharedWorker { .. }
| TestMode::ServiceWorker { .. } => {
if test_mode.no_modules() {
b.no_modules(true)?
} else {
b.web(true)?
}
}
};

if std::env::var("WASM_BINDGEN_SPLIT_LINKED_MODULES").is_ok() {
b.split_linked_modules(true);
}

b.debug(debug)
.input_module(module, wasm)
.keep_debug(false)
.emit_start(false)
.generate(&tmpdir)
.context("executing `wasm-bindgen` over the wasm file")?;
shell.clear();
b.debug(debug)
.input_module(module, wasm)
.keep_debug(false)
.emit_start(false)
.generate(&tmpdir)
.context("executing `wasm-bindgen` over the wasm file")?;
shell.clear();

lock.downgrade_lock()
.context("failed to downgrade temporary directory lock")?;
} else {
lock.lock_shared()
.context("failed to aquire temporary directory shared lock")?;
}

let args: Vec<_> = args.collect();

Expand Down
75 changes: 75 additions & 0 deletions crates/cli/src/bin/wasm-bindgen-test-runner/tmp_dir_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use fs2::FileExt;
use std::fs::File;
//use std::remove_file;
use std::ffi::OsStr;
use std::io::Error;
use std::path::PathBuf;

pub struct TmpDirLock {
exclusive: File,
shared: File,
exclusive_path: PathBuf,
shared_path: PathBuf,
}

impl TmpDirLock {
pub fn new(directory: &PathBuf) -> Result<Self, Error> {
let base = directory.parent().unwrap();
let name = directory.file_name().and_then(OsStr::to_str).unwrap();
let exclusive = &base.join(format!("{}-exclusive.lock", name));
let shared = &base.join(format!("{}-shared.lock", name));
Ok(TmpDirLock {
exclusive: File::create(exclusive)?,
exclusive_path: exclusive.to_path_buf(),
shared: File::create(shared)?,
shared_path: shared.to_path_buf(),
})
}

pub fn downgrade_lock(&self) -> Result<(), Error> {
self.shared.unlock()?;
self.shared.lock_shared()?;
self.exclusive.unlock()?;
Ok(())
}

pub fn lock_shared(&self) -> Result<(), Error> {
self.shared.lock_shared()
}

pub fn try_lock_exclusive(&self) -> Result<(), Error> {
self.exclusive.try_lock_exclusive()?;
let result = self.shared.try_lock_exclusive();
if result.is_err() {
self.exclusive.unlock()?;
result
} else {
Ok(())
}
}

pub fn try_upgrade_lock(&self) -> Result<(), Error> {
self.exclusive.try_lock_exclusive()?;
let result = self.shared.unlock();
if result.is_err() {
self.exclusive.unlock()?;
return result;
}
let result = self.shared.try_lock_exclusive();
if result.is_err() {
self.exclusive.unlock()?;
result
} else {
Ok(())
}
}
}

impl Drop for TmpDirLock {
fn drop(&mut self) {
let _ = self.exclusive.unlock();
let _ = self.shared.unlock();
//let _ = remove_file(&self.exclusive_path);
//let _ = remove_file(&self.shared_path);
}
}

0 comments on commit 926432b

Please sign in to comment.