Move signal handling code into the C module

This commit is contained in:
Kovid Goyal
2017-09-06 18:14:08 +05:30
parent 1308bbac2d
commit 94c4c00859
5 changed files with 42 additions and 60 deletions

View File

@@ -2,10 +2,6 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import io
import os
import signal
import struct
from gettext import gettext as _ from gettext import gettext as _
from threading import Thread from threading import Thread
from time import monotonic from time import monotonic
@@ -31,7 +27,7 @@ from .keys import (
from .session import create_session from .session import create_session
from .shaders import Sprites from .shaders import Sprites
from .tabs import SpecialWindow, TabManager from .tabs import SpecialWindow, TabManager
from .utils import handle_unix_signals, safe_print from .utils import safe_print
if isosx: if isosx:
from .fast_data_types import cocoa_update_title from .fast_data_types import cocoa_update_title
@@ -100,10 +96,9 @@ class Boss(Thread):
self.glfw_window_title = None self.glfw_window_title = None
self.resize_gl_viewport = False self.resize_gl_viewport = False
self.shutting_down = False self.shutting_down = False
self.signal_fd = handle_unix_signals()
self.ui_timers = Timers() self.ui_timers = Timers()
self.child_monitor = ChildMonitor( self.child_monitor = ChildMonitor(
self.signal_fd, opts.repaint_delay / 1000.0, opts.repaint_delay / 1000.0,
self.on_child_death, self.update_screen, self.ui_timers, self.on_child_death, self.update_screen, self.ui_timers,
DumpCommands(args) if args.dump_commands or args.dump_bytes else None) DumpCommands(args) if args.dump_commands or args.dump_bytes else None)
set_boss(self) set_boss(self)
@@ -129,17 +124,10 @@ class Boss(Thread):
self.show_mouse_cursor() self.show_mouse_cursor()
self.start_cursor_blink() self.start_cursor_blink()
def signal_received(self): def close_signal_received(self):
try: if not self.shutting_down:
data = os.read(self.signal_fd, io.DEFAULT_BUFFER_SIZE) self.glfw_window.set_should_close(True)
except BlockingIOError: glfw_post_empty_event()
return
if data:
signals = struct.unpack('%uB' % len(data), data)
if signal.SIGINT in signals or signal.SIGTERM in signals:
if not self.shutting_down:
self.glfw_window.set_should_close(True)
glfw_post_empty_event()
@property @property
def current_tab_bar_height(self): def current_tab_bar_height(self):
@@ -432,9 +420,6 @@ class Boss(Thread):
glfw_post_empty_event() glfw_post_empty_event()
def destroy(self): def destroy(self):
# Must be called in the main thread as it manipulates signal handlers
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGTERM, signal.SIG_DFL)
self.shutting_down = True self.shutting_down = True
self.child_monitor.shutdown() self.child_monitor.shutdown()
wakeup() wakeup()

View File

