feat(cli): support --single-thread in the Rust frontend
The Rust CLI frontend rejected --single-thread, but tests/playTests.sh uses it 28 times, so the flag is required before the original CLI test suite can gate the migration. Mirror the C zstdcli.c semantics: --single-thread pins zero workers and sets a latch that suppresses the automatic core-count resolution, so fileio receives nbWorkers == 0 and runs its single-thread streaming mode (slightly different from -T1, which uses one worker thread). A bare zero from -T0 or the zstdmt program name still auto-detects the core count, and a later -T# overrides the pinned worker count while the latch stays set, exactly as the C variable pair behaved. Test plan: - cd rust/cli && cargo clippy --all-targets -- -D warnings && cargo test --all-targets (plus the compression-only and decompression-only feature matrices); new parser tests cover the flag, the -T override order, and rejection of an attached value. - make -C programs zstd, then: --single-thread -3/-19 round-trips against COPYING via cmp; --single-thread=1 fails with "does not take an argument".
This commit is contained in:
+47
-4
@@ -210,6 +210,7 @@ struct Cli {
|
|||||||
mmap_dict: i32,
|
mmap_dict: i32,
|
||||||
progress: i32,
|
progress: i32,
|
||||||
workers: Option<i32>,
|
workers: Option<i32>,
|
||||||
|
single_thread: bool,
|
||||||
block_size: Option<usize>,
|
block_size: Option<usize>,
|
||||||
mem_limit: Option<u32>,
|
mem_limit: Option<u32>,
|
||||||
ldm: bool,
|
ldm: bool,
|
||||||
@@ -254,6 +255,7 @@ impl Cli {
|
|||||||
mmap_dict: ZSTD_PS_AUTO,
|
mmap_dict: ZSTD_PS_AUTO,
|
||||||
progress: FIO_PS_AUTO,
|
progress: FIO_PS_AUTO,
|
||||||
workers: None,
|
workers: None,
|
||||||
|
single_thread: false,
|
||||||
block_size: None,
|
block_size: None,
|
||||||
mem_limit: None,
|
mem_limit: None,
|
||||||
ldm: false,
|
ldm: false,
|
||||||
@@ -332,8 +334,11 @@ unsafe fn default_worker_count() -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "compression")]
|
#[cfg(feature = "compression")]
|
||||||
unsafe fn resolved_worker_count(workers: Option<i32>) -> i32 {
|
unsafe fn resolved_worker_count(workers: Option<i32>, single_thread: bool) -> i32 {
|
||||||
match workers {
|
match workers {
|
||||||
|
/* --single-thread pins zero workers; a bare zero (-T0 or the zstdmt
|
||||||
|
* program name) auto-detects the core count as in the C CLI. */
|
||||||
|
Some(0) if single_thread => 0,
|
||||||
Some(0) => unsafe { UTIL_countPhysicalCores() }.max(1),
|
Some(0) => unsafe { UTIL_countPhysicalCores() }.max(1),
|
||||||
Some(workers) => workers,
|
Some(workers) => workers,
|
||||||
None => unsafe { default_worker_count() },
|
None => unsafe { default_worker_count() },
|
||||||
@@ -385,7 +390,7 @@ fn usage(advanced: bool) {
|
|||||||
let _ = writeln!(out, "\nImplemented advanced compression controls:");
|
let _ = writeln!(out, "\nImplemented advanced compression controls:");
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
out,
|
out,
|
||||||
" --fast[=#], --ultra, --long[=#], --threads=#, --block-size=#"
|
" --fast[=#], --ultra, --long[=#], --threads=#, --single-thread, --block-size=#"
|
||||||
);
|
);
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
out,
|
out,
|
||||||
@@ -638,6 +643,7 @@ fn parse_long_option(
|
|||||||
| "--no-row-match-finder"
|
| "--no-row-match-finder"
|
||||||
| "--row-match-finder"
|
| "--row-match-finder"
|
||||||
| "--rsyncable"
|
| "--rsyncable"
|
||||||
|
| "--single-thread"
|
||||||
| "--compress-literals"
|
| "--compress-literals"
|
||||||
| "--no-compress-literals"
|
| "--no-compress-literals"
|
||||||
| "--exclude-compressed"
|
| "--exclude-compressed"
|
||||||
@@ -809,6 +815,14 @@ fn parse_long_option(
|
|||||||
cli.workers = Some(parse_worker_count(&value)?);
|
cli.workers = Some(parse_worker_count(&value)?);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
"--single-thread" => {
|
||||||
|
/* As in the C CLI: zero workers plus a latch that suppresses the
|
||||||
|
* automatic core-count resolution, so fileio runs its
|
||||||
|
* single-thread streaming mode (slightly different from -T1). */
|
||||||
|
cli.workers = Some(0);
|
||||||
|
cli.single_thread = true;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
"--memlimit" | "--memory" | "--memlimit-decompress" => {
|
"--memlimit" | "--memory" | "--memlimit-decompress" => {
|
||||||
let value = next_value(attached, args, index, name)?;
|
let value = next_value(attached, args, index, name)?;
|
||||||
cli.mem_limit = Some(parse_u32(&value, "memory limit")?);
|
cli.mem_limit = Some(parse_u32(&value, "memory limit")?);
|
||||||
@@ -854,7 +868,6 @@ fn parse_long_option(
|
|||||||
| "--trace"
|
| "--trace"
|
||||||
| "--format"
|
| "--format"
|
||||||
| "--priority"
|
| "--priority"
|
||||||
| "--single-thread"
|
|
||||||
| "--auto-threads"
|
| "--auto-threads"
|
||||||
| "--fake-stdin-is-console"
|
| "--fake-stdin-is-console"
|
||||||
| "--fake-stdout-is-console"
|
| "--fake-stdout-is-console"
|
||||||
@@ -1012,7 +1025,7 @@ unsafe fn apply_preferences(cli: &Cli, prefs: *mut FIO_prefs_t, ctx: *mut FIO_ct
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
#[cfg(feature = "compression")]
|
#[cfg(feature = "compression")]
|
||||||
FIO_setNbWorkers(prefs, resolved_worker_count(cli.workers));
|
FIO_setNbWorkers(prefs, resolved_worker_count(cli.workers, cli.single_thread));
|
||||||
FIO_setLdmFlag(prefs, u32::from(cli.ldm));
|
FIO_setLdmFlag(prefs, u32::from(cli.ldm));
|
||||||
FIO_setAdaptiveMode(prefs, i32::from(cli.adapt));
|
FIO_setAdaptiveMode(prefs, i32::from(cli.adapt));
|
||||||
FIO_setRsyncable(prefs, i32::from(cli.rsyncable));
|
FIO_setRsyncable(prefs, i32::from(cli.rsyncable));
|
||||||
@@ -1502,6 +1515,36 @@ mod tests {
|
|||||||
let cli = parse(&["zstdmt", "input"]);
|
let cli = parse(&["zstdmt", "input"]);
|
||||||
|
|
||||||
assert_eq!(cli.workers, Some(0));
|
assert_eq!(cli.workers, Some(0));
|
||||||
|
assert!(!cli.single_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_thread_pins_zero_workers() {
|
||||||
|
let cli = parse(&["zstd", "--single-thread", "input"]);
|
||||||
|
|
||||||
|
assert_eq!(cli.workers, Some(0));
|
||||||
|
assert!(cli.single_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_later_thread_count_overrides_single_thread_workers() {
|
||||||
|
/* Mirrors the C CLI: -T after --single-thread wins the worker count,
|
||||||
|
* while the single-thread latch stays set. */
|
||||||
|
let cli = parse(&["zstd", "--single-thread", "-T2", "input"]);
|
||||||
|
|
||||||
|
assert_eq!(cli.workers, Some(2));
|
||||||
|
assert!(cli.single_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_thread_rejects_attached_values() {
|
||||||
|
let error = parse_args(vec![
|
||||||
|
OsString::from("zstd"),
|
||||||
|
OsString::from("--single-thread=1"),
|
||||||
|
])
|
||||||
|
.expect_err("an attached value must not activate --single-thread");
|
||||||
|
|
||||||
|
assert!(error.contains("does not take an argument"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user