cmd: add spawn and wait commands
Add a spawn command which runs another command in the background, as well as a wait command to suspend the shell until one or more background jobs have completed. The job_id environment variable is set by spawn and wait accepts optional job ids, so that one can selectively wait on any job. Example: => date; spawn sleep 5; spawn sleep 3; date; echo "waiting..."; wait; date Date: 2025-02-21 (Friday) Time: 17:04:52 Date: 2025-02-21 (Friday) Time: 17:04:52 waiting... Date: 2025-02-21 (Friday) Time: 17:04:57 => Another example showing how background jobs can make initlizations faster. The board is i.MX93 EVK, with one spinning HDD connected to USB1 via a hub, and a network cable plugged into ENET1. # From power up / reset u-boot=> setenv autoload 0 u-boot=> setenv ud "usb start; dhcp" u-boot=> time run ud [...] time: 8.058 seconds # From power up / reset u-boot=> setenv autoload 0 u-boot=> setenv ud "spawn usb start; spawn dhcp; wait" u-boot=> time run ud [...] time: 4.475 seconds Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
This commit is contained in:

committed by
Tom Rini

parent
1c0f6999b5
commit
ec89a4f7a3
17
cmd/Kconfig
17
cmd/Kconfig
@@ -3081,4 +3081,21 @@ config CMD_MESON
|
||||
help
|
||||
Enable useful commands for the Meson Soc family developed by Amlogic Inc.
|
||||
|
||||
config CMD_SPAWN
|
||||
bool "spawn and wait commands"
|
||||
depends on UTHREAD
|
||||
help
|
||||
spawn runs a command in the background and sets the job_id environment
|
||||
variable. wait is used to suspend the shell execution until one or more
|
||||
jobs are complete.
|
||||
|
||||
config CMD_SPAWN_NUM_JOBS
|
||||
int "Maximum number of simultaneous jobs for spawn"
|
||||
default 16
|
||||
help
|
||||
Job identifiers are in the range 1..CMD_SPAWN_NUM_JOBS. In other words
|
||||
there can be no more that CMD_SPAWN_NUM_JOBS running simultaneously.
|
||||
When a jobs exits, its identifier is available to be re-used by the next
|
||||
spawn command.
|
||||
|
||||
endif
|
||||
|
@@ -239,6 +239,8 @@ obj-$(CONFIG_CMD_SCP03) += scp03.o
|
||||
|
||||
obj-$(CONFIG_HUSH_SELECTABLE) += cli.o
|
||||
|
||||
obj-$(CONFIG_CMD_SPAWN) += spawn.o
|
||||
|
||||
obj-$(CONFIG_ARM) += arm/
|
||||
obj-$(CONFIG_RISCV) += riscv/
|
||||
obj-$(CONFIG_SANDBOX) += sandbox/
|
||||
|
187
cmd/spawn.c
Normal file
187
cmd/spawn.c
Normal file
@@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
*/
|
||||
|
||||
#include <command.h>
|
||||
#include <console.h>
|
||||
#include <malloc.h>
|
||||
#include <vsprintf.h>
|
||||
#include <uthread.h>
|
||||
|
||||
/* Spawn arguments and job index */
|
||||
struct spa {
|
||||
int argc;
|
||||
char **argv;
|
||||
unsigned int job_idx;
|
||||
};
|
||||
|
||||
/*
|
||||
* uthread group identifiers for each running job
|
||||
* 0: job slot available, != 0: uthread group id
|
||||
* Note that job[0] is job_id 1, job[1] is job_id 2 etc.
|
||||
*/
|
||||
static unsigned int job[CONFIG_CMD_SPAWN_NUM_JOBS];
|
||||
/* Return values of the commands run as jobs */
|
||||
static enum command_ret_t job_ret[CONFIG_CMD_SPAWN_NUM_JOBS];
|
||||
|
||||
static void spa_free(struct spa *spa)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!spa)
|
||||
return;
|
||||
|
||||
for (i = 0; i < spa->argc; i++)
|
||||
free(spa->argv[i]);
|
||||
free(spa->argv);
|
||||
free(spa);
|
||||
}
|
||||
|
||||
static struct spa *spa_create(int argc, char *const argv[])
|
||||
{
|
||||
struct spa *spa;
|
||||
int i;
|
||||
|
||||
spa = calloc(1, sizeof(*spa));
|
||||
if (!spa)
|
||||
return NULL;
|
||||
spa->argc = argc;
|
||||
spa->argv = malloc(argc * sizeof(char *));
|
||||
if (!spa->argv)
|
||||
goto err;
|
||||
for (i = 0; i < argc; i++) {
|
||||
spa->argv[i] = strdup(argv[i]);
|
||||
if (!spa->argv[i])
|
||||
goto err;
|
||||
}
|
||||
return spa;
|
||||
err:
|
||||
spa_free(spa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void spawn_thread(void *arg)
|
||||
{
|
||||
struct spa *spa = (struct spa *)arg;
|
||||
ulong cycles = 0;
|
||||
int repeatable = 0;
|
||||
|
||||
job_ret[spa->job_idx] = cmd_process(0, spa->argc, spa->argv,
|
||||
&repeatable, &cycles);
|
||||
spa_free(spa);
|
||||
}
|
||||
|
||||
static unsigned int next_job_id(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
|
||||
if (!job[i])
|
||||
return i + 1;
|
||||
|
||||
/* No job available */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void refresh_jobs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
|
||||
if (job[i] && uthread_grp_done(job[i]))
|
||||
job[i] = 0;
|
||||
|
||||
}
|
||||
|
||||
static int do_spawn(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
unsigned int id;
|
||||
unsigned int idx;
|
||||
struct spa *spa;
|
||||
int ret;
|
||||
|
||||
if (argc == 1)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
spa = spa_create(argc - 1, argv + 1);
|
||||
if (!spa)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
refresh_jobs();
|
||||
|
||||
id = next_job_id();
|
||||
if (!id)
|
||||
return CMD_RET_FAILURE;
|
||||
idx = id - 1;
|
||||
|
||||
job[idx] = uthread_grp_new_id();
|
||||
|
||||
ret = uthread_create(NULL, spawn_thread, spa, 0, job[idx]);
|
||||
if (ret) {
|
||||
job[idx] = 0;
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
ret = env_set_ulong("job_id", id);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(spawn, CONFIG_SYS_MAXARGS, 0, do_spawn,
|
||||
"run commands and summarize execution time",
|
||||
"command [args...]\n");
|
||||
|
||||
static enum command_ret_t wait_job(unsigned int idx)
|
||||
{
|
||||
int prev = disable_ctrlc(false);
|
||||
|
||||
while (!uthread_grp_done(job[idx])) {
|
||||
if (ctrlc()) {
|
||||
puts("<INTERRUPT>\n");
|
||||
disable_ctrlc(prev);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
uthread_schedule();
|
||||
}
|
||||
|
||||
job[idx] = 0;
|
||||
disable_ctrlc(prev);
|
||||
|
||||
return job_ret[idx];
|
||||
}
|
||||
|
||||
static int do_wait(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
enum command_ret_t ret = CMD_RET_SUCCESS;
|
||||
unsigned long id;
|
||||
unsigned int idx;
|
||||
int i;
|
||||
|
||||
if (argc == 1) {
|
||||
for (i = 0; i < CONFIG_CMD_SPAWN_NUM_JOBS; i++)
|
||||
if (job[i])
|
||||
ret = wait_job(i);
|
||||
} else {
|
||||
for (i = 1; i < argc; i++) {
|
||||
id = dectoul(argv[i], NULL);
|
||||
if (id < 0 || id > CONFIG_CMD_SPAWN_NUM_JOBS)
|
||||
return CMD_RET_USAGE;
|
||||
idx = (int)id - 1;
|
||||
ret = wait_job(idx);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(wait, CONFIG_SYS_MAXARGS, 0, do_wait,
|
||||
"wait for one or more jobs to complete",
|
||||
"[job_id ...]\n"
|
||||
" - Wait until all specified jobs have exited and return the\n"
|
||||
" exit status of the last job waited for. When no job_id is\n"
|
||||
" given, wait for all the background jobs.\n");
|
Reference in New Issue
Block a user