Shell: Make interrupts kill the whole chain and not just the current job

This makes interrupting `sleep 10; echo hi` not print `hi` anymore,
which is the expected behaviour anyway.
Also fixes the problem with fast-running loops "eating" interrupts and
not quitting.
This commit is contained in:
Ali Mohammad Pur 2022-01-09 09:19:01 +03:30 committed by Ali Mohammad Pur
parent abaee3a325
commit ea66750640
3 changed files with 33 additions and 13 deletions

View file

@ -1190,7 +1190,7 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
return IterationDecision::Continue;
}
if (!shell->has_error(Shell::ShellError::None))
if (shell->has_any_error() && !shell->has_error(Shell::ShellError::InternalControlFlowInterrupted))
return IterationDecision::Break;
if (block_value->is_job()) {
@ -1199,13 +1199,12 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
return IterationDecision::Continue;
shell->block_on_job(job);
if (job->signaled()) {
if (job->termination_signal() == SIGINT)
if (shell->has_any_error()) {
if (shell->has_error(Shell::ShellError::InternalControlFlowInterrupted))
++consecutive_interruptions;
else
if (shell->has_error(Shell::ShellError::InternalControlFlowKilled))
return IterationDecision::Break;
} else {
consecutive_interruptions = 0;
}
}
return IterationDecision::Continue;
@ -1216,11 +1215,16 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
Optional<StringView> index_name = m_index_variable.has_value() ? Optional<StringView>(m_index_variable->name) : Optional<StringView>();
size_t i = 0;
m_iterated_expression->for_each_entry(shell, [&](auto value) {
if (consecutive_interruptions == 2)
if (consecutive_interruptions >= 2)
return IterationDecision::Break;
if (shell && shell->has_any_error())
return IterationDecision::Break;
if (shell) {
if (shell->has_error(Shell::ShellError::InternalControlFlowInterrupted))
shell->take_error();
if (shell->has_any_error())
return IterationDecision::Break;
}
RefPtr<Value> block_value;
@ -1240,11 +1244,16 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
});
} else {
for (;;) {
if (shell && shell->has_any_error())
if (consecutive_interruptions >= 2)
break;
if (consecutive_interruptions == 2)
break;
if (shell) {
if (shell->has_error(Shell::ShellError::InternalControlFlowInterrupted))
shell->take_error();
if (shell->has_any_error())
break;
}
RefPtr<Value> block_value = m_block->run(shell);
if (run(block_value) == IterationDecision::Break)

View file

@ -1765,7 +1765,12 @@ void Shell::notify_child_event()
}
if (child_pid == job.pid()) {
if (WIFSIGNALED(wstatus) && !WIFSTOPPED(wstatus)) {
job.set_signalled(WTERMSIG(wstatus));
auto signal = WTERMSIG(wstatus);
job.set_signalled(signal);
if (signal == SIGINT)
raise_error(ShellError::InternalControlFlowInterrupted, "Interrupted"sv, job.command().position);
else if (signal == SIGKILL)
raise_error(ShellError::InternalControlFlowKilled, "Interrupted"sv, job.command().position);
} else if (WIFEXITED(wstatus)) {
job.set_has_exit(WEXITSTATUS(wstatus));
} else if (WIFSTOPPED(wstatus)) {
@ -1998,6 +2003,8 @@ void Shell::possibly_print_error() const
break;
case ShellError::InternalControlFlowBreak:
case ShellError::InternalControlFlowContinue:
case ShellError::InternalControlFlowInterrupted:
case ShellError::InternalControlFlowKilled:
return;
case ShellError::None:
return;

View file

@ -228,6 +228,8 @@ public:
None,
InternalControlFlowBreak,
InternalControlFlowContinue,
InternalControlFlowInterrupted,
InternalControlFlowKilled,
EvaluatedSyntaxError,
NonExhaustiveMatchRules,
InvalidGlobError,
@ -260,6 +262,8 @@ public:
switch (error) {
case ShellError::InternalControlFlowBreak:
case ShellError::InternalControlFlowContinue:
case ShellError::InternalControlFlowInterrupted:
case ShellError::InternalControlFlowKilled:
return true;
default:
return false;