Skip to content

Commit 666e2bf

Browse files
branchseerclaude
andcommitted
Port barrier.js to vtt barrier subcommand for concurrent-execution tests
Add vtt barrier <dir> <prefix> <count> [--exit=<code>] [--hang] [--daemonize] as a Rust port of the Node.js barrier tool from packages/tools. Update concurrent-execution fixture scripts and snapshots to use vtt barrier. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6fdff86 commit 666e2bf

File tree

9 files changed

+104
-15
lines changed

9 files changed

+104
-15
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_task_bin/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ path = "src/vtt.rs"
1616

1717
[dependencies]
1818
anyhow = { workspace = true }
19+
libc = { workspace = true }
1920
async-trait = { workspace = true }
2021
clap = { workspace = true, features = ["derive"] }
2122
jsonc-parser = { workspace = true }

crates/vite_task_bin/src/vtt.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ fn main() {
1111
if args.len() < 2 {
1212
eprintln!("Usage: vtt <subcommand> [args...]");
1313
eprintln!(
14-
"Subcommands: check-tty, print, print-cwd, print-env, print-file, read-stdin, replace-file-content, touch-file"
14+
"Subcommands: barrier, check-tty, print, print-cwd, print-env, print-file, read-stdin, replace-file-content, touch-file"
1515
);
1616
std::process::exit(1);
1717
}
1818

1919
let result: Result<(), Box<dyn std::error::Error>> = match args[1].as_str() {
20+
"barrier" => cmd_barrier(&args[2..]),
2021
"check-tty" => {
2122
cmd_check_tty();
2223
Ok(())
@@ -43,6 +44,92 @@ fn main() {
4344
}
4445
}
4546

47+
/// barrier <dir> <prefix> <count> [--exit=<code>] [--hang] [--daemonize]
48+
///
49+
/// Cross-platform concurrency barrier for testing.
50+
/// Creates <dir>/<prefix>_<pid>, then polls until <count> files matching
51+
/// <prefix>_* exist in <dir>.
52+
///
53+
/// Options:
54+
/// --exit=<code> Exit with the given code after the barrier is met.
55+
/// --hang Keep process alive after the barrier (for kill tests).
56+
/// --daemonize Close stdout/stderr but keep process alive (for daemon kill tests).
57+
fn cmd_barrier(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
58+
let mut positional: Vec<&str> = Vec::new();
59+
let mut exit_code: i32 = 0;
60+
let mut hang = false;
61+
let mut daemonize = false;
62+
63+
for arg in args {
64+
if let Some(code) = arg.strip_prefix("--exit=") {
65+
exit_code = code.parse()?;
66+
} else if arg == "--hang" {
67+
hang = true;
68+
} else if arg == "--daemonize" {
69+
daemonize = true;
70+
} else {
71+
positional.push(arg.as_str());
72+
}
73+
}
74+
75+
if positional.len() < 3 {
76+
return Err(
77+
"Usage: vtt barrier <dir> <prefix> <count> [--exit=<code>] [--hang] [--daemonize]"
78+
.into(),
79+
);
80+
}
81+
82+
let dir = std::path::Path::new(positional[0]);
83+
let prefix = positional[1];
84+
let count: usize = positional[2].parse()?;
85+
86+
std::fs::create_dir_all(dir)?;
87+
88+
// Create this participant's marker file.
89+
let pid = std::process::id();
90+
let marker = dir.join(std::format!("{prefix}_{pid}"));
91+
std::fs::write(&marker, "")?;
92+
93+
// Poll until <count> matching files exist.
94+
loop {
95+
let matches = std::fs::read_dir(dir)?
96+
.filter_map(|e| e.ok())
97+
.filter(|e| e.file_name().to_string_lossy().starts_with(&std::format!("{prefix}_")))
98+
.count();
99+
if matches >= count {
100+
break;
101+
}
102+
std::thread::sleep(std::time::Duration::from_millis(10));
103+
}
104+
105+
if daemonize {
106+
// Close stdout/stderr but keep the process alive. Simulates a daemon that
107+
// detaches from stdio — tests that the runner can still kill such processes.
108+
// Replace stdout/stderr fds with /dev/null so the pipe the parent holds gets EOF.
109+
#[cfg(unix)]
110+
{
111+
use std::os::unix::io::IntoRawFd;
112+
let null1 = std::fs::OpenOptions::new().write(true).open("/dev/null").unwrap();
113+
let null2 = std::fs::OpenOptions::new().write(true).open("/dev/null").unwrap();
114+
unsafe {
115+
libc::dup2(null1.into_raw_fd(), 1);
116+
libc::dup2(null2.into_raw_fd(), 2);
117+
}
118+
}
119+
loop {
120+
std::thread::sleep(std::time::Duration::from_secs(3600));
121+
}
122+
}
123+
124+
if hang {
125+
loop {
126+
std::thread::sleep(std::time::Duration::from_secs(3600));
127+
}
128+
}
129+
130+
std::process::exit(exit_code);
131+
}
132+
46133
fn cmd_check_tty() {
47134
use std::io::IsTerminal as _;
48135
let stdin_tty = if std::io::stdin().is_terminal() { "tty" } else { "not-tty" };
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "@concurrent/a",
33
"scripts": {
4-
"build": "barrier ../../.barrier sync 2",
5-
"test": "barrier ../../.barrier test-sync 2 --exit=1",
6-
"daemon": "barrier ../../.barrier daemon-sync 2 --exit=1"
4+
"build": "vtt barrier ../../.barrier sync 2",
5+
"test": "vtt barrier ../../.barrier test-sync 2 --exit=1",
6+
"daemon": "vtt barrier ../../.barrier daemon-sync 2 --exit=1"
77
}
88
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "@concurrent/b",
33
"scripts": {
4-
"build": "barrier ../../.barrier sync 2",
5-
"test": "barrier ../../.barrier test-sync 2 --hang",
6-
"daemon": "barrier ../../.barrier daemon-sync 2 --daemonize"
4+
"build": "vtt barrier ../../.barrier sync 2",
5+
"test": "vtt barrier ../../.barrier test-sync 2 --hang",
6+
"daemon": "vtt barrier ../../.barrier daemon-sync 2 --daemonize"
77
}
88
}

crates/vite_task_bin/tests/e2e_snapshots/fixtures/concurrent-execution/snapshots/failure kills concurrent cached tasks.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
33
expression: e2e_outputs
44
---
55
[1]> vt run -r --cache test
6-
~/packages/a$ barrier ../../.barrier test-sync 2 --exit=1
7-
~/packages/b$ barrier ../../.barrier test-sync 2 --hang
6+
~/packages/a$ vtt barrier ../../.barrier test-sync 2 --exit=1
7+
~/packages/b$ vtt barrier ../../.barrier test-sync 2 --hang
88

99

1010
---

crates/vite_task_bin/tests/e2e_snapshots/fixtures/concurrent-execution/snapshots/failure kills concurrent tasks.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
33
expression: e2e_outputs
44
---
55
[1]> vt run -r test
6-
~/packages/a$ barrier ../../.barrier test-sync 2 --exit=1cache disabled
7-
~/packages/b$ barrier ../../.barrier test-sync 2 --hangcache disabled
6+
~/packages/a$ vtt barrier ../../.barrier test-sync 2 --exit=1cache disabled
7+
~/packages/b$ vtt barrier ../../.barrier test-sync 2 --hangcache disabled
88

99

1010
---

crates/vite_task_bin/tests/e2e_snapshots/fixtures/concurrent-execution/snapshots/failure kills daemonized concurrent tasks.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
33
expression: e2e_outputs
44
---
55
[1]> vt run -r --cache daemon
6-
~/packages/a$ barrier ../../.barrier daemon-sync 2 --exit=1
7-
~/packages/b$ barrier ../../.barrier daemon-sync 2 --daemonize
6+
~/packages/a$ vtt barrier ../../.barrier daemon-sync 2 --exit=1
7+
~/packages/b$ vtt barrier ../../.barrier daemon-sync 2 --daemonize
88

99

1010
---

crates/vite_task_bin/tests/e2e_snapshots/fixtures/concurrent-execution/snapshots/independent tasks run concurrently.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
33
expression: e2e_outputs
44
---
55
> vt run -r build
6-
~/packages/a$ barrier ../../.barrier sync 2cache disabled
7-
~/packages/b$ barrier ../../.barrier sync 2cache disabled
6+
~/packages/a$ vtt barrier ../../.barrier sync 2cache disabled
7+
~/packages/b$ vtt barrier ../../.barrier sync 2cache disabled
88

99

1010
---

0 commit comments

Comments
 (0)