Move child process management into child-monitor.c
This commit is contained in:
parent
52ab534e22
commit
af02b735e7
@ -135,7 +135,7 @@ class Boss:
|
|||||||
yield from t
|
yield from t
|
||||||
|
|
||||||
def add_child(self, window):
|
def add_child(self, window):
|
||||||
self.child_monitor.add_child(window.id, window.child.child_fd, window.screen)
|
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
|
||||||
self.window_id_map[window.id] = window
|
self.window_id_map[window.id] = window
|
||||||
wakeup()
|
wakeup()
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stropts.h>
|
#include <stropts.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ typedef struct {
|
|||||||
int fd;
|
int fd;
|
||||||
unsigned long id;
|
unsigned long id;
|
||||||
double last_paint_at;
|
double last_paint_at;
|
||||||
|
pid_t pid;
|
||||||
} Child;
|
} Child;
|
||||||
|
|
||||||
static const Child EMPTY_CHILD = {0};
|
static const Child EMPTY_CHILD = {0};
|
||||||
@ -202,7 +204,7 @@ add_child(ChildMonitor *self, PyObject *args) {
|
|||||||
if (self->count + add_queue_count >= MAX_CHILDREN) { PyErr_SetString(PyExc_ValueError, "Too many children"); children_mutex(unlock); return NULL; }
|
if (self->count + add_queue_count >= MAX_CHILDREN) { PyErr_SetString(PyExc_ValueError, "Too many children"); children_mutex(unlock); return NULL; }
|
||||||
add_queue[add_queue_count] = EMPTY_CHILD;
|
add_queue[add_queue_count] = EMPTY_CHILD;
|
||||||
#define A(attr) &add_queue[add_queue_count].attr
|
#define A(attr) &add_queue[add_queue_count].attr
|
||||||
if (!PyArg_ParseTuple(args, "kiO", A(id), A(fd), A(screen))) {
|
if (!PyArg_ParseTuple(args, "kiiO", A(id), A(pid), A(fd), A(screen))) {
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -446,6 +448,70 @@ add_children(ChildMonitor *self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
hangup(pid_t pid) {
|
||||||
|
errno = 0;
|
||||||
|
pid_t pgid = getpgid(pid);
|
||||||
|
if (errno == ESRCH) return;
|
||||||
|
if (errno != 0) { perror("Failed to get process group id for child"); return; }
|
||||||
|
if (killpg(pgid, SIGHUP) != 0) {
|
||||||
|
if (errno != ESRCH) perror("Failed to kill child");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static pid_t pid_buf[MAX_CHILDREN] = {0};
|
||||||
|
static size_t pid_buf_pos = 0;
|
||||||
|
static pthread_t reap_thread;
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
set_thread_name(pthread_t UNUSED thread, const char *name) {
|
||||||
|
int ret = 0;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
ret = pthread_setname_np(name);
|
||||||
|
#else
|
||||||
|
ret = pthread_setname_np(thread, name);
|
||||||
|
#endif
|
||||||
|
if (ret != 0) perror("Failed to set thread name");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void*
|
||||||
|
reap(void *pid_p) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
set_thread_name(reap_thread, "KittyReapChild");
|
||||||
|
#endif
|
||||||
|
pid_t pid = *((pid_t*)pid_p);
|
||||||
|
while(true) {
|
||||||
|
pid_t ret = waitpid(pid, NULL, 0);
|
||||||
|
if (ret != pid) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
fprintf(stderr, "Failed to reap child process with pid: %d with error: %s\n", pid, strerror(errno));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cleanup_child(ssize_t i) {
|
||||||
|
close(children[i].fd);
|
||||||
|
hangup(children[i].pid);
|
||||||
|
pid_buf[pid_buf_pos] = children[i].pid;
|
||||||
|
if (waitpid(pid_buf[pid_buf_pos], NULL, WNOHANG) != pid_buf[pid_buf_pos]) {
|
||||||
|
errno = 0;
|
||||||
|
int ret = pthread_create(&reap_thread, NULL, reap, pid_buf + pid_buf_pos);
|
||||||
|
if (ret != 0) perror("Failed to create thread to reap child");
|
||||||
|
else {
|
||||||
|
#ifndef __APPLE__
|
||||||
|
set_thread_name(reap_thread, "KittyReapChild");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pid_buf_pos = (pid_buf_pos + 1) % MAX_CHILDREN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
remove_children(ChildMonitor *self) {
|
remove_children(ChildMonitor *self) {
|
||||||
if (self->count > 0) {
|
if (self->count > 0) {
|
||||||
@ -453,7 +519,7 @@ remove_children(ChildMonitor *self) {
|
|||||||
for (ssize_t i = self->count - 1; i >= 0; i--) {
|
for (ssize_t i = self->count - 1; i >= 0; i--) {
|
||||||
if (children[i].needs_removal) {
|
if (children[i].needs_removal) {
|
||||||
count++;
|
count++;
|
||||||
close(fds[EXTRA_FDS + i].fd);
|
cleanup_child(i);
|
||||||
remove_queue[remove_queue_count] = children[i];
|
remove_queue[remove_queue_count] = children[i];
|
||||||
remove_queue_count++;
|
remove_queue_count++;
|
||||||
children[i] = EMPTY_CHILD;
|
children[i] = EMPTY_CHILD;
|
||||||
@ -542,13 +608,7 @@ io_loop(void *data) {
|
|||||||
bool has_more, data_received;
|
bool has_more, data_received;
|
||||||
Screen *screen;
|
Screen *screen;
|
||||||
ChildMonitor *self = (ChildMonitor*)data;
|
ChildMonitor *self = (ChildMonitor*)data;
|
||||||
|
set_thread_name(self->io_thread, "KittyChildMon");
|
||||||
#define THREAD_NAME "KittyChildMon"
|
|
||||||
#ifdef __APPLE__
|
|
||||||
pthread_setname_np(THREAD_NAME);
|
|
||||||
#else
|
|
||||||
pthread_setname_np(self->io_thread, THREAD_NAME);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (LIKELY(!self->shutting_down)) {
|
while (LIKELY(!self->shutting_down)) {
|
||||||
children_mutex(lock);
|
children_mutex(lock);
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import fcntl
|
import fcntl
|
||||||
import signal
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from .constants import terminfo_dir
|
from .constants import terminfo_dir
|
||||||
@ -74,23 +73,3 @@ class Child:
|
|||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
return pid
|
return pid
|
||||||
|
|
||||||
def hangup(self):
|
|
||||||
if self.pid is not None:
|
|
||||||
pid, self.pid = self.pid, None
|
|
||||||
try:
|
|
||||||
pgrp = os.getpgid(pid)
|
|
||||||
except ProcessLookupError:
|
|
||||||
return
|
|
||||||
os.killpg(pgrp, signal.SIGHUP)
|
|
||||||
self.child_fd = None
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.hangup()
|
|
||||||
|
|
||||||
def get_child_status(self):
|
|
||||||
if self.pid is not None:
|
|
||||||
try:
|
|
||||||
return os.waitid(os.P_PID, self.pid, os.WEXITED | os.WNOHANG)
|
|
||||||
except ChildProcessError:
|
|
||||||
self.pid = None
|
|
||||||
|
|||||||
@ -103,8 +103,6 @@ class Window:
|
|||||||
if self.destroyed:
|
if self.destroyed:
|
||||||
return
|
return
|
||||||
self.destroyed = True
|
self.destroyed = True
|
||||||
self.child.hangup()
|
|
||||||
self.child.get_child_status() # Ensure child does not become zombie
|
|
||||||
# Remove cycles so that screen is de-allocated immediately
|
# Remove cycles so that screen is de-allocated immediately
|
||||||
boss = get_boss()
|
boss = get_boss()
|
||||||
self.screen.reset_callbacks()
|
self.screen.reset_callbacks()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user