Repair control-C cancellation of loops

Commit ab189a75 introduced a regression where we stop breaking out
of loops in response to a child death via a signal. Fix that regression.

Also introduces a test to help ensure we don't regress in the future.

Fixes #3780
This commit is contained in:
ridiculousfish 2017-01-26 13:39:19 -08:00
parent 98b561929f
commit 1d9cc12984
2 changed files with 12 additions and 4 deletions

View file

@ -757,6 +757,15 @@ static void test_cancellation() {
test_1_cancellation(L"while true ; echo nothing > /dev/null; end"); test_1_cancellation(L"while true ; echo nothing > /dev/null; end");
test_1_cancellation(L"for i in (while true ; end) ; end"); test_1_cancellation(L"for i in (while true ; end) ; end");
// Ensure that if child processes SIGINT, we exit our loops
// Test for #3780
// Ugly hack - temporarily set is_interactive_session
// else we will SIGINT ourselves in response to our child death
scoped_push<int> iis(&is_interactive_session, 1);
const wchar_t *child_self_destructor = L"while true ; sh -c 'sleep .25; kill -s INT $$' ; end";
parser_t::principal_parser().eval(child_self_destructor, io_chain_t(), TOP);
iis.restore();
// Restore signal handling. // Restore signal handling.
proc_pop_interactive(); proc_pop_interactive();
signal_reset_handlers(); signal_reset_handlers();

View file

@ -299,9 +299,8 @@ void job_mark_process_as_failed(job_t *job, const process_t *failed_proc) {
/// \param pid the pid of the process whose status changes /// \param pid the pid of the process whose status changes
/// \param status the status as returned by wait /// \param status the status as returned by wait
static void handle_child_status(pid_t pid, int status) { static void handle_child_status(pid_t pid, int status) {
bool found_proc = false;
job_t *j = NULL; job_t *j = NULL;
process_t *p = NULL; const process_t *found_proc = NULL;
job_iterator_t jobs; job_iterator_t jobs;
while (!found_proc && (j = jobs.next())) { while (!found_proc && (j = jobs.next())) {
@ -312,7 +311,7 @@ static void handle_child_status(pid_t pid, int status) {
if (p->completed && prev && !prev->completed && prev->pid) { if (p->completed && prev && !prev->completed && prev->pid) {
kill(prev->pid, SIGPIPE); kill(prev->pid, SIGPIPE);
} }
found_proc = true; found_proc = p.get();
break; break;
} }
prev = p.get(); prev = p.get();
@ -327,7 +326,7 @@ static void handle_child_status(pid_t pid, int status) {
if (is_interactive_session) { if (is_interactive_session) {
// In an interactive session, tell the principal parser to skip all blocks we're executing // In an interactive session, tell the principal parser to skip all blocks we're executing
// so control-C returns control to the user. // so control-C returns control to the user.
if (p && found_proc) parser_t::skip_all_blocks(); if (found_proc) parser_t::skip_all_blocks();
} else { } else {
// Deliver the SIGINT or SIGQUIT signal to ourself since we're not interactive. // Deliver the SIGINT or SIGQUIT signal to ourself since we're not interactive.
struct sigaction act; struct sigaction act;