mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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.
This commit is contained in:
parent
0c6fdd5251
commit
a6fb511c7f
@ -1,6 +1,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
#include "nvim/os/uv_helpers.h"
|
#include "nvim/os/uv_helpers.h"
|
||||||
@ -43,6 +45,7 @@
|
|||||||
Job *table[MAX_RUNNING_JOBS] = {NULL};
|
Job *table[MAX_RUNNING_JOBS] = {NULL};
|
||||||
size_t stop_requests = 0;
|
size_t stop_requests = 0;
|
||||||
uv_timer_t job_stop_timer;
|
uv_timer_t job_stop_timer;
|
||||||
|
uv_signal_t schld;
|
||||||
|
|
||||||
// Some helpers shared in this module
|
// Some helpers shared in this module
|
||||||
|
|
||||||
@ -56,6 +59,8 @@ void job_init(void)
|
|||||||
{
|
{
|
||||||
uv_disable_stdio_inheritance();
|
uv_disable_stdio_inheritance();
|
||||||
uv_timer_init(uv_default_loop(), &job_stop_timer);
|
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
|
/// Releases job control resources and terminates running jobs
|
||||||
@ -73,6 +78,8 @@ void job_teardown(void)
|
|||||||
|
|
||||||
// Wait until all jobs are closed
|
// Wait until all jobs are closed
|
||||||
event_poll_until(-1, !stop_requests);
|
event_poll_until(-1, !stop_requests);
|
||||||
|
uv_signal_stop(&schld);
|
||||||
|
uv_close((uv_handle_t *)&schld, NULL);
|
||||||
// Close the timer
|
// Close the timer
|
||||||
uv_close((uv_handle_t *)&job_stop_timer, NULL);
|
uv_close((uv_handle_t *)&job_stop_timer, NULL);
|
||||||
}
|
}
|
||||||
@ -200,10 +207,16 @@ void job_stop(Job *job)
|
|||||||
}
|
}
|
||||||
|
|
||||||
job->stopped_time = os_hrtime();
|
job->stopped_time = os_hrtime();
|
||||||
// Close the job's stdin. If the job doesn't close it's own stdout/stderr,
|
if (job->opts.pty) {
|
||||||
// they will be closed when the job exits(possibly due to being terminated
|
// close all streams for pty jobs to send SIGHUP to the process
|
||||||
// after a timeout)
|
job_close_streams(job);
|
||||||
close_job_in(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++) {
|
if (!stop_requests++) {
|
||||||
// When there's at least one stop request pending, start a timer that
|
// 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;
|
return job->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the job pid
|
||||||
|
int job_pid(Job *job)
|
||||||
|
{
|
||||||
|
return job->pid;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get data associated with a job
|
/// Get data associated with a job
|
||||||
///
|
///
|
||||||
/// @param job A pointer to the 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;
|
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
|
/// 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).
|
/// that didn't die from SIGTERM after a while(exit_timeout is 0).
|
||||||
static void job_stop_timer_cb(uv_timer_t *handle)
|
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)
|
|
||||||
{
|
|
||||||
close_job_in(job);
|
|
||||||
close_job_out(job);
|
|
||||||
close_job_err(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_cb(uv_handle_t *handle)
|
static void close_cb(uv_handle_t *handle)
|
||||||
{
|
{
|
||||||
job_decref(handle_get_job(handle));
|
job_decref(handle_get_job(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void chld_handler(uv_signal_t *handle, int signum)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
struct winsize winsize;
|
struct winsize winsize;
|
||||||
uv_pipe_t proc_stdin, proc_stdout, proc_stderr;
|
uv_pipe_t proc_stdin, proc_stdout, proc_stderr;
|
||||||
uv_signal_t schld;
|
|
||||||
int tty_fd;
|
int tty_fd;
|
||||||
} PtyProcess;
|
} PtyProcess;
|
||||||
|
|
||||||
@ -136,9 +135,6 @@ bool pty_process_spawn(Job *job) FUNC_ATTR_NONNULL_ALL
|
|||||||
goto error;
|
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;
|
ptyproc->tty_fd = master;
|
||||||
job->pid = pid;
|
job->pid = pid;
|
||||||
return true;
|
return true;
|
||||||
@ -162,9 +158,6 @@ error:
|
|||||||
|
|
||||||
void pty_process_close(Job *job) FUNC_ATTR_NONNULL_ALL
|
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);
|
pty_process_close_master(job);
|
||||||
job_close_streams(job);
|
job_close_streams(job);
|
||||||
job_decref(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));
|
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
|
static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
memset(termios, 0, sizeof(struct termios));
|
memset(termios, 0, sizeof(struct termios));
|
||||||
|
Loading…
Reference in New Issue
Block a user