Factor the exit state to make exit handlers more explicit

This adds a new type 'exit_state_t' which encapsulates where fish is in
the process of exiting. This makes it explicit when fish wants to cancel
"ordinary" fish script but still run exit handlers.

There should be no user-visible behavior change here; this is just
refactoring in preparation for the next commit.
This commit is contained in:
ridiculousfish 2020-08-29 15:14:33 -07:00
parent 0d3f4db33a
commit 0b075fce88
4 changed files with 30 additions and 11 deletions

View file

@ -226,7 +226,7 @@ process_type_t parse_execution_context_t::process_type_for_command(
} }
maybe_t<end_execution_reason_t> parse_execution_context_t::check_end_execution() const { maybe_t<end_execution_reason_t> parse_execution_context_t::check_end_execution() const {
if (this->cancel_signal || ctx.check_cancel() || reader_exit_forced()) { if (this->cancel_signal || ctx.check_cancel() || check_cancel_from_fish_signal()) {
return end_execution_reason_t::cancelled; return end_execution_reason_t::cancelled;
} }
const auto &ld = parser->libdata(); const auto &ld = parser->libdata();

View file

@ -1012,7 +1012,7 @@ void job_t::continue_job(parser_t &parser, bool in_foreground) {
if (in_foreground) { if (in_foreground) {
// Wait for the status of our own job to change. // Wait for the status of our own job to change.
while (!reader_exit_forced() && !is_stopped() && !is_completed()) { while (!check_cancel_from_fish_signal() && !is_stopped() && !is_completed()) {
process_mark_finished_children(parser, true); process_mark_finished_children(parser, true);
} }
} }

View file

@ -727,8 +727,13 @@ static void term_fix_modes(struct termios *modes) {
modes->c_cc[VSTART] = disabling_char; modes->c_cc[VSTART] = disabling_char;
} }
/// If set, we are committed to exiting. This latches to true. /// A description of where fish is in the process of exiting.
static relaxed_atomic_t<bool> s_exit_forced{false}; enum class exit_state_t {
none, /// fish is not exiting.
running_handlers, /// fish intends to exit, and is running handlers like 'fish_exit'.
finished_handlers, /// fish is finished running handlers and no more fish script may be run.
};
static relaxed_atomic_t<exit_state_t> s_exit_state{exit_state_t::none};
/// If set, SIGHUP has been received. This latches to true. /// If set, SIGHUP has been received. This latches to true.
/// This is set from a signal handler. /// This is set from a signal handler.
@ -798,7 +803,20 @@ static void term_steal() {
termsize_container_t::shared().invalidate_tty(); termsize_container_t::shared().invalidate_tty();
} }
bool reader_exit_forced() { return s_exit_forced; } bool check_cancel_from_fish_signal() {
switch (s_exit_state) {
case exit_state_t::none:
// No reason to exit now.
return false;
case exit_state_t::running_handlers:
// We intend to exit but we want to allow these handlers to run.
return false;
case exit_state_t::finished_handlers:
// Done running exit handlers, time to exit.
return true;
}
DIE("Unreachable");
}
/// Given a command line and an autosuggestion, return the string that gets shown to the user. /// Given a command line and an autosuggestion, return the string that gets shown to the user.
wcstring combine_command_and_autosuggestion(const wcstring &cmdline, wcstring combine_command_and_autosuggestion(const wcstring &cmdline,
@ -2554,10 +2572,10 @@ static int read_i(parser_t &parser) {
// If we are the last reader, then kill remaining jobs before exiting. // If we are the last reader, then kill remaining jobs before exiting.
if (reader_data_stack.size() == 0) { if (reader_data_stack.size() == 0) {
// Once s_exit_forced is set, nothing more can be executed, // Send the exit event and then commit to not executing any more fish script.
// so send the exit event now. s_exit_state = exit_state_t::running_handlers;
event_fire_generic(parser, L"fish_exit"); event_fire_generic(parser, L"fish_exit");
s_exit_forced = true; s_exit_state = exit_state_t::finished_handlers;
hup_jobs(parser.jobs()); hup_jobs(parser.jobs());
} }
@ -3688,7 +3706,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
pager.clear(); pager.clear();
} }
if (!reader_exit_forced()) { if (s_exit_state != exit_state_t::finished_handlers) {
// The order of the two conditions below is important. Try to restore the mode // The order of the two conditions below is important. Try to restore the mode
// in all cases, but only complain if interactive. // in all cases, but only complain if interactive.
if (tcsetattr(0, TCSANOW, &old_modes) == -1 && if (tcsetattr(0, TCSANOW, &old_modes) == -1 &&

View file

@ -232,8 +232,9 @@ void reader_pop();
/// The readers interrupt signal handler. Cancels all currently running blocks. /// The readers interrupt signal handler. Cancels all currently running blocks.
void reader_handle_sigint(); void reader_handle_sigint();
/// This function returns true if fish is exiting by force, i.e. because stdin died. /// \return whether we should cancel fish script due to fish itself receiving a signal.
bool reader_exit_forced(); /// TODO: this doesn't belong in reader.
bool check_cancel_from_fish_signal();
/// Test whether the interactive reader is in search mode. /// Test whether the interactive reader is in search mode.
bool reader_is_in_search_mode(); bool reader_is_in_search_mode();