Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/uu/sort/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ sort-invalid-suffix-in-option-arg = invalid suffix in --{$option} argument {$arg
sort-invalid-option-arg = invalid --{$option} argument {$arg}
sort-option-arg-too-large = --{$option} argument {$arg} too large
sort-error-disorder = {$file}:{$line_number}: disorder: {$line}
sort-error-parallel-nonzero = number in parallel must be nonzero
sort-error-buffer-size-too-big = Buffer size {$size} does not fit in address space
sort-error-no-match-for-key = ^ no match for key
sort-error-write-failed = write failed: {$output}
Expand Down
1 change: 1 addition & 0 deletions src/uu/sort/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ sort-invalid-suffix-in-option-arg = suffixe invalide dans l'argument --{$option}
sort-invalid-option-arg = argument --{$option} invalide {$arg}
sort-option-arg-too-large = argument --{$option} {$arg} trop grand
sort-error-disorder = {$file}:{$line_number}: désordre : {$line}
sort-error-parallel-nonzero = le nombre en parallèle doit être non nul
sort-error-buffer-size-too-big = La taille du tampon {$size} ne rentre pas dans l'espace d'adressage
sort-error-no-match-for-key = ^ aucune correspondance pour la clé
sort-error-write-failed = échec d'écriture : {$output}
Expand Down
42 changes: 37 additions & 5 deletions src/uu/sort/src/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2249,10 +2249,42 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// WASI doesn't support threads, so we ignore the corresponding option
#[cfg(not(target_os = "wasi"))]
{
let threads = matches
.get_one::<u64>(options::PARALLEL)
.copied()
.unwrap_or_else(|| std::thread::available_parallelism().map_or(1, |n| n.get() as u64));
let threads = if let Some(threads_str) = matches.get_one::<String>(options::PARALLEL) {
// GNU accepts a suffix syntactically -- it goes through the same
// number-with-suffix parser as `-S` -- but none is ever valid for
// a thread count, so the allow list is empty rather than absent.
//
// `parse_u64` errors on overflow instead of clamping: GNU itself
// accepts an arbitrarily large count without erroring, but
// `rayon::ThreadPoolBuilder` builds its pool eagerly, so asking
// it for anywhere near that many threads hangs rather than
// completing quickly. That is a pre-existing limitation already
// reachable with an ordinary large-but-in-range count (10000
// already hangs); erroring here at least avoids making it
// reachable from *more* inputs than it already was.
let count = Parser::default()
.with_allow_list(&[])
.parse_u64(threads_str)
.map_err(|error| {
let message = format_error_message(&error, threads_str, options::PARALLEL);
error.size_value_error(
key_args.as_deref(),
&OptionValue::with_names(threads_str, None, Some(options::PARALLEL)),
0,
&message,
USimpleError::new(2, message.clone()),
)
})?;
if count == 0 {
return Err(USimpleError::new(
2,
translate!("sort-error-parallel-nonzero"),
));
}
count
} else {
std::thread::available_parallelism().map_or(1, |n| n.get() as u64)
};
let _ = rayon::ThreadPoolBuilder::new()
.num_threads(threads as usize)
.build_global();
Expand Down Expand Up @@ -2667,7 +2699,7 @@ pub fn uu_app() -> Command {
Arg::new(options::PARALLEL)
.long(options::PARALLEL)
.help(translate!("sort-help-parallel"))
.value_parser(clap::value_parser!(u64).range(1..))
.allow_hyphen_values(true)
.value_name("NUM_THREADS"),
)
.arg(
Expand Down
31 changes: 28 additions & 3 deletions tests/by-util/test_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,34 @@ fn test_version_empty_lines() {

#[test]
fn test_parallel_invalid() {
// clap provided stderr
new_ucmd!().arg("--parallel=0").fails().code_is(2);
new_ucmd!().arg("--parallel=NaN").fails().code_is(2);
new_ucmd!()
.arg("--parallel=0")
.fails_with_code(2)
.stderr_only("sort: number in parallel must be nonzero\n");
new_ucmd!()
.arg("--parallel=NaN")
.fails_with_code(2)
.stderr_only("sort: invalid --parallel argument 'NaN'\n");
new_ucmd!()
.arg("--parallel=-1")
.fails_with_code(2)
.stderr_only("sort: invalid --parallel argument '-1'\n");
new_ucmd!()
.arg("--parallel=")
.fails_with_code(2)
.stderr_only("sort: invalid --parallel argument ''\n");
// No unit is ever valid for a thread count, unlike `-S`/`--buffer-size`,
// which this shares its parser with.
new_ucmd!()
.arg("--parallel=2K")
.fails_with_code(2)
.stderr_only("sort: invalid suffix in --parallel argument '2K'\n");
// A separate (not `=`-attached) value starting with `-` is still this
// option's value, not a new flag -- as GNU accepts it.
new_ucmd!()
.args(&["--parallel", "-1"])
.fails_with_code(2)
.stderr_only("sort: invalid --parallel argument '-1'\n");
}

#[test]
Expand Down
Loading