Factor internal process short-circuiting together

When executing a buffered block or builtin, the usual approach is to
execute, collect output in a string, and then output that string to
stdout or whatever the redirections say. Similarly for stderr.

If we get no output, then we can elide the outputting which means
skipping the background thread. In this case we just mark the process as
finished immediately.

We do this in multiple locations which is confusing. Factor them all
together into a new function run_internal_process_or_short_circuit.
This commit is contained in:
ridiculousfish 2020-01-13 14:46:31 -08:00
parent fe4f7fea5c
commit 47b87dbeb7

View file

@ -237,7 +237,7 @@ static void on_process_created(const std::shared_ptr<job_t> &j, pid_t child_pid)
/// stdout and \p errdata to stderr, respecting the io chain \p ios. For example if target_fd is 1
/// (stdout), and there is a dup2 3->1, then we need to write to fd 3. Then exit the internal
/// process.
static void run_internal_process(process_t *p, std::string outdata, std::string errdata,
static void run_internal_process(process_t *p, std::string &&outdata, std::string &&errdata,
const io_chain_t &ios) {
p->check_generations_before_launch();
@ -328,6 +328,23 @@ static void run_internal_process(process_t *p, std::string outdata, std::string
});
}
/// If \p outdata or \p errdata are both empty, then mark the process as completed immediately.
/// Otherwise, run an internal process.
static void run_internal_process_or_short_circuit(parser_t &parser, const std::shared_ptr<job_t> &j,
process_t *p, std::string &&outdata,
std::string &&errdata, const io_chain_t &ios) {
if (outdata.empty() && errdata.empty()) {
p->completed = true;
if (p->is_last_in_job) {
FLOGF(exec_job_status, L"Set status of job %d (%ls) to %d using short circuit",
j->job_id(), j->preview().c_str(), p->status);
parser.set_last_statuses(j->get_statuses());
}
} else {
run_internal_process(p, std::move(outdata), std::move(errdata), ios);
}
}
/// Call fork() as part of executing a process \p p in a job \j. Execute \p child_action in the
/// context of the child. Returns true if fork succeeded, false if fork failed.
static bool fork_child_for_process(const std::shared_ptr<job_t> &job, process_t *p,
@ -505,19 +522,9 @@ static bool handle_builtin_output(parser_t &parser, const std::shared_ptr<job_t>
if (!outbuff.empty()) fflush(stdout);
if (!errbuff.empty()) fflush(stderr);
if (outbuff.empty() && errbuff.empty()) {
// We do not need to construct a background process.
// TODO: factor this job-status-setting stuff into a single place.
p->completed = true;
if (p->is_last_in_job) {
FLOGF(exec_job_status, L"Set status of job %d (%ls) to %d using short circuit",
j->job_id(), j->preview().c_str(), p->status);
parser.set_last_statuses(j->get_statuses());
}
} else {
// Construct and run our background process.
run_internal_process(p, std::move(outbuff), std::move(errbuff), *io_chain);
}
// Construct and run our background process.
run_internal_process_or_short_circuit(parser, j, p, std::move(outbuff), std::move(errbuff),
*io_chain);
return true;
}
@ -791,14 +798,8 @@ static bool exec_block_or_func_process(parser_t &parser, const std::shared_ptr<j
buffer_contents = block_output_buffer->buffer().newline_serialized();
}
if (!buffer_contents.empty()) {
run_internal_process(p, std::move(buffer_contents), {} /*errdata*/, io_chain);
} else {
if (p->is_last_in_job) {
parser.set_last_statuses(j->get_statuses());
}
p->completed = true;
}
run_internal_process_or_short_circuit(parser, j, p, std::move(buffer_contents),
{} /* errdata */, io_chain);
return true;
}