From cc43a7ecda73c5369218f1b68bf34faa0134751a Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Wed, 11 Oct 2023 15:12:06 +0330 Subject: [PATCH] Shell: Add a 'in_parallel' builtin for easy concurrency --- Userland/Shell/Builtin.cpp | 50 ++++++++++++++++++++++++++++++++++++++ Userland/Shell/Shell.h | 1 + 2 files changed, 51 insertions(+) diff --git a/Userland/Shell/Builtin.cpp b/Userland/Shell/Builtin.cpp index 58d4705295e..03082115db5 100644 --- a/Userland/Shell/Builtin.cpp +++ b/Userland/Shell/Builtin.cpp @@ -2099,6 +2099,56 @@ ErrorOr Shell::builtin_shell_set_active_prompt(Main::Arguments arguments) return 0; } +ErrorOr Shell::builtin_in_parallel(Main::Arguments arguments) +{ + unsigned max_jobs = 1; + Vector command_and_arguments; + +#ifdef _SC_NPROCESSORS_ONLN + max_jobs = sysconf(_SC_NPROCESSORS_ONLN); +#endif + + Core::ArgsParser parser; + parser.set_general_help("Run the given command in the background, allowing at most jobs running at once."); + parser.add_option(max_jobs, "Maximum number of jobs to run in parallel", "max-jobs", 'j', "N"); + parser.add_positional_argument(command_and_arguments, "Command and arguments to run", "argument", Core::ArgsParser::Required::Yes); + parser.set_stop_on_first_non_option(true); + + if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore)) + return 1; + + if (command_and_arguments.is_empty()) { + warnln("in_parallel: No command to run"); + return 1; + } + + AST::Command command; + TRY(command.argv.try_ensure_capacity(command_and_arguments.size())); + for (auto& arg : command_and_arguments) + command.argv.append(TRY(String::from_utf8(arg))); + + auto commands = TRY(expand_aliases({ move(command) })); + + Vector commands_to_run; + for (auto& command : commands) { + if (command.argv.is_empty()) + continue; + command.should_notify_if_in_background = false; + command.should_wait = false; + commands_to_run.append(move(command)); + } + + if (commands_to_run.is_empty()) { + warnln("in_parallel: No command to run"); + return 1; + } + + Core::EventLoop loop; + loop.spin_until([&] { return jobs.size() + commands_to_run.size() <= max_jobs; }); + run_commands(commands_to_run); + return 0; +} + bool Shell::has_builtin(StringView name) const { if (name == ":"sv || (m_in_posix_mode && name == "."sv)) diff --git a/Userland/Shell/Shell.h b/Userland/Shell/Shell.h index f322437469d..dee13616c85 100644 --- a/Userland/Shell/Shell.h +++ b/Userland/Shell/Shell.h @@ -64,6 +64,7 @@ __ENUMERATE_SHELL_BUILTIN(read, OnlyInPOSIXMode) \ __ENUMERATE_SHELL_BUILTIN(run_with_env, OnlyInPOSIXMode) \ __ENUMERATE_SHELL_BUILTIN(argsparser_parse, InAllModes) \ + __ENUMERATE_SHELL_BUILTIN(in_parallel, InAllModes) \ __ENUMERATE_SHELL_BUILTIN(shell_set_active_prompt, InAllModes) #define ENUMERATE_SHELL_OPTIONS() \