diff options
-rw-r--r-- | Shell/Job.h | 3 | ||||
-rw-r--r-- | Shell/Shell.cpp | 100 | ||||
-rw-r--r-- | Shell/Shell.h | 2 | ||||
-rw-r--r-- | Shell/main.cpp | 1 |
4 files changed, 68 insertions, 38 deletions
diff --git a/Shell/Job.h b/Shell/Job.h index 9f89bf54b8..b3ad9f6765 100644 --- a/Shell/Job.h +++ b/Shell/Job.h @@ -52,7 +52,8 @@ public: #ifdef JOB_TIME_INFO if (m_active) { auto elapsed = m_command_timer.elapsed(); - dbg() << "Command \"" << m_cmd << "\" finished in " << elapsed << " ms"; + // Don't mistake this for the command! + dbg() << "Job entry \"" << m_cmd << "\" deleted in " << elapsed << " ms"; } #endif } diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index 4a837bc6e8..ba8b2ba8b9 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -60,43 +60,10 @@ extern char** environ; void Shell::setup_signals() { Core::EventLoop::register_signal(SIGCHLD, [this](int) { - Vector<u64> disowned_jobs; - for (auto& it : jobs) { - auto job_id = it.key; - auto& job = *it.value; - int wstatus = 0; - auto child_pid = waitpid(job.pid(), &wstatus, WNOHANG | WUNTRACED); - if (child_pid < 0) { - if (errno == ECHILD) { - // The child process went away before we could process its death, just assume it exited all ok. - // FIXME: This should never happen, the child should stay around until we do the waitpid above. - dbg() << "Child process gone, cannot get exit code for " << job_id; - child_pid = job.pid(); - } else { - ASSERT_NOT_REACHED(); - } - } -#ifndef __serenity__ - if (child_pid == 0) { - // Linux: if child didn't "change state", but existed. - continue; - } +#ifdef SH_DEBUG + dbg() << "SIGCHLD!"; #endif - if (child_pid == job.pid()) { - if (WIFSIGNALED(wstatus) && !WIFSTOPPED(wstatus)) { - job.set_signalled(WTERMSIG(wstatus)); - } else if (WIFEXITED(wstatus)) { - job.set_has_exit(WEXITSTATUS(wstatus)); - } else if (WIFSTOPPED(wstatus)) { - job.unblock(); - job.set_is_suspended(true); - } - } - if (job.should_be_disowned()) - disowned_jobs.append(job_id); - } - for (auto job_id : disowned_jobs) - jobs.remove(job_id); + notify_child_event(); }); Core::EventLoop::register_signal(SIGTSTP, [this](auto) { @@ -1413,6 +1380,67 @@ void Shell::custom_event(Core::CustomEvent& event) } } +void Shell::notify_child_event() +{ + Vector<u64> disowned_jobs; + // Workaround the fact that we can't receive *who* exactly changed state. + // The child might still be alive (and even running) when this signal is dispatched to us + // so just...repeat until we find a suitable child. + // This, of course, will mean that someone can send us a SIGCHILD and we'd be spinning here + // until the next child event we can actually handle. + bool found_child = false; + do { + // Ignore stray SIGCHLD when there are no jobs. + if (jobs.is_empty()) + return; + + for (auto& it : jobs) { + auto job_id = it.key; + auto& job = *it.value; + int wstatus = 0; +#ifdef SH_DEBUG + dbgf("waitpid({}) = ...", job.pid()); +#endif + auto child_pid = waitpid(job.pid(), &wstatus, WNOHANG | WUNTRACED); +#ifdef SH_DEBUG + dbgf("... = {} - {}", child_pid, wstatus); +#endif + + if (child_pid < 0) { + if (errno == ECHILD) { + // The child process went away before we could process its death, just assume it exited all ok. + // FIXME: This should never happen, the child should stay around until we do the waitpid above. + dbg() << "Child process gone, cannot get exit code for " << job_id; + child_pid = job.pid(); + } else { + ASSERT_NOT_REACHED(); + } + } + if (child_pid == 0) { + // If the child existed, but wasn't dead. + continue; + } + if (child_pid == job.pid()) { + if (WIFSIGNALED(wstatus) && !WIFSTOPPED(wstatus)) { + job.set_signalled(WTERMSIG(wstatus)); + } else if (WIFEXITED(wstatus)) { + job.set_has_exit(WEXITSTATUS(wstatus)); + } else if (WIFSTOPPED(wstatus)) { + job.unblock(); + job.set_is_suspended(true); + } + found_child = true; + } + if (job.should_be_disowned()) + disowned_jobs.append(job_id); + } + + for (auto job_id : disowned_jobs) { + jobs.remove(job_id); + } + } while (!found_child); +} + Shell::Shell(Line::Editor& editor) : m_editor(editor) { diff --git a/Shell/Shell.h b/Shell/Shell.h index 8cf661d84a..8b11732db6 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -158,6 +158,8 @@ public: bool read_single_line(); + void notify_child_event(); + struct termios termios; struct termios default_termios; bool was_interrupted { false }; diff --git a/Shell/main.cpp b/Shell/main.cpp index 46fc8200c8..d7ad157fcf 100644 --- a/Shell/main.cpp +++ b/Shell/main.cpp @@ -34,7 +34,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/wait.h> RefPtr<Line::Editor> editor; Shell* s_shell; |