diff --git a/alderbench/Makefile b/alderbench/Makefile
index b0de8cf..3a99d3a 100644
--- a/alderbench/Makefile
+++ b/alderbench/Makefile
@@ -24,7 +24,7 @@ install: build
uninstall:
rm -f $(BIN) $(CONF)
- rmdir -p --ignore-fail-on-non-empty $(CONF) 2>/dev/null || true
+ rmdir -p --ignore-fail-on-non-empty $(dir $(BIN)) $(dir $(CONF)) 2>/dev/null || true
clean:
cargo clean
diff --git a/alderbench/src/main.rs b/alderbench/src/main.rs
index 52a971e..c3b07a0 100644
--- a/alderbench/src/main.rs
+++ b/alderbench/src/main.rs
@@ -20,7 +20,7 @@ const USAGE: &str = "\
usage: alderbench [OPTION]...
(no option) pick a preset or edit a custom sweep (TUI)
- --tui TUI with the config as loaded (no picker)
+ --tui TUI picker (loaded config = Custom fallback)
--preset NAME TUI straight into a preset: stability | bench |
core-cycle
--headless run the configured sweep without a TUI
@@ -78,13 +78,12 @@ fn main() {
}
if let Some(idx) = args.iter().position(|a| a == "--smoke-workload") {
match args.get(idx + 1) {
- Some(name) => smoke_workload(name, &config::load()),
+ Some(name) => std::process::exit(smoke_workload(name, &config::load())),
None => {
eprintln!("alderbench: --smoke-workload needs a name\n{USAGE}");
std::process::exit(2);
}
}
- return;
}
if args.iter().any(|a| a == "--headless") {
headless_run(config::load());
@@ -264,7 +263,7 @@ fn format_score(s: &workload::Score) -> String {
}
}
-fn smoke_workload(name: &str, cfg: &config::Config) {
+fn smoke_workload(name: &str, cfg: &config::Config) -> i32 {
use std::thread;
use std::time::Duration;
// Build a 3-second spec for the requested workload, regardless of the
@@ -276,10 +275,10 @@ fn smoke_workload(name: &str, cfg: &config::Config) {
};
let Some(mut w) = workload::from_spec(&spec, &cfg.bin_dir) else {
eprintln!("unknown or not-yet-wired workload: {name}");
- return;
+ return 1;
};
println!("smoke: {} params={}", w.name(), w.params());
- match w.start() {
+ let verdict = match w.start() {
Ok(()) => {
// Poll is_running every 250ms; stop after the spec's duration
// plus a 1s grace (some workloads self-exit right at the
@@ -299,11 +298,20 @@ fn smoke_workload(name: &str, cfg: &config::Config) {
println!("smoke: still running — calling stop()");
let _ = w.stop();
}
- let verdict = w.wait();
- let score = w.score();
- println!("smoke: verdict = {:?}", verdict);
- println!("smoke: score = {}", format_score(&score));
+ w.wait()
}
- Err(e) => println!("smoke: start failed: {e}"),
+ Err(e) => {
+ println!("smoke: start failed: {e}");
+ return 1;
+ }
+ };
+ let score = w.score();
+ println!("smoke: verdict = {verdict:?}");
+ println!("smoke: score = {}", format_score(&score));
+ // Stopped is expected when a longer workload (7z) is cut at the smoke
+ // window — only real failures fail the exit code.
+ match verdict {
+ workload::Verdict::Clean | workload::Verdict::Stopped => 0,
+ _ => 1,
}
}
diff --git a/alderbench/src/workload.rs b/alderbench/src/workload.rs
index 9db9a52..8254916 100644
--- a/alderbench/src/workload.rs
+++ b/alderbench/src/workload.rs
@@ -158,6 +158,7 @@ impl SpawnedWorkload {
fn spawn(cmd: &mut Command) -> io::Result<Self> {
let child = cmd
+ .stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
@@ -413,10 +414,12 @@ impl Workload for YCruncher {
format!("y-cruncher variant path canonicalize failed: {e}"),
)
})?;
- // Wrap in `taskset -c <cores>` when the cores range is a subset
- // (not all 0-15). y-cruncher spawns 16 threads by default + has no
- // CLI flag for affinity; taskset pins it at the kernel level. The
- // full-range case skips the wrapper (no point constraining to all).
+ // NOTE: y-cruncher pins its worker threads itself (verified on
+ // 12600KF: taskset -c 0 still spread threads across cpu0-15), and
+ // stress mode rejects -TD/-PF/-noSMT entirely — so a y-cruncher
+ // stress leg ALWAYS occupies every logical core. The taskset
+ // wrapper only constrains the initial thread; it does NOT make
+ // the leg desktop-safe. `cores` is decorative for yc legs.
let mut cmd = taskset_command(&self.cores, &variant_path);
cmd.arg("skip-warnings")
.arg("stress")
@@ -591,8 +594,10 @@ impl Workload for SevenZip {
/// Build a `Command` for `program`, wrapped in `taskset -c <cores>` when
/// `cores` is a subset of the available CPUs (not the full "0-15" range).
/// The full-range case skips the wrapper — there's no point constraining
-/// to all CPUs. Used by YCruncher + SevenZip (StressNg has its own
-/// `--taskset` flag, so it doesn't go through here). Returns a `Command`
+/// to all CPUs. Used by SevenZip (honors it: taskset pins + `-mmt<N>`
+/// caps threads) + YCruncher (starts under it but then RE-PINS its own
+/// threads to its own core table — all cores, see YCruncher::start;
+/// StressNg has its own `--taskset` flag, so it doesn't go through here). Returns a `Command`
/// pointing at either `taskset` (with program as an arg) or `program`
/// directly. `program` accepts `OsStr` so it works with both `&str`
/// (7z) and `&PathBuf` (y-cruncher's canonicalized variant path).
fix: smoke exit codes; null child stdin (yc tty-hang); uninstall rmdir
- main.rs: smoke_workload -> i32 and exit() wiring; Clean/Stopped = 0, real failures = 1 (7z cut at the smoke window is Stopped by design); unknown/unwired name = 1; start failure = 1 - workload.rs: SpawnedWorkload::spawn sets stdin(null) - y-cruncher stress prompts on a tty and hung waiting for ENTER - Makefile: uninstall rmdir -p also removes the bin dir, not just conf dir - 71 tests, clippy -D/fmt clean
c7cc56f04e7cec733e0217d37b05bebb16427c38
josie <josie@example.com> · 2026-10-08T21:23 ·
browse files at this commit
parents:
d7832fa