mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-14 17:07:44 +00:00
Rename job_tree to job_group
Initially I wanted to pick a different name to avoid confusion with process groups, but really job trees *are* process groups. So name them to reflect that fact. Also rename "placeholder" to "internal" which is clearer.
This commit is contained in:
parent
b119c4b3bb
commit
7cc99a2d80
13 changed files with 99 additions and 101 deletions
|
@ -59,7 +59,7 @@ int builtin_eval(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
}
|
||||
|
||||
int status = STATUS_CMD_OK;
|
||||
auto res = parser.eval(new_cmd, ios, streams.job_tree);
|
||||
auto res = parser.eval(new_cmd, ios, streams.job_group);
|
||||
if (res.was_empty) {
|
||||
// Issue #5692, in particular, to catch `eval ""`, `eval "begin; end;"`, etc.
|
||||
// where we have an argument but nothing is executed.
|
||||
|
|
27
src/exec.cpp
27
src/exec.cpp
|
@ -175,7 +175,7 @@ static void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &block_i
|
|||
/// If our pgroup assignment mode wants us to use the first external proc, then apply it here.
|
||||
/// \returns the job's pgid, which should always be set to something valid after this call.
|
||||
static pid_t maybe_assign_pgid_from_child(const std::shared_ptr<job_t> &j, pid_t child_pid) {
|
||||
auto &jt = j->job_tree;
|
||||
auto &jt = j->group;
|
||||
if (jt->needs_pgid_assignment()) {
|
||||
jt->set_pgid(child_pid);
|
||||
}
|
||||
|
@ -309,8 +309,7 @@ bool blocked_signals_for_job(const job_t &job, sigset_t *sigmask) {
|
|||
static bool fork_child_for_process(const std::shared_ptr<job_t> &job, process_t *p,
|
||||
const dup2_list_t &dup2s, const char *fork_type,
|
||||
const std::function<void()> &child_action) {
|
||||
assert(!job->job_tree->is_placeholder() &&
|
||||
"Placeholders are for internal functions, they should never fork");
|
||||
assert(!job->group->is_internal() && "Internal groups should never need to fork");
|
||||
pid_t pid = execute_fork();
|
||||
if (pid == 0) {
|
||||
// This is the child process. Setup redirections, print correct output to
|
||||
|
@ -637,15 +636,15 @@ static proc_performer_t get_performer_for_process(process_t *p, job_t *job,
|
|||
const io_chain_t &io_chain) {
|
||||
assert((p->type == process_type_t::function || p->type == process_type_t::block_node) &&
|
||||
"Unexpected process type");
|
||||
// We want to capture the job tree.
|
||||
job_tree_ref_t job_tree = job->job_tree;
|
||||
// We want to capture the job group.
|
||||
job_group_ref_t job_group = job->group;
|
||||
|
||||
if (p->type == process_type_t::block_node) {
|
||||
const parsed_source_ref_t &source = p->block_node_source;
|
||||
tnode_t<grammar::statement> node = p->internal_block_node;
|
||||
assert(source && node && "Process is missing node info");
|
||||
return [=](parser_t &parser) {
|
||||
return parser.eval_node(source, node, io_chain, job_tree).status;
|
||||
return parser.eval_node(source, node, io_chain, job_group).status;
|
||||
};
|
||||
} else {
|
||||
assert(p->type == process_type_t::function);
|
||||
|
@ -659,7 +658,7 @@ static proc_performer_t get_performer_for_process(process_t *p, job_t *job,
|
|||
// Pull out the job list from the function.
|
||||
tnode_t<grammar::job_list> body = props->func_node.child<1>();
|
||||
const block_t *fb = function_prepare_environment(parser, *argv, *props);
|
||||
auto res = parser.eval_node(props->parsed_source, body, io_chain, job_tree);
|
||||
auto res = parser.eval_node(props->parsed_source, body, io_chain, job_group);
|
||||
function_restore_environment(parser, fb);
|
||||
|
||||
// If the function did not execute anything, treat it as success.
|
||||
|
@ -832,7 +831,7 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, const std::share
|
|||
|
||||
case process_type_t::builtin: {
|
||||
io_streams_t builtin_io_streams{stdout_read_limit};
|
||||
builtin_io_streams.job_tree = j->job_tree;
|
||||
builtin_io_streams.job_group = j->group;
|
||||
if (!exec_internal_builtin_proc(parser, p, pipe_read.get(), process_net_io_chain,
|
||||
builtin_io_streams)) {
|
||||
return false;
|
||||
|
@ -1066,14 +1065,14 @@ static void populate_subshell_output(wcstring_list_t *lst, const io_buffer_t &bu
|
|||
|
||||
/// Execute \p cmd in a subshell in \p parser. If \p lst is not null, populate it with the output.
|
||||
/// Return $status in \p out_status.
|
||||
/// If \p job_tree is set, any spawned commands should join that job tree.
|
||||
/// If \p job_group is set, any spawned commands should join that job group.
|
||||
/// If \p apply_exit_status is false, then reset $status back to its original value.
|
||||
/// \p is_subcmd controls whether we apply a read limit.
|
||||
/// \p break_expand is used to propagate whether the result should be "expansion breaking" in the
|
||||
/// sense that subshells used during string expansion should halt that expansion. \return the value
|
||||
/// of $status.
|
||||
static int exec_subshell_internal(const wcstring &cmd, parser_t &parser,
|
||||
const job_tree_ref_t &job_tree, wcstring_list_t *lst,
|
||||
const job_group_ref_t &job_group, wcstring_list_t *lst,
|
||||
bool *break_expand, bool apply_exit_status, bool is_subcmd) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
auto &ld = parser.libdata();
|
||||
|
@ -1097,7 +1096,7 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser,
|
|||
*break_expand = true;
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
eval_res_t eval_res = parser.eval(cmd, io_chain_t{bufferfill}, job_tree, block_type_t::subst);
|
||||
eval_res_t eval_res = parser.eval(cmd, io_chain_t{bufferfill}, job_group, block_type_t::subst);
|
||||
std::shared_ptr<io_buffer_t> buffer = io_bufferfill_t::finish(std::move(bufferfill));
|
||||
if (buffer->buffer().discarded()) {
|
||||
*break_expand = true;
|
||||
|
@ -1116,11 +1115,11 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser,
|
|||
return eval_res.status.status_value();
|
||||
}
|
||||
|
||||
int exec_subshell_for_expand(const wcstring &cmd, parser_t &parser, const job_tree_ref_t &job_tree,
|
||||
wcstring_list_t &outputs) {
|
||||
int exec_subshell_for_expand(const wcstring &cmd, parser_t &parser,
|
||||
const job_group_ref_t &job_group, wcstring_list_t &outputs) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
bool break_expand = false;
|
||||
int ret = exec_subshell_internal(cmd, parser, job_tree, &outputs, &break_expand, true, true);
|
||||
int ret = exec_subshell_internal(cmd, parser, job_group, &outputs, &break_expand, true, true);
|
||||
// Only return an error code if we should break expansion.
|
||||
return break_expand ? ret : STATUS_CMD_OK;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ int exec_subshell(const wcstring &cmd, parser_t &parser, wcstring_list_t &output
|
|||
/// "success" (even though the command may have failed), a non-zero return means that we should
|
||||
/// halt expansion. If the \p pgid is supplied, then any spawned external commands should join that
|
||||
/// pgroup.
|
||||
int exec_subshell_for_expand(const wcstring &cmd, parser_t &parser, const job_tree_ref_t &job_tree,
|
||||
wcstring_list_t &outputs);
|
||||
int exec_subshell_for_expand(const wcstring &cmd, parser_t &parser,
|
||||
const job_group_ref_t &job_group, wcstring_list_t &outputs);
|
||||
|
||||
/// Loops over close until the syscall was run without being interrupted.
|
||||
void exec_close(int fd);
|
||||
|
|
|
@ -622,7 +622,7 @@ static expand_result_t expand_cmdsubst(wcstring input, const operation_context_t
|
|||
|
||||
wcstring_list_t sub_res;
|
||||
const wcstring subcmd(paren_begin + 1, paren_end - paren_begin - 1);
|
||||
int subshell_status = exec_subshell_for_expand(subcmd, *ctx.parser, ctx.job_tree, sub_res);
|
||||
int subshell_status = exec_subshell_for_expand(subcmd, *ctx.parser, ctx.job_group, sub_res);
|
||||
if (subshell_status != 0) {
|
||||
// TODO: Ad-hoc switch, how can we enumerate the possible errors more safely?
|
||||
const wchar_t *err;
|
||||
|
|
8
src/io.h
8
src/io.h
|
@ -21,7 +21,7 @@
|
|||
|
||||
using std::shared_ptr;
|
||||
|
||||
class job_tree_t;
|
||||
class job_group_t;
|
||||
|
||||
/// A simple set of FDs.
|
||||
struct fd_set_t {
|
||||
|
@ -464,10 +464,10 @@ struct io_streams_t {
|
|||
// Actual IO redirections. This is only used by the source builtin. Unowned.
|
||||
const io_chain_t *io_chain{nullptr};
|
||||
|
||||
// The job tree of the job, if any. This enables builtins which run more code like eval() to
|
||||
// The job group of the job, if any. This enables builtins which run more code like eval() to
|
||||
// share pgid.
|
||||
// TODO: this is awkwardly placed.
|
||||
std::shared_ptr<job_tree_t> job_tree{};
|
||||
// FIXME: this is awkwardly placed.
|
||||
std::shared_ptr<job_group_t> job_group{};
|
||||
|
||||
// io_streams_t cannot be copied.
|
||||
io_streams_t(const io_streams_t &) = delete;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
class environment_t;
|
||||
class parser_t;
|
||||
class job_tree_t;
|
||||
class job_group_t;
|
||||
|
||||
/// A common helper which always returns false.
|
||||
bool no_cancel();
|
||||
|
@ -30,10 +30,10 @@ class operation_context_t {
|
|||
// context itself.
|
||||
const environment_t &vars;
|
||||
|
||||
/// The job tree of the parental job.
|
||||
/// The job group of the parental job.
|
||||
/// This is used only when expanding command substitutions. If this is set, any jobs created by
|
||||
/// the command substitions should use this tree.
|
||||
std::shared_ptr<job_tree_t> job_tree{};
|
||||
std::shared_ptr<job_group_t> job_group{};
|
||||
|
||||
// A function which may be used to poll for cancellation.
|
||||
cancel_checker_t cancel_checker;
|
||||
|
|
|
@ -1254,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()) ||
|
||||
(ctx.job_tree && ctx.job_tree->wants_job_control());
|
||||
(ctx.job_group && ctx.job_group->wants_job_control());
|
||||
|
||||
job_t::properties_t props{};
|
||||
props.wants_terminal = wants_job_control && !ld.is_event;
|
||||
|
@ -1287,9 +1287,9 @@ 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_tree_t::populate_tree_for_job(job.get(), ctx.job_tree);
|
||||
assert(job->job_tree && "Should have a job tree");
|
||||
// Set the pgroup assignment mode and job group, now that the job is populated.
|
||||
job_group_t::populate_tree_for_job(job.get(), ctx.job_group);
|
||||
assert(job->group && "Should have a job group");
|
||||
|
||||
// Success. Give the job to the parser - it will clean it up.
|
||||
parser->job_add(job);
|
||||
|
|
|
@ -145,7 +145,7 @@ class parse_execution_context_t {
|
|||
|
||||
public:
|
||||
/// 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.
|
||||
/// The execution context may access the parser and group through ctx.
|
||||
parse_execution_context_t(parsed_source_ref_t pstree, const operation_context_t &ctx,
|
||||
io_chain_t block_io);
|
||||
|
||||
|
|
|
@ -632,12 +632,12 @@ profile_item_t *parser_t::create_profile_item() {
|
|||
return result;
|
||||
}
|
||||
|
||||
eval_res_t parser_t::eval(const wcstring &cmd, const io_chain_t &io, const job_tree_ref_t &job_tree,
|
||||
enum block_type_t block_type) {
|
||||
eval_res_t parser_t::eval(const wcstring &cmd, const io_chain_t &io,
|
||||
const job_group_ref_t &job_group, enum block_type_t block_type) {
|
||||
// Parse the source into a tree, if we can.
|
||||
parse_error_list_t error_list;
|
||||
if (parsed_source_ref_t ps = parse_source(cmd, parse_flag_none, &error_list)) {
|
||||
return this->eval(ps, io, job_tree, block_type);
|
||||
return this->eval(ps, io, job_group, block_type);
|
||||
} else {
|
||||
// Get a backtrace. This includes the message.
|
||||
wcstring backtrace_and_desc;
|
||||
|
@ -654,12 +654,12 @@ eval_res_t parser_t::eval(const wcstring &cmd, const io_chain_t &io, const job_t
|
|||
}
|
||||
|
||||
eval_res_t parser_t::eval(const parsed_source_ref_t &ps, const io_chain_t &io,
|
||||
const job_tree_ref_t &job_tree, enum block_type_t block_type) {
|
||||
const job_group_ref_t &job_group, enum block_type_t block_type) {
|
||||
assert(block_type == block_type_t::top || block_type == block_type_t::subst);
|
||||
if (!ps->tree.empty()) {
|
||||
// Execute the first node.
|
||||
tnode_t<grammar::job_list> start{&ps->tree, &ps->tree.front()};
|
||||
return this->eval_node(ps, start, io, job_tree, block_type);
|
||||
return this->eval_node(ps, start, io, job_group, block_type);
|
||||
} else {
|
||||
auto status = proc_status_t::from_exit_code(get_last_status());
|
||||
bool break_expand = false;
|
||||
|
@ -670,7 +670,7 @@ eval_res_t parser_t::eval(const parsed_source_ref_t &ps, const io_chain_t &io,
|
|||
|
||||
template <typename T>
|
||||
eval_res_t parser_t::eval_node(const parsed_source_ref_t &ps, tnode_t<T> node,
|
||||
const io_chain_t &block_io, const job_tree_ref_t &job_tree,
|
||||
const io_chain_t &block_io, const job_group_ref_t &job_group,
|
||||
block_type_t block_type) {
|
||||
static_assert(
|
||||
std::is_same<T, grammar::statement>::value || std::is_same<T, grammar::job_list>::value,
|
||||
|
@ -695,8 +695,8 @@ eval_res_t parser_t::eval_node(const parsed_source_ref_t &ps, tnode_t<T> node,
|
|||
operation_context_t op_ctx = this->context();
|
||||
block_t *scope_block = this->push_block(block_t::scope_block(block_type));
|
||||
|
||||
// Propogate our job tree.
|
||||
op_ctx.job_tree = job_tree;
|
||||
// Propogate our job group.
|
||||
op_ctx.job_group = job_group;
|
||||
|
||||
// Create and set a new execution context.
|
||||
using exc_ctx_ref_t = std::unique_ptr<parse_execution_context_t>;
|
||||
|
@ -726,9 +726,9 @@ eval_res_t parser_t::eval_node(const parsed_source_ref_t &ps, tnode_t<T> node,
|
|||
|
||||
// Explicit instantiations. TODO: use overloads instead?
|
||||
template eval_res_t parser_t::eval_node(const parsed_source_ref_t &, tnode_t<grammar::statement>,
|
||||
const io_chain_t &, const job_tree_ref_t &, block_type_t);
|
||||
const io_chain_t &, const job_group_ref_t &, block_type_t);
|
||||
template eval_res_t parser_t::eval_node(const parsed_source_ref_t &, tnode_t<grammar::job_list>,
|
||||
const io_chain_t &, const job_tree_ref_t &, block_type_t);
|
||||
const io_chain_t &, const job_group_ref_t &, block_type_t);
|
||||
|
||||
void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors,
|
||||
wcstring &output) const {
|
||||
|
|
|
@ -282,27 +282,28 @@ class parser_t : public std::enable_shared_from_this<parser_t> {
|
|||
///
|
||||
/// \param cmd the string to evaluate
|
||||
/// \param io io redirections to perform on all started jobs
|
||||
/// \param job_tree if set, the job tree to give to spawned jobs.
|
||||
/// \param job_group if set, the job group to give to spawned jobs.
|
||||
/// \param block_type The type of block to push on the block stack, which must be either 'top'
|
||||
/// or 'subst'.
|
||||
/// \param break_expand If not null, return by reference whether the error ought to be an expand
|
||||
/// error. This includes nested expand errors, and command-not-found.
|
||||
///
|
||||
/// \return the result of evaluation.
|
||||
eval_res_t eval(const wcstring &cmd, const io_chain_t &io, const job_tree_ref_t &job_tree = {},
|
||||
eval_res_t eval(const wcstring &cmd, const io_chain_t &io,
|
||||
const job_group_ref_t &job_group = {},
|
||||
block_type_t block_type = block_type_t::top);
|
||||
|
||||
/// Evaluate the parsed source ps.
|
||||
/// Because the source has been parsed, a syntax error is impossible.
|
||||
eval_res_t eval(const parsed_source_ref_t &ps, const io_chain_t &io,
|
||||
const job_tree_ref_t &job_tree = {},
|
||||
const job_group_ref_t &job_group = {},
|
||||
block_type_t block_type = block_type_t::top);
|
||||
|
||||
/// Evaluates a node.
|
||||
/// The node type must be grammar::statement or grammar::job_list.
|
||||
template <typename T>
|
||||
eval_res_t eval_node(const parsed_source_ref_t &ps, tnode_t<T> node, const io_chain_t &block_io,
|
||||
const job_tree_ref_t &job_tree,
|
||||
const job_group_ref_t &job_group,
|
||||
block_type_t block_type = block_type_t::top);
|
||||
|
||||
/// Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and
|
||||
|
|
|
@ -205,11 +205,11 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr,
|
|||
// desired_pgid tracks the pgroup for the process. If it is none, the pgroup is left unchanged.
|
||||
// If it is zero, create a new pgroup from the pid. If it is >0, join that pgroup.
|
||||
maybe_t<pid_t> desired_pgid = none();
|
||||
if (auto job_pgid = j->job_tree->get_pgid()) {
|
||||
if (auto job_pgid = j->group->get_pgid()) {
|
||||
desired_pgid = *job_pgid;
|
||||
} else {
|
||||
assert(j->job_tree->needs_pgid_assignment() && "We should be expecting a pgid");
|
||||
// We are the first external proc in the job tree. Set the desired_pgid to 0 to indicate we
|
||||
assert(j->group->needs_pgid_assignment() && "We should be expecting a pgid");
|
||||
// We are the first external proc in the job group. Set the desired_pgid to 0 to indicate we
|
||||
// should creating a new process group.
|
||||
desired_pgid = 0;
|
||||
}
|
||||
|
|
50
src/proc.cpp
50
src/proc.cpp
|
@ -164,7 +164,7 @@ bool job_t::should_report_process_exits() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool job_t::job_chain_is_fully_constructed() const { return job_tree->is_root_constructed(); }
|
||||
bool job_t::job_chain_is_fully_constructed() const { return group->is_root_constructed(); }
|
||||
|
||||
bool job_t::signal(int signal) {
|
||||
// Presumably we are distinguishing between the two cases below because we do
|
||||
|
@ -241,38 +241,38 @@ void print_exit_warning_for_jobs(const job_list_t &jobs) {
|
|||
fputws(_(L"Use 'disown PID' to remove jobs from the list without terminating them.\n"), stdout);
|
||||
}
|
||||
|
||||
job_tree_t::job_tree_t(bool job_control, bool placeholder)
|
||||
job_group_t::job_group_t(bool job_control, bool internal)
|
||||
: job_control_(job_control),
|
||||
is_placeholder_(placeholder),
|
||||
job_id_(placeholder ? -1 : acquire_job_id()) {}
|
||||
is_internal_(internal),
|
||||
job_id_(internal ? -1 : acquire_job_id()) {}
|
||||
|
||||
job_tree_t::~job_tree_t() {
|
||||
job_group_t::~job_group_t() {
|
||||
if (job_id_ > 0) {
|
||||
release_job_id(job_id_);
|
||||
}
|
||||
}
|
||||
|
||||
void job_tree_t::set_pgid(pid_t pgid) {
|
||||
// Note we need not be concerned about thread safety. job_trees are intended to be shared across
|
||||
// threads, but their pgid should always have been set beforehand.
|
||||
void job_group_t::set_pgid(pid_t pgid) {
|
||||
// Note we need not be concerned about thread safety. job_groups are intended to be shared
|
||||
// across threads, but their pgid should always have been set beforehand.
|
||||
assert(needs_pgid_assignment() && "We should not be setting a pgid");
|
||||
assert(pgid >= 0 && "Invalid pgid");
|
||||
pgid_ = pgid;
|
||||
}
|
||||
|
||||
maybe_t<pid_t> job_tree_t::get_pgid() const { return pgid_; }
|
||||
maybe_t<pid_t> job_group_t::get_pgid() const { return pgid_; }
|
||||
|
||||
void job_tree_t::populate_tree_for_job(job_t *job, const job_tree_ref_t &proposed) {
|
||||
void job_group_t::populate_tree_for_job(job_t *job, const job_group_ref_t &proposed) {
|
||||
// Note there's three cases to consider:
|
||||
// nullptr -> this is a root job, there is no inherited job tree
|
||||
// placeholder -> the parent is running as part of a simple function execution
|
||||
// We may need to create a new job tree if we are going to fork.
|
||||
// non-placeholder -> we are running as part of a real pipeline
|
||||
// Decide if this job can use the placeholder tree.
|
||||
// nullptr -> this is a root job, there is no inherited job group
|
||||
// internal -> the parent is running as part of a simple function execution
|
||||
// We may need to create a new job group if we are going to fork.
|
||||
// non-internal -> we are running as part of a real pipeline
|
||||
// Decide if this job can use an internal tree.
|
||||
// This is true if it's a simple foreground execution of an internal proc.
|
||||
bool first_proc_internal = job->processes.front()->is_internal();
|
||||
bool can_use_placeholder = !job->is_initially_background() && job->processes.size() == 1 &&
|
||||
job->processes.front()->is_internal();
|
||||
bool can_use_internal = !job->is_initially_background() && job->processes.size() == 1 &&
|
||||
job->processes.front()->is_internal();
|
||||
|
||||
bool needs_new_tree = false;
|
||||
if (!proposed) {
|
||||
|
@ -281,8 +281,8 @@ void job_tree_t::populate_tree_for_job(job_t *job, const job_tree_ref_t &propose
|
|||
} else if (!job->is_foreground()) {
|
||||
// Background jobs always get a new tree.
|
||||
needs_new_tree = true;
|
||||
} else if (proposed->is_placeholder() && !can_use_placeholder) {
|
||||
// We cannot use the placeholder tree for this job.
|
||||
} else if (proposed->is_internal() && !can_use_internal) {
|
||||
// We cannot use the internal tree for this job.
|
||||
needs_new_tree = true;
|
||||
}
|
||||
|
||||
|
@ -290,18 +290,18 @@ void job_tree_t::populate_tree_for_job(job_t *job, const job_tree_ref_t &propose
|
|||
bool job_control = job->wants_job_control();
|
||||
|
||||
if (!needs_new_tree) {
|
||||
job->job_tree = proposed;
|
||||
} else if (can_use_placeholder) {
|
||||
job->job_tree.reset(new job_tree_t(job_control, true));
|
||||
job->group = proposed;
|
||||
} else if (can_use_internal) {
|
||||
job->group.reset(new job_group_t(job_control, true));
|
||||
} else {
|
||||
job->job_tree.reset(new job_tree_t(job_control, false));
|
||||
job->group.reset(new job_group_t(job_control, false));
|
||||
|
||||
// Perhaps this job should immediately live in fish's pgroup.
|
||||
// There's two reasons why it may be so:
|
||||
// 1. The job doesn't need job control.
|
||||
// 2. The first process in the job is internal to fish; this needs to own the tty.
|
||||
if (!job_control || first_proc_internal) {
|
||||
job->job_tree->set_pgid(getpgrp());
|
||||
job->group->set_pgid(getpgrp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ void job_t::mark_constructed() {
|
|||
assert(!is_constructed() && "Job was already constructed");
|
||||
mut_flags().constructed = true;
|
||||
if (flags().is_tree_root) {
|
||||
job_tree->mark_root_constructed();
|
||||
group->mark_root_constructed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
56
src/proc.h
56
src/proc.h
|
@ -160,26 +160,25 @@ using job_id_t = int;
|
|||
job_id_t acquire_job_id(void);
|
||||
void release_job_id(job_id_t jid);
|
||||
|
||||
/// job_tree_t is conceptually similar to the idea of a process group. It represents data which
|
||||
/// job_group_t is conceptually similar to the idea of a process group. It represents data which
|
||||
/// is shared among all of the "subjobs" that may be spawned by a single job.
|
||||
/// For example, two fish functions in a pipeline may themselves spawn multiple jobs, but all will
|
||||
/// share the same job tree.
|
||||
/// There is also a notion of a "placeholder" job tree. Placeholders are used when executing a
|
||||
/// foreground function or block. These are not jobs as the user understands them - they do not
|
||||
/// consume a job ID, they do not show up in job lists, and they do not have a pgid because they
|
||||
/// contain no external procs.
|
||||
/// Note that job_tree_t is intended to eventually be shared between threads, and so must be thread
|
||||
/// safe.
|
||||
/// share the same job group.
|
||||
/// There is also a notion of a "internal" job group. Internal groups are used when executing a
|
||||
/// foreground function or block with no pipeline. These are not jobs as the user understands them -
|
||||
/// they do not consume a job ID, they do not show up in job lists, and they do not have a pgid
|
||||
/// because they contain no external procs. Note that job_group_t is intended to eventually be
|
||||
/// shared between threads, and so must be thread safe.
|
||||
class job_t;
|
||||
class job_tree_t;
|
||||
using job_tree_ref_t = std::shared_ptr<job_tree_t>;
|
||||
class job_group_t;
|
||||
using job_group_ref_t = std::shared_ptr<job_group_t>;
|
||||
|
||||
class job_tree_t {
|
||||
class job_group_t {
|
||||
public:
|
||||
/// Set the pgid for this job tree, latching it to this value.
|
||||
/// Set the pgid for this job group, latching it to this value.
|
||||
/// The pgid should not already have been set.
|
||||
/// Of course this does not keep the pgid alive by itself.
|
||||
/// The placeholder job tree does not have a pgid and it is an error to set it.
|
||||
/// An internal job group does not have a pgid and it is an error to set it.
|
||||
void set_pgid(pid_t pgid);
|
||||
|
||||
/// Get the pgid, or none() if it has not been set.
|
||||
|
@ -188,12 +187,12 @@ class job_tree_t {
|
|||
/// \return whether we want job control
|
||||
bool wants_job_control() const { return job_control_; }
|
||||
|
||||
/// \return whether this is a placeholder.
|
||||
bool is_placeholder() const { return is_placeholder_; }
|
||||
/// \return whether this is an internal group.
|
||||
bool is_internal() const { return is_internal_; }
|
||||
|
||||
/// \return whether this job tree is awaiting a pgid.
|
||||
/// This is true for non-placeholder trees that don't already have a pgid.
|
||||
bool needs_pgid_assignment() const { return !is_placeholder_ && !pgid_.has_value(); }
|
||||
/// \return whether this job group is awaiting a pgid.
|
||||
/// This is true for non-internal trees that don't already have a pgid.
|
||||
bool needs_pgid_assignment() const { return !is_internal_ && !pgid_.has_value(); }
|
||||
|
||||
/// \return the job ID, or -1 if none.
|
||||
job_id_t get_id() const { return job_id_; }
|
||||
|
@ -204,20 +203,20 @@ class job_tree_t {
|
|||
void mark_root_constructed() { root_constructed_ = true; };
|
||||
bool is_root_constructed() const { return root_constructed_; }
|
||||
|
||||
/// Given a job and a proposed job tree (possibly null), populate the job's tree.
|
||||
/// Given a job and a proposed job group (possibly null), populate the job's tree.
|
||||
/// The proposed tree is the tree from the parent job, or null if this is a root.
|
||||
static void populate_tree_for_job(job_t *job, const job_tree_ref_t &proposed_tree);
|
||||
static void populate_tree_for_job(job_t *job, const job_group_ref_t &proposed_tree);
|
||||
|
||||
~job_tree_t();
|
||||
~job_group_t();
|
||||
|
||||
private:
|
||||
maybe_t<pid_t> pgid_{};
|
||||
const bool job_control_;
|
||||
const bool is_placeholder_;
|
||||
const bool is_internal_;
|
||||
const job_id_t job_id_;
|
||||
relaxed_atomic_bool_t root_constructed_{};
|
||||
|
||||
explicit job_tree_t(bool job_control, bool placeholder);
|
||||
explicit job_group_t(bool job_control, bool internal);
|
||||
};
|
||||
|
||||
/// A structure representing a single fish process. Contains variables for tracking process state
|
||||
|
@ -347,7 +346,6 @@ using job_id_t = int;
|
|||
job_id_t acquire_job_id(void);
|
||||
void release_job_id(job_id_t jid);
|
||||
|
||||
|
||||
/// A struct representing a job. A job is a pipeline of one or more processes.
|
||||
class job_t {
|
||||
public:
|
||||
|
@ -439,22 +437,22 @@ class job_t {
|
|||
/// All the processes in this job.
|
||||
process_list_t processes;
|
||||
|
||||
// The tree containing this job.
|
||||
// The group containing this job.
|
||||
// This is never null and not changed after construction.
|
||||
job_tree_ref_t job_tree{};
|
||||
job_group_ref_t group{};
|
||||
|
||||
/// Process group ID for the process group that this job is running in.
|
||||
/// Set to a nonexistent, non-return-value of getpgid() integer by the constructor
|
||||
// pid_t pgid{INVALID_PID};
|
||||
|
||||
/// \return the pgid for the job, based on the job tree.
|
||||
/// \return the pgid for the job, based on the job group.
|
||||
/// This may be none if the job consists of just internal fish functions or builtins.
|
||||
/// This may also be fish itself.
|
||||
maybe_t<pid_t> get_pgid() const { return job_tree->get_pgid(); }
|
||||
maybe_t<pid_t> get_pgid() const { return group->get_pgid(); }
|
||||
|
||||
/// The id of this job.
|
||||
/// This is user-visible, is recycled, and may be -1.
|
||||
job_id_t job_id() const { return job_tree->get_id(); }
|
||||
job_id_t job_id() const { return group->get_id(); }
|
||||
|
||||
/// A non-user-visible, never-recycled job ID.
|
||||
const internal_job_id_t internal_job_id;
|
||||
|
|
Loading…
Reference in a new issue