@@ -9,6 +9,7 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#define EXTRA_FDS 2 #define EXTRA_FDS 2
@@ -41,6 +42,7 @@ static struct pollfd fds[MAX_CHILDREN + EXTRA_FDS] = {{0}};
static pthread_mutex_t children_lock = {{0}}; static pthread_mutex_t children_lock = {{0}};
static bool created = false, signal_received = false; static bool created = false, signal_received = false;
static uint8_t drain_buf[1024]; static uint8_t drain_buf[1024];
static int signal_fds[2], wakeup_fds[2];
// Main thread functions {{{ // Main thread functions {{{
@@ -52,6 +54,18 @@ static uint8_t drain_buf[1024];
#define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF) #define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF)
#define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF) #define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF)
static void
handle_signal(int sig_num) {
int save_err = errno;
unsigned char byte = (unsigned char)sig_num;
while(true) {
ssize_t ret = write(signal_fds[1], &byte, 1);
if (ret < 0 && errno == EINTR) continue;
break;
}
errno = save_err;
}
static inline bool static inline bool
self_pipe(int fds[2]) { self_pipe(int fds[2]) {
int flags; int flags;
@@ -70,17 +84,22 @@ static PyObject *
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
ChildMonitor *self; ChildMonitor *self;
PyObject *dump_callback, *death_notify, *update_screen, *timers; PyObject *dump_callback, *death_notify, *update_screen, *timers;
int signal_fd, ret, wakeup_fds[2]; int ret;
double repaint_delay; double repaint_delay;
if (created) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; } if (created) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
if (!PyArg_ParseTuple(args, "idOOOO", &signal_fd, &repaint_delay, &death_notify, &update_screen, &timers, &dump_callback)) return NULL; if (!PyArg_ParseTuple(args, "dOOOO", &repaint_delay, &death_notify, &update_screen, &timers, &dump_callback)) return NULL;
created = true; created = true;
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) { if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret)); PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
return NULL; return NULL;
} }
if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError); if (!self_pipe(wakeup_fds)) return PyErr_SetFromErrno(PyExc_OSError);
if (!self_pipe(signal_fds)) return PyErr_SetFromErrno(PyExc_OSError);
if (signal(SIGINT, handle_signal) == SIG_ERR) return PyErr_SetFromErrno(PyExc_OSError);
if (signal(SIGTERM, handle_signal) == SIG_ERR) return PyErr_SetFromErrno(PyExc_OSError);
if (siginterrupt(SIGINT, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
if (siginterrupt(SIGTERM, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);
self = (ChildMonitor *)type->tp_alloc(type, 0); self = (ChildMonitor *)type->tp_alloc(type, 0);
if (self == NULL) return PyErr_NoMemory(); if (self == NULL) return PyErr_NoMemory();
self->death_notify = death_notify; Py_INCREF(death_notify); self->death_notify = death_notify; Py_INCREF(death_notify);
@@ -91,9 +110,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
parse_func = parse_worker_dump; parse_func = parse_worker_dump;
} else parse_func = parse_worker; } else parse_func = parse_worker;
self->count = 0; self->count = 0;
fds[0].fd = wakeup_fds[0]; fds[1].fd = signal_fd; fds[0].fd = wakeup_fds[0]; fds[1].fd = signal_fds[0];
fds[0].events = POLLIN; fds[1].events = POLLIN; fds[0].events = POLLIN; fds[1].events = POLLIN;
self->write_wakeup_fd = wakeup_fds[1];
self->repaint_delay = repaint_delay; self->repaint_delay = repaint_delay;
return (PyObject*) self; return (PyObject*) self;
@@ -115,9 +133,10 @@ dealloc(ChildMonitor* self) {
add_queue_count--; add_queue_count--;
FREE_CHILD(add_queue[add_queue_count]); FREE_CHILD(add_queue[add_queue_count]);
} }
close(self->write_wakeup_fd); close(wakeup_fds[0]);
close(fds[0].fd); close(wakeup_fds[1]);
close(signal_fds[0]);
close(signal_fds[1]);
} }
static void static void
@@ -133,9 +152,9 @@ wakeup_(int fd) {
} }
static PyObject * static PyObject *
wakeup(ChildMonitor *self) { wakeup(ChildMonitor UNUSED *self) {
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there." #define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
wakeup_(self->write_wakeup_fd); wakeup_(wakeup_fds[1]);
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@@ -188,6 +207,8 @@ needs_write(ChildMonitor *self, PyObject *args) {
static PyObject * static PyObject *
shutdown(ChildMonitor *self) { shutdown(ChildMonitor *self) {
#define shutdown_doc "shutdown() -> Shutdown the monitor loop." #define shutdown_doc "shutdown() -> Shutdown the monitor loop."
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
self->shutting_down = true; self->shutting_down = true;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@@ -197,7 +218,7 @@ do_parse(ChildMonitor *self, Screen *screen, unsigned long child_id) {
screen_mutex(lock, read); screen_mutex(lock, read);
if (screen->read_buf_sz) { if (screen->read_buf_sz) {
parse_func(screen, self->dump_callback); parse_func(screen, self->dump_callback);
if (screen->read_buf_sz >= READ_BUF_SZ) wakeup_(self->write_wakeup_fd); // Ensure the read fd has POLLIN set if (screen->read_buf_sz >= READ_BUF_SZ) wakeup_(wakeup_fds[1]); // Ensure the read fd has POLLIN set
screen->read_buf_sz = 0; screen->read_buf_sz = 0;
PyObject *t = PyObject_CallFunction(self->update_screen, "k", child_id); PyObject *t = PyObject_CallFunction(self->update_screen, "k", child_id);
if (t == NULL) PyErr_Print(); if (t == NULL) PyErr_Print();
@@ -341,12 +362,12 @@ read_bytes(int fd, Screen *screen) {
static inline void static inline void
drain_wakeup(int fd) { drain_fd(int fd) {
while(true) { while(true) {
ssize_t len = read(fd, drain_buf, sizeof(drain_buf)); ssize_t len = read(fd, drain_buf, sizeof(drain_buf));
if (len < 0) { if (len < 0) {
if (errno == EINTR) continue; if (errno == EINTR) continue;
if (errno != EIO) perror("Call to read() from wakeup fd failed"); if (errno != EIO) perror("Call to read() from drain fd failed");
break; break;
} }
break; break;
@@ -399,9 +420,10 @@ loop(ChildMonitor *self) {
} }
ret = poll(fds, self->count + EXTRA_FDS, -1); ret = poll(fds, self->count + EXTRA_FDS, -1);
if (ret > 0) { if (ret > 0) {
if (fds[0].revents && POLLIN) drain_wakeup(fds[0].fd); if (fds[0].revents && POLLIN) drain_fd(fds[0].fd);
if (fds[1].revents && POLLIN) { if (fds[1].revents && POLLIN) {
data_received = true; data_received = true;
drain_fd(fds[1].fd);
children_mutex(lock); children_mutex(lock);
signal_received = true; signal_received = true;
children_mutex(unlock); children_mutex(unlock);

View File

@@ -281,7 +281,6 @@ typedef struct {
PyObject *dump_callback, *update_screen, *death_notify; PyObject *dump_callback, *update_screen, *death_notify;
Timers *timers; Timers *timers;
int write_wakeup_fd;
double repaint_delay; double repaint_delay;
unsigned int count; unsigned int count;
bool shutting_down; bool shutting_down;

View File

@@ -169,7 +169,7 @@ def clear_buffers(window, opts):
def dispatch_pending_calls(boss): def dispatch_pending_calls(boss):
boss.ui_timers.call() boss.ui_timers.call()
if boss.child_monitor.parse_input(): if boss.child_monitor.parse_input():
boss.signal_received() boss.close_signal_received()
def run_app(opts, args): def run_app(opts, args):

View File

@@ -6,7 +6,6 @@ import math
import os import os
import re import re
import shlex import shlex
import signal
import string import string
import subprocess import subprocess
from contextlib import contextmanager from contextlib import contextmanager
@@ -153,29 +152,6 @@ def parse_color_set(raw):
continue continue
def pipe2():
try:
read_fd, write_fd = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
except AttributeError:
import fcntl
read_fd, write_fd = os.pipe()
for fd in (read_fd, write_fd):
flag = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flag | fcntl.FD_CLOEXEC)
flag = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, flag | os.O_NONBLOCK)
return read_fd, write_fd
def handle_unix_signals():
read_fd, write_fd = pipe2()
for sig in (signal.SIGINT, signal.SIGTERM):
signal.signal(sig, lambda x, y: None)
signal.siginterrupt(sig, False)
signal.set_wakeup_fd(write_fd)
return read_fd
def get_primary_selection(): def get_primary_selection():
if isosx: if isosx:
return '' # There is no primary selection on OS X return '' # There is no primary selection on OS X