From 71ca9e5011dae473444847b5896cf94c935fe6db Mon Sep 17 00:00:00 2001 From: Aaron Reisman Date: Sun, 30 Aug 2026 14:05:10 +0700 Subject: [PATCH 1/6] fix(fspy): bundle osh with the RunProcess getpgid ESRCH fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stock oils 0.37.0 calls getpgid(child) after fork even with job control disabled, and on macOS getpgid of an already-exited child fails with ESRCH, so a fast external command intermittently killed the substituted shell with "oils I/O error (main): No such process" and exit 2 — a few percent of runs under CPU contention. Bundle a build of 0.37.0 plus the upstream fix (oils-for-unix/oils#2689, gate the getpgid on job control being enabled) until an oils release carries it. crates/fspy/tests/oils_esrch.rs pins the fix: it fails against a stock binary and passes against the patched one. Fixes #702. --- CHANGELOG.md | 1 + crates/fspy/build.rs | 14 +++++++--- crates/fspy/tests/oils_esrch.rs | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 crates/fspy/tests/oils_esrch.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a0d93dcf2..bd0363e85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +- **Fixed** The bundled shell that runs cached task commands on macOS no longer intermittently dies with exit 2 (`oils I/O error (main): No such process`) when a fast command finishes before the shell gets scheduled — the bundled osh now carries the upstream fix for its post-fork `getpgid` race ([#702](https://github.com/voidzero-dev/vite-task/issues/702), [oils#2689](https://github.com/oils-for-unix/oils/pull/2689)). - **Fixed** `vp run` no longer hangs or fails when a task leaves a process running behind it, such as a dev server or a background helper, or when one of a task's processes is killed. The run finishes as soon as the task itself does, and the files the task used are still recorded ([#544](https://github.com/voidzero-dev/vite-task/issues/544), [#675](https://github.com/voidzero-dev/vite-task/pull/675)). - **Fixed** A task that reads or writes an unusually large number of files now runs to the end instead of being killed partway through. Vite+ reports the run as not cached, because it could not record every file the task used ([#533](https://github.com/voidzero-dev/vite-task/issues/533), [#675](https://github.com/voidzero-dev/vite-task/pull/675)). - **Fixed** Vite+ diagnostics now display individual paths and working directories without Rust debug formatting such as quoted paths or escaped Windows backslashes ([#534](https://github.com/voidzero-dev/vite-task/pull/534)). diff --git a/crates/fspy/build.rs b/crates/fspy/build.rs index 031251828..74197a120 100644 --- a/crates/fspy/build.rs +++ b/crates/fspy/build.rs @@ -72,13 +72,16 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "aarch64", &[ + // Carries the Process::RunProcess getpgid ESRCH fix on top of stock + // 0.37.0 until an upstream release includes it + // (https://github.com/oils-for-unix/oils/pull/2689). Stock builds: // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-arm64.tar.gz", + url: "https://github.com/lifeiscontent/oils-for-unix-build/releases/download/oils-for-unix-0.37.0-fspy.1/oils-for-unix-0.37.0-fspy.1-darwin-arm64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "ce4bb80b15f0a0371af08b19b65bfa5ea17d30429ebb911f487de3d2bcc7a07d", + expected_sha256: "b61c45ee8c240d58fc75d6d2ae20f6a89790036362c30aa5f2c21c8f328643f7", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { @@ -93,13 +96,16 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "x86_64", &[ + // Carries the Process::RunProcess getpgid ESRCH fix on top of stock + // 0.37.0 until an upstream release includes it + // (https://github.com/oils-for-unix/oils/pull/2689). Stock builds: // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-x86_64.tar.gz", + url: "https://github.com/lifeiscontent/oils-for-unix-build/releases/download/oils-for-unix-0.37.0-fspy.1/oils-for-unix-0.37.0-fspy.1-darwin-x86_64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "cf1a95993127770e2a5fff277cd256a2bb28cf97d7f83ae42fdccc172cdb540d", + expected_sha256: "bf570d54a78dbe0a55712620f746c2b90bbc2aedc633c80d41ca9bbd934424d3", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { diff --git a/crates/fspy/tests/oils_esrch.rs b/crates/fspy/tests/oils_esrch.rs new file mode 100644 index 000000000..6dcc6f20e --- /dev/null +++ b/crates/fspy/tests/oils_esrch.rs @@ -0,0 +1,45 @@ +//! The bundled osh substitute must not lose the getpgid race against a fast +//! child: stock oils 0.37.0 calls getpgid(child) after fork even when job +//! control is disabled, and on macOS getpgid of an already-exited child fails +//! with ESRCH, killing the shell with "oils I/O error (main)" and exit 2 +//! (oils-for-unix/oils#2689). Under CPU contention a few percent of runs died. +//! +//! The race is gone (not just rarer) with a fixed osh, so this fails +//! deterministically if the bundled artifact regresses to a stock build. +#![cfg(target_os = "macos")] + +use std::{fs, path::Path}; + +use test_log::test; + +#[test(tokio::test(flavor = "multi_thread", worker_threads = 8))] +async fn fast_external_commands_under_contention() -> anyhow::Result<()> { + let input = Path::new(env!("CARGO_TARGET_TMPDIR")).join("fspy-oils-esrch-input.txt"); + fs::write(&input, "hello\n")?; + + let mut failures = Vec::new(); + for _round in 0..25 { + let mut handles = Vec::new(); + for _ in 0..8 { + handles.push(tokio::spawn({ + let input = input.clone(); + async move { + let mut cmd = fspy::Command::new("/bin/sh"); + cmd.arg("-c").arg(format!("cat {}", input.display())); + cmd.env("PATH", "/usr/bin:/bin"); + let child = cmd.spawn(tokio_util::sync::CancellationToken::new()).await?; + let termination = child.wait_handle.await?; + anyhow::Ok(termination.status.code()) + } + })); + } + for h in handles { + let code = h.await??; + if code != Some(0) { + failures.push(code); + } + } + } + assert!(failures.is_empty(), "osh exited non-zero {} times: {failures:?}", failures.len()); + Ok(()) +} From 80542c0adb59671d75ae935465da4c607ac62fd5 Mon Sep 17 00:00:00 2001 From: wan9chi Date: Thu, 24 Sep 2026 00:39:08 +0800 Subject: [PATCH 2/6] fix(fspy): bundle stock Oils 0.38.0 instead of patched 0.37.0 Oils 0.38.0 includes the RunProcess getpgid ESRCH fix (oils-for-unix/oils@f5bd5d9c8), so the bundled osh no longer needs a patched 0.37.0 build from a personal fork. Point build.rs back at wan9chi/oils-for-unix-build, now with a 0.38.0 release, and update the test doc comment and changelog entry to match. Co-authored-by: Claude Opus 5.5 --- CHANGELOG.md | 2 +- crates/fspy/build.rs | 18 ++++++------------ crates/fspy/tests/oils_esrch.rs | 13 +++++++------ 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd0363e85..2d617a408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -- **Fixed** The bundled shell that runs cached task commands on macOS no longer intermittently dies with exit 2 (`oils I/O error (main): No such process`) when a fast command finishes before the shell gets scheduled — the bundled osh now carries the upstream fix for its post-fork `getpgid` race ([#702](https://github.com/voidzero-dev/vite-task/issues/702), [oils#2689](https://github.com/oils-for-unix/oils/pull/2689)). +- **Fixed** Cached tasks on macOS no longer intermittently fail with exit 2 and `oils I/O error (main): No such process` when a fast command finishes before the shell gets scheduled. The bundled shell that runs task commands is updated to Oils 0.38.0, which fixes this race ([#702](https://github.com/voidzero-dev/vite-task/issues/702), [#703](https://github.com/voidzero-dev/vite-task/pull/703)). - **Fixed** `vp run` no longer hangs or fails when a task leaves a process running behind it, such as a dev server or a background helper, or when one of a task's processes is killed. The run finishes as soon as the task itself does, and the files the task used are still recorded ([#544](https://github.com/voidzero-dev/vite-task/issues/544), [#675](https://github.com/voidzero-dev/vite-task/pull/675)). - **Fixed** A task that reads or writes an unusually large number of files now runs to the end instead of being killed partway through. Vite+ reports the run as not cached, because it could not record every file the task used ([#533](https://github.com/voidzero-dev/vite-task/issues/533), [#675](https://github.com/voidzero-dev/vite-task/pull/675)). - **Fixed** Vite+ diagnostics now display individual paths and working directories without Rust debug formatting such as quoted paths or escaped Windows backslashes ([#534](https://github.com/voidzero-dev/vite-task/pull/534)). diff --git a/crates/fspy/build.rs b/crates/fspy/build.rs index 74197a120..7675d6dab 100644 --- a/crates/fspy/build.rs +++ b/crates/fspy/build.rs @@ -72,16 +72,13 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "aarch64", &[ - // Carries the Process::RunProcess getpgid ESRCH fix on top of stock - // 0.37.0 until an upstream release includes it - // (https://github.com/oils-for-unix/oils/pull/2689). Stock builds: - // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 + // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.38.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/lifeiscontent/oils-for-unix-build/releases/download/oils-for-unix-0.37.0-fspy.1/oils-for-unix-0.37.0-fspy.1-darwin-arm64.tar.gz", + url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.38.0/oils-for-unix-0.38.0-darwin-arm64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "b61c45ee8c240d58fc75d6d2ae20f6a89790036362c30aa5f2c21c8f328643f7", + expected_sha256: "b6e9d77aa6b22692f6132ca03346a8e166f1f57d966712f0ffff14c89412df15", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { @@ -96,16 +93,13 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "x86_64", &[ - // Carries the Process::RunProcess getpgid ESRCH fix on top of stock - // 0.37.0 until an upstream release includes it - // (https://github.com/oils-for-unix/oils/pull/2689). Stock builds: - // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 + // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.38.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/lifeiscontent/oils-for-unix-build/releases/download/oils-for-unix-0.37.0-fspy.1/oils-for-unix-0.37.0-fspy.1-darwin-x86_64.tar.gz", + url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.38.0/oils-for-unix-0.38.0-darwin-x86_64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "bf570d54a78dbe0a55712620f746c2b90bbc2aedc633c80d41ca9bbd934424d3", + expected_sha256: "1a41a729538fd3663809c70f72179503345ea717b956607771ba766574ab6382", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { diff --git a/crates/fspy/tests/oils_esrch.rs b/crates/fspy/tests/oils_esrch.rs index 6dcc6f20e..4567b99da 100644 --- a/crates/fspy/tests/oils_esrch.rs +++ b/crates/fspy/tests/oils_esrch.rs @@ -1,11 +1,12 @@ -//! The bundled osh substitute must not lose the getpgid race against a fast -//! child: stock oils 0.37.0 calls getpgid(child) after fork even when job -//! control is disabled, and on macOS getpgid of an already-exited child fails -//! with ESRCH, killing the shell with "oils I/O error (main)" and exit 2 +//! On macOS, fspy runs `/bin/sh` commands under the bundled osh, which must not +//! lose the getpgid race against a fast child. Oils before 0.38.0 called +//! getpgid(child) after fork even when job control was disabled, and on macOS +//! getpgid of an already-exited child fails with ESRCH, killing the shell with +//! "oils I/O error (main): No such process" and exit 2 //! (oils-for-unix/oils#2689). Under CPU contention a few percent of runs died. //! -//! The race is gone (not just rarer) with a fixed osh, so this fails -//! deterministically if the bundled artifact regresses to a stock build. +//! A fixed osh never fails here, while an affected one fails a few percent of +//! runs, so 200 runs catch a bundled osh that reintroduces the race. #![cfg(target_os = "macos")] use std::{fs, path::Path}; From e9b34297970d527ce0622123de9c8ff8f91e6632 Mon Sep 17 00:00:00 2001 From: wan9chi Date: Thu, 24 Sep 2026 00:48:14 +0800 Subject: [PATCH 3/6] test(fspy): discard shell stdout in oils_esrch test The test only checks exit codes; the inherited stdout printed 200 "hello" lines into the CI log. Stderr stays inherited so a failing shell's error message is still visible. Co-authored-by: Claude Opus 5.5 --- crates/fspy/tests/oils_esrch.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/fspy/tests/oils_esrch.rs b/crates/fspy/tests/oils_esrch.rs index 4567b99da..4067c8977 100644 --- a/crates/fspy/tests/oils_esrch.rs +++ b/crates/fspy/tests/oils_esrch.rs @@ -9,7 +9,7 @@ //! runs, so 200 runs catch a bundled osh that reintroduces the race. #![cfg(target_os = "macos")] -use std::{fs, path::Path}; +use std::{fs, path::Path, process::Stdio}; use test_log::test; @@ -28,6 +28,7 @@ async fn fast_external_commands_under_contention() -> anyhow::Result<()> { let mut cmd = fspy::Command::new("/bin/sh"); cmd.arg("-c").arg(format!("cat {}", input.display())); cmd.env("PATH", "/usr/bin:/bin"); + cmd.stdout(Stdio::null()); let child = cmd.spawn(tokio_util::sync::CancellationToken::new()).await?; let termination = child.wait_handle.await?; anyhow::Ok(termination.status.code()) From 336bed001d0859061addbedd4b13115b1d4ec008 Mon Sep 17 00:00:00 2001 From: wan9chi Date: Thu, 24 Sep 2026 00:48:25 +0800 Subject: [PATCH 4/6] test(fspy): TEMP revert bundled osh to stock 0.37.0 [do not merge] Temporarily restore main's build.rs (stock Oils 0.37.0, which has the getpgid ESRCH race) to confirm that oils_esrch fails in CI against an affected osh. To be reverted. Co-authored-by: Claude Opus 5.5 --- crates/fspy/build.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/fspy/build.rs b/crates/fspy/build.rs index 7675d6dab..031251828 100644 --- a/crates/fspy/build.rs +++ b/crates/fspy/build.rs @@ -72,13 +72,13 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "aarch64", &[ - // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.38.0 + // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.38.0/oils-for-unix-0.38.0-darwin-arm64.tar.gz", + url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-arm64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "b6e9d77aa6b22692f6132ca03346a8e166f1f57d966712f0ffff14c89412df15", + expected_sha256: "ce4bb80b15f0a0371af08b19b65bfa5ea17d30429ebb911f487de3d2bcc7a07d", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { @@ -93,13 +93,13 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "x86_64", &[ - // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.38.0 + // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.38.0/oils-for-unix-0.38.0-darwin-x86_64.tar.gz", + url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-x86_64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "1a41a729538fd3663809c70f72179503345ea717b956607771ba766574ab6382", + expected_sha256: "cf1a95993127770e2a5fff277cd256a2bb28cf97d7f83ae42fdccc172cdb540d", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { From f314827be76bfcb54167d3995532e82e29fb45be Mon Sep 17 00:00:00 2001 From: wan9chi Date: Thu, 24 Sep 2026 00:57:33 +0800 Subject: [PATCH 5/6] Revert "test(fspy): TEMP revert bundled osh to stock 0.37.0 [do not merge]" This reverts commit 336bed001d0859061addbedd4b13115b1d4ec008. Co-authored-by: Claude Opus 5.5 --- crates/fspy/build.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/fspy/build.rs b/crates/fspy/build.rs index 031251828..7675d6dab 100644 --- a/crates/fspy/build.rs +++ b/crates/fspy/build.rs @@ -72,13 +72,13 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "aarch64", &[ - // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 + // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.38.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-arm64.tar.gz", + url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.38.0/oils-for-unix-0.38.0-darwin-arm64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "ce4bb80b15f0a0371af08b19b65bfa5ea17d30429ebb911f487de3d2bcc7a07d", + expected_sha256: "b6e9d77aa6b22692f6132ca03346a8e166f1f57d966712f0ffff14c89412df15", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { @@ -93,13 +93,13 @@ const MACOS_BINARY_DOWNLOADS: &[(&str, &[BinaryDownload])] = &[ ( "x86_64", &[ - // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.37.0 + // https://github.com/wan9chi/oils-for-unix-build/releases/tag/oils-for-unix-0.38.0 BinaryDownload { name: "oils_for_unix", env_var: "FSPY_MACOS_ARTIFACT_OILS_FOR_UNIX", - url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.37.0/oils-for-unix-0.37.0-darwin-x86_64.tar.gz", + url: "https://github.com/wan9chi/oils-for-unix-build/releases/download/oils-for-unix-0.38.0/oils-for-unix-0.38.0-darwin-x86_64.tar.gz", path_in_targz: "oils-for-unix", - expected_sha256: "cf1a95993127770e2a5fff277cd256a2bb28cf97d7f83ae42fdccc172cdb540d", + expected_sha256: "1a41a729538fd3663809c70f72179503345ea717b956607771ba766574ab6382", }, // https://github.com/uutils/coreutils/releases/tag/0.4.0 BinaryDownload { From 5d208546545f5f59b1e447f7f471d1cba0c8907f Mon Sep 17 00:00:00 2001 From: wan9chi Date: Thu, 24 Sep 2026 00:57:50 +0800 Subject: [PATCH 6/6] test(fspy): drop oils_esrch test Under fspy, the shell's child runs as the bundled coreutils with the preload library injected, which slows it enough that the getpgid race rarely triggers. Against stock Oils 0.37.0 the test failed only about 1% of spawns locally and passed in both macOS CI jobs, so it cannot guard against a regression in CI. Co-authored-by: Claude Opus 5.5 --- crates/fspy/tests/oils_esrch.rs | 47 --------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 crates/fspy/tests/oils_esrch.rs diff --git a/crates/fspy/tests/oils_esrch.rs b/crates/fspy/tests/oils_esrch.rs deleted file mode 100644 index 4067c8977..000000000 --- a/crates/fspy/tests/oils_esrch.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! On macOS, fspy runs `/bin/sh` commands under the bundled osh, which must not -//! lose the getpgid race against a fast child. Oils before 0.38.0 called -//! getpgid(child) after fork even when job control was disabled, and on macOS -//! getpgid of an already-exited child fails with ESRCH, killing the shell with -//! "oils I/O error (main): No such process" and exit 2 -//! (oils-for-unix/oils#2689). Under CPU contention a few percent of runs died. -//! -//! A fixed osh never fails here, while an affected one fails a few percent of -//! runs, so 200 runs catch a bundled osh that reintroduces the race. -#![cfg(target_os = "macos")] - -use std::{fs, path::Path, process::Stdio}; - -use test_log::test; - -#[test(tokio::test(flavor = "multi_thread", worker_threads = 8))] -async fn fast_external_commands_under_contention() -> anyhow::Result<()> { - let input = Path::new(env!("CARGO_TARGET_TMPDIR")).join("fspy-oils-esrch-input.txt"); - fs::write(&input, "hello\n")?; - - let mut failures = Vec::new(); - for _round in 0..25 { - let mut handles = Vec::new(); - for _ in 0..8 { - handles.push(tokio::spawn({ - let input = input.clone(); - async move { - let mut cmd = fspy::Command::new("/bin/sh"); - cmd.arg("-c").arg(format!("cat {}", input.display())); - cmd.env("PATH", "/usr/bin:/bin"); - cmd.stdout(Stdio::null()); - let child = cmd.spawn(tokio_util::sync::CancellationToken::new()).await?; - let termination = child.wait_handle.await?; - anyhow::Ok(termination.status.code()) - } - })); - } - for h in handles { - let code = h.await??; - if code != Some(0) { - failures.push(code); - } - } - } - assert!(failures.is_empty(), "osh exited non-zero {} times: {failures:?}", failures.len()); - Ok(()) -}