mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-15 01:17:45 +00:00
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:
parent
a4b66d948b
commit
55db918d59
5 changed files with 36 additions and 31 deletions
26
src/exec.cpp
26
src/exec.cpp
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue