Start to unwind lineages

job_lineage was used to track "where jobs came from" but the job tree idea is
a better abstraction. It groups jobs together similar to how a process group
would in other shells. Begin to remove the notion of lineage.
This commit is contained in:
ridiculousfish 2020-05-29 12:10:41 -07:00
parent a4b66d948b
commit 55db918d59
5 changed files with 36 additions and 31 deletions

View file

@ -52,17 +52,14 @@
/// Number of calls to fork() or posix_spawn().
static relaxed_atomic_t<int> s_fork_count{0};
pgroup_provenance_t get_pgroup_provenance(const shared_ptr<job_t> &j,
const job_lineage_t &lineage) {
pgroup_provenance_t get_pgroup_provenance(const shared_ptr<job_t> &j) {
bool first_proc_is_internal = j->processes.front()->is_internal();
bool has_internal = j->has_internal_proc();
bool has_external = j->has_external_proc();
assert(first_proc_is_internal ? has_internal : has_external);
if (lineage.job_tree && lineage.job_tree->get_pgid().has_value() && j->is_foreground()) {
// Our lineage indicates a pgid. This means the job is "nested" as a function or block
// inside another job, which has a real pgroup. We're going to use that, unless it's
// backgrounded, in which case it should not inherit a pgroup.
if (j->job_tree->get_pgid().has_value()) {
// Our job tree already has a pgid.
return pgroup_provenance_t::lineage;
} else if (!j->wants_job_control()) {
// This job doesn't need job control, it can just live in the fish pgroup.
@ -928,7 +925,7 @@ static bool allow_exec_with_background_jobs(parser_t &parser) {
}
}
bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const job_lineage_t &lineage) {
bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const io_chain_t &block_io) {
assert(j && "null job_t passed to exec_job!");
// Set to true if something goes wrong while executing the job, in which case the cleanup
@ -948,7 +945,7 @@ bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const job_lineage_t
assert(j->pgid == INVALID_PID && "Should not yet have a pid.");
switch (j->pgroup_provenance) {
case pgroup_provenance_t::lineage: {
auto pgid = lineage.job_tree->get_pgid();
auto pgid = j->job_tree->get_pgid();
assert(pgid && *pgid != INVALID_PID && "Should have valid pgid");
j->pgid = *pgid;
break;
@ -971,7 +968,7 @@ bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const job_lineage_t
const size_t stdout_read_limit = parser.libdata().read_limit;
// Get the list of all FDs so we can ensure our pipes do not conflict.
fd_set_t conflicts = lineage.block_io.fd_set();
fd_set_t conflicts = block_io.fd_set();
for (const auto &p : j->processes) {
for (const auto &spec : p->redirection_specs()) {
conflicts.add(spec.fd);
@ -985,7 +982,7 @@ bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const job_lineage_t
return false;
}
internal_exec(parser.vars(), j.get(), lineage.block_io);
internal_exec(parser.vars(), j.get(), block_io);
// internal_exec only returns if it failed to set up redirections.
// In case of an successful exec, this code is not reached.
int status = j->flags().negate ? 0 : 1;
@ -1034,8 +1031,8 @@ bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const job_lineage_t
if (p.get() == deferred_process) {
deferred_pipes = std::move(proc_pipes);
} else {
if (!exec_process_in_job(parser, p.get(), j, lineage.block_io, std::move(proc_pipes),
conflicts, deferred_pipes, stdout_read_limit)) {
if (!exec_process_in_job(parser, p.get(), j, block_io, std::move(proc_pipes), conflicts,
deferred_pipes, stdout_read_limit)) {
exec_error = true;
break;
}
@ -1046,9 +1043,8 @@ bool exec_job(parser_t &parser, const shared_ptr<job_t> &j, const job_lineage_t
// Now execute any deferred process.
if (!exec_error && deferred_process) {
assert(deferred_pipes.write.valid() && "Deferred process should always have a write pipe");
if (!exec_process_in_job(parser, deferred_process, j, lineage.block_io,
std::move(deferred_pipes), conflicts, {}, stdout_read_limit,
true)) {
if (!exec_process_in_job(parser, deferred_process, j, block_io, std::move(deferred_pipes),
conflicts, {}, stdout_read_limit, true)) {
exec_error = true;
}
}

View file

@ -13,7 +13,7 @@
#define PIPE_ERROR _(L"An error occurred while setting up pipe")
/// Execute the processes specified by \p j in the parser \p.
bool exec_job(parser_t &parser, const std::shared_ptr<job_t> &j, const job_lineage_t &lineage);
bool exec_job(parser_t &parser, const std::shared_ptr<job_t> &j, const io_chain_t &block_io);
/// Evaluate a command.
///
@ -39,8 +39,7 @@ void exec_close(int fd);
/// Compute the "pgroup provenance" for a job. This is a description of how the pgroup is
/// assigned. It's factored out because the logic has subtleties, and this centralizes it.
pgroup_provenance_t get_pgroup_provenance(const std::shared_ptr<job_t> &j,
const job_lineage_t &lineage);
pgroup_provenance_t get_pgroup_provenance(const std::shared_ptr<job_t> &j);
/// Add signals that should be masked for external processes in this job.
bool blocked_signals_for_job(const job_t &job, sigset_t *sigmask);

View file

@ -88,10 +88,13 @@ static redirection_spec_t get_stderr_merge() {
return redirection_spec_t{STDERR_FILENO, redirection_mode_t::fd, stdout_fileno_str};
}
parse_execution_context_t::parse_execution_context_t(parsed_source_ref_t pstree, parser_t *p,
parse_execution_context_t::parse_execution_context_t(parsed_source_ref_t pstree,
const operation_context_t &ctx,
job_lineage_t lineage)
: pstree(std::move(pstree)), parser(p), ctx(ctx), lineage(std::move(lineage)) {}
io_chain_t block_io)
: pstree(std::move(pstree)),
parser(ctx.parser.get()),
ctx(ctx),
block_io(std::move(block_io)) {}
// Utilities
@ -1251,7 +1254,7 @@ end_execution_reason_t parse_execution_context_t::run_1_job(tnode_t<g::job> job_
bool wants_job_control =
(job_control_mode == job_control_t::all) ||
((job_control_mode == job_control_t::interactive) && parser->is_interactive()) ||
(lineage.job_tree && lineage.job_tree->wants_job_control());
(ctx.job_tree && ctx.job_tree->wants_job_control());
job_t::properties_t props{};
props.wants_terminal = wants_job_control && !ld.is_event;
@ -1285,8 +1288,8 @@ end_execution_reason_t parse_execution_context_t::run_1_job(tnode_t<g::job> job_
// Clean up the job on failure or cancellation.
if (pop_result == end_execution_reason_t::ok) {
// Set the pgroup assignment mode and job tree, now that the job is populated.
job->pgroup_provenance = get_pgroup_provenance(job, lineage);
job_tree_t::populate_tree_for_job(job.get(), lineage.job_tree);
job_tree_t::populate_tree_for_job(job.get(), ctx.job_tree);
job->pgroup_provenance = get_pgroup_provenance(job);
assert(job->job_tree && "Should have a job tree");
// Success. Give the job to the parser - it will clean it up.
@ -1302,7 +1305,7 @@ end_execution_reason_t parse_execution_context_t::run_1_job(tnode_t<g::job> job_
}
// Actually execute the job.
if (!exec_job(*this->parser, job, lineage)) {
if (!exec_job(*this->parser, job, block_io)) {
remove_job(*this->parser, job.get());
}

View file

@ -36,13 +36,18 @@ class parse_execution_context_t {
parsed_source_ref_t pstree;
parser_t *const parser;
const operation_context_t &ctx;
// The currently executing job node, used to indicate the line number.
tnode_t<grammar::job> executing_job_node{};
// Cached line number information.
size_t cached_lineno_offset = 0;
int cached_lineno_count = 0;
// The lineage for any jobs created by this context.
const job_lineage_t lineage;
/// The block IO chain.
/// For example, in `begin; foo ; end < file.txt` this would have the 'file.txt' IO.
io_chain_t block_io{};
// No copying allowed.
parse_execution_context_t(const parse_execution_context_t &) = delete;
parse_execution_context_t &operator=(const parse_execution_context_t &) = delete;
@ -139,8 +144,10 @@ class parse_execution_context_t {
int line_offset_of_character_at_offset(size_t offset);
public:
parse_execution_context_t(parsed_source_ref_t pstree, parser_t *p,
const operation_context_t &ctx, job_lineage_t lineage);
/// Construct a context in preparation for evaluating a node in a tree, with the given block_io.
/// The execution context may access the parser and job_tree through ctx.
parse_execution_context_t(parsed_source_ref_t pstree, const operation_context_t &ctx,
io_chain_t block_io);
/// Returns the current line number, indexed from 1. Not const since it touches
/// cached_lineno_offset.

View file

@ -710,8 +710,8 @@ eval_res_t parser_t::eval_node(const parsed_source_ref_t &ps, tnode_t<T> node,
// Create and set a new execution context.
using exc_ctx_ref_t = std::unique_ptr<parse_execution_context_t>;
scoped_push<exc_ctx_ref_t> exc(&execution_context, make_unique<parse_execution_context_t>(
ps, this, op_ctx, std::move(lineage)));
scoped_push<exc_ctx_ref_t> exc(
&execution_context, make_unique<parse_execution_context_t>(ps, op_ctx, lineage.block_io));
// Check the exec count so we know if anything got executed.
const size_t prev_exec_count = libdata().exec_count;