aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2015-02-26 23:29:08 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2015-03-24 10:56:42 -0300
commita6fb511c7f20d14dac692418a5c2a3e81615a63d (patch)
tree64db8d795e240271ced605bd70111380fcb7182a
parent0c6fdd52513e643a74ebde99a92389492f4150f5 (diff)
downloadrneovim-a6fb511c7f20d14dac692418a5c2a3e81615a63d.tar.gz
rneovim-a6fb511c7f20d14dac692418a5c2a3e81615a63d.tar.bz2
rneovim-a6fb511c7f20d14dac692418a5c2a3e81615a63d.zip
job: Fix process cleanup using SIGCHLD/waitpid
Add a SIGCHLD handler for cleaning up pty processes passing the WNOHANG flag. It may also be used to cleanup processes spawned with uv_spawn.
-rw-r--r--src/nvim/os/job.c73
-rw-r--r--src/nvim/os/pty_process.c32
2 files changed, 63 insertions, 42 deletions
diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c
index 9855a19269..48aaf8e9ed 100644
--- a/src/nvim/os/job.c
+++ b/src/nvim/os/job.c
@@ -1,6 +1,8 @@
#include <stdint.h>
#include <stdbool.h>
+#include <sys/wait.h>
+
#include <uv.h>
#include "nvim/os/uv_helpers.h"
@@ -43,6 +45,7 @@
Job *table[MAX_RUNNING_JOBS] = {NULL};
size_t stop_requests = 0;
uv_timer_t job_stop_timer;
+uv_signal_t schld;
// Some helpers shared in this module
@@ -56,6 +59,8 @@ void job_init(void)
{
uv_disable_stdio_inheritance();
uv_timer_init(uv_default_loop(), &job_stop_timer);
+ uv_signal_init(uv_default_loop(), &schld);
+ uv_signal_start(&schld, chld_handler, SIGCHLD);
}
/// Releases job control resources and terminates running jobs
@@ -73,6 +78,8 @@ void job_teardown(void)
// Wait until all jobs are closed
event_poll_until(-1, !stop_requests);
+ uv_signal_stop(&schld);
+ uv_close((uv_handle_t *)&schld, NULL);
// Close the timer
uv_close((uv_handle_t *)&job_stop_timer, NULL);
}
@@ -200,10 +207,16 @@ void job_stop(Job *job)
}
job->stopped_time = os_hrtime();
- // Close the job's stdin. If the job doesn't close it's own stdout/stderr,
- // they will be closed when the job exits(possibly due to being terminated
- // after a timeout)
- close_job_in(job);
+ if (job->opts.pty) {
+ // close all streams for pty jobs to send SIGHUP to the process
+ job_close_streams(job);
+ pty_process_close_master(job);
+ } else {
+ // Close the job's stdin. If the job doesn't close its own stdout/stderr,
+ // they will be closed when the job exits(possibly due to being terminated
+ // after a timeout)
+ close_job_in(job);
+ }
if (!stop_requests++) {
// When there's at least one stop request pending, start a timer that
@@ -312,6 +325,12 @@ int job_id(Job *job)
return job->id;
}
+// Get the job pid
+int job_pid(Job *job)
+{
+ return job->pid;
+}
+
/// Get data associated with a job
///
/// @param job A pointer to the job
@@ -331,6 +350,13 @@ bool job_resize(Job *job, uint16_t width, uint16_t height)
return true;
}
+void job_close_streams(Job *job)
+{
+ close_job_in(job);
+ close_job_out(job);
+ close_job_err(job);
+}
+
/// Iterates the table, sending SIGTERM to stopped jobs and SIGKILL to those
/// that didn't die from SIGTERM after a while(exit_timeout is 0).
static void job_stop_timer_cb(uv_timer_t *handle)
@@ -375,14 +401,41 @@ static void read_cb(RStream *rstream, void *data, bool eof)
}
}
-void job_close_streams(Job *job)
+static void close_cb(uv_handle_t *handle)
{
- close_job_in(job);
- close_job_out(job);
- close_job_err(job);
+ job_decref(handle_get_job(handle));
}
-static void close_cb(uv_handle_t *handle)
+static void chld_handler(uv_signal_t *handle, int signum)
{
- job_decref(handle_get_job(handle));
+ int stat = 0;
+ int pid;
+
+ do {
+ pid = waitpid(-1, &stat, WNOHANG);
+ } while (pid < 0 && errno == EINTR);
+
+ if (pid <= 0) {
+ return;
+ }
+
+ if (WIFSTOPPED(stat) || WIFCONTINUED(stat)) {
+ // Only care for processes that exited
+ return;
+ }
+
+ Job *job = NULL;
+ // find the job corresponding to the exited pid
+ for (int i = 0; i < MAX_RUNNING_JOBS; i++) {
+ if ((job = table[i]) != NULL && job->pid == pid) {
+ if (WIFEXITED(stat)) {
+ job->status = WEXITSTATUS(stat);
+ } else if (WIFSIGNALED(stat)) {
+ job->status = WTERMSIG(stat);
+ }
+ process_close(job);
+ break;
+ }
+ }
}
+
diff --git a/src/nvim/os/pty_process.c b/src/nvim/os/pty_process.c
index c135efc6d3..9a2721f769 100644
--- a/src/nvim/os/pty_process.c
+++ b/src/nvim/os/pty_process.c
@@ -34,7 +34,6 @@
typedef struct {
struct winsize winsize;
uv_pipe_t proc_stdin, proc_stdout, proc_stderr;
- uv_signal_t schld;
int tty_fd;
} PtyProcess;
@@ -136,9 +135,6 @@ bool pty_process_spawn(Job *job) FUNC_ATTR_NONNULL_ALL
goto error;
}
- uv_signal_init(uv_default_loop(), &ptyproc->schld);
- uv_signal_start(&ptyproc->schld, chld_handler, SIGCHLD);
- ptyproc->schld.data = job;
ptyproc->tty_fd = master;
job->pid = pid;
return true;
@@ -162,9 +158,6 @@ error:
void pty_process_close(Job *job) FUNC_ATTR_NONNULL_ALL
{
- PtyProcess *ptyproc = job->process;
- uv_signal_stop(&ptyproc->schld);
- uv_close((uv_handle_t *)&ptyproc->schld, NULL);
pty_process_close_master(job);
job_close_streams(job);
job_decref(job);
@@ -207,31 +200,6 @@ static void init_child(Job *job) FUNC_ATTR_NONNULL_ALL
fprintf(stderr, "execvp failed: %s\n", strerror(errno));
}
-static void chld_handler(uv_signal_t *handle, int signum) FUNC_ATTR_NONNULL_ALL
-{
- Job *job = handle->data;
- int stat = 0;
-
- if (waitpid(job->pid, &stat, 0) < 0) {
- fprintf(stderr, "Waiting for pid %d failed: %s\n", job->pid,
- strerror(errno));
- return;
- }
-
- if (WIFSTOPPED(stat) || WIFCONTINUED(stat)) {
- // Did not exit
- return;
- }
-
- if (WIFEXITED(stat)) {
- job->status = WEXITSTATUS(stat);
- } else if (WIFSIGNALED(stat)) {
- job->status = WTERMSIG(stat);
- }
-
- pty_process_close(job);
-}
-
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
{
memset(termios, 0, sizeof(struct termios));