Move signal handling code into the C module
This commit is contained in:
parent
1308bbac2d
commit
94c4c00859
@ -2,10 +2,6 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# 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 threading import Thread
|
||||
from time import monotonic
|
||||
@ -31,7 +27,7 @@ from .keys import (
|
||||
from .session import create_session
|
||||
from .shaders import Sprites
|
||||
from .tabs import SpecialWindow, TabManager
|
||||
from .utils import handle_unix_signals, safe_print
|
||||
from .utils import safe_print
|
||||
|
||||
if isosx:
|
||||
from .fast_data_types import cocoa_update_title
|
||||
@ -100,10 +96,9 @@ class Boss(Thread):
|
||||
self.glfw_window_title = None
|
||||
self.resize_gl_viewport = False
|
||||
self.shutting_down = False
|
||||
self.signal_fd = handle_unix_signals()
|
||||
self.ui_timers = Timers()
|
||||
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,
|
||||
DumpCommands(args) if args.dump_commands or args.dump_bytes else None)
|
||||
set_boss(self)
|
||||
@ -129,17 +124,10 @@ class Boss(Thread):
|
||||
self.show_mouse_cursor()
|
||||
self.start_cursor_blink()
|
||||
|
||||
def signal_received(self):
|
||||
try:
|
||||
data = os.read(self.signal_fd, io.DEFAULT_BUFFER_SIZE)
|
||||
except BlockingIOError:
|
||||
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()
|
||||
def close_signal_received(self):
|
||||
if not self.shutting_down:
|
||||
self.glfw_window.set_should_close(True)
|
||||
glfw_post_empty_event()
|
||||
|
||||
@property
|
||||
def current_tab_bar_height(self):
|
||||
@ -432,9 +420,6 @@ class Boss(Thread):
|
||||
glfw_post_empty_event()
|
||||
|
||||
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.child_monitor.shutdown()
|
||||
wakeup()
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#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 bool created = false, signal_received = false;
|
||||
static uint8_t drain_buf[1024];
|
||||
static int signal_fds[2], wakeup_fds[2];
|
||||
|
||||
|
||||
// Main thread functions {{{
|
||||
@ -52,6 +54,18 @@ static uint8_t drain_buf[1024];
|
||||
#define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF)
|
||||
#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
|
||||
self_pipe(int fds[2]) {
|
||||
int flags;
|
||||
@ -70,17 +84,22 @@ static PyObject *
|
||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
ChildMonitor *self;
|
||||
PyObject *dump_callback, *death_notify, *update_screen, *timers;
|
||||
int signal_fd, ret, wakeup_fds[2];
|
||||
int ret;
|
||||
double repaint_delay;
|
||||
|
||||
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;
|
||||
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
|
||||
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
||||
return NULL;
|
||||
}
|
||||
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);
|
||||
if (self == NULL) return PyErr_NoMemory();
|
||||
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;
|
||||
} else parse_func = parse_worker;
|
||||
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;
|
||||
self->write_wakeup_fd = wakeup_fds[1];
|
||||
self->repaint_delay = repaint_delay;
|
||||
|
||||
return (PyObject*) self;
|
||||
@ -115,9 +133,10 @@ dealloc(ChildMonitor* self) {
|
||||
add_queue_count--;
|
||||
FREE_CHILD(add_queue[add_queue_count]);
|
||||
}
|
||||
close(self->write_wakeup_fd);
|
||||
close(fds[0].fd);
|
||||
|
||||
close(wakeup_fds[0]);
|
||||
close(wakeup_fds[1]);
|
||||
close(signal_fds[0]);
|
||||
close(signal_fds[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -133,9 +152,9 @@ wakeup_(int fd) {
|
||||
}
|
||||
|
||||
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."
|
||||
wakeup_(self->write_wakeup_fd);
|
||||
wakeup_(wakeup_fds[1]);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -188,6 +207,8 @@ needs_write(ChildMonitor *self, PyObject *args) {
|
||||
static PyObject *
|
||||
shutdown(ChildMonitor *self) {
|
||||
#define shutdown_doc "shutdown() -> Shutdown the monitor loop."
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
self->shutting_down = true;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
@ -197,7 +218,7 @@ do_parse(ChildMonitor *self, Screen *screen, unsigned long child_id) {
|
||||
screen_mutex(lock, read);
|
||||
if (screen->read_buf_sz) {
|
||||
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;
|
||||
PyObject *t = PyObject_CallFunction(self->update_screen, "k", child_id);
|
||||
if (t == NULL) PyErr_Print();
|
||||
@ -341,12 +362,12 @@ read_bytes(int fd, Screen *screen) {
|
||||
|
||||
|
||||
static inline void
|
||||
drain_wakeup(int fd) {
|
||||
drain_fd(int fd) {
|
||||
while(true) {
|
||||
ssize_t len = read(fd, drain_buf, sizeof(drain_buf));
|
||||
if (len < 0) {
|
||||
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;
|
||||
@ -399,9 +420,10 @@ loop(ChildMonitor *self) {
|
||||
}
|
||||
ret = poll(fds, self->count + EXTRA_FDS, -1);
|
||||
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) {
|
||||
data_received = true;
|
||||
drain_fd(fds[1].fd);
|
||||
children_mutex(lock);
|
||||
signal_received = true;
|
||||
children_mutex(unlock);
|
||||
|
||||
@ -281,7 +281,6 @@ typedef struct {
|
||||
|
||||
PyObject *dump_callback, *update_screen, *death_notify;
|
||||
Timers *timers;
|
||||
int write_wakeup_fd;
|
||||
double repaint_delay;
|
||||
unsigned int count;
|
||||
bool shutting_down;
|
||||
|
||||
@ -169,7 +169,7 @@ def clear_buffers(window, opts):
|
||||
def dispatch_pending_calls(boss):
|
||||
boss.ui_timers.call()
|
||||
if boss.child_monitor.parse_input():
|
||||
boss.signal_received()
|
||||
boss.close_signal_received()
|
||||
|
||||
|
||||
def run_app(opts, args):
|
||||
|
||||
@ -6,7 +6,6 @@ import math
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import signal
|
||||
import string
|
||||
import subprocess
|
||||
from contextlib import contextmanager
|
||||
@ -153,29 +152,6 @@ def parse_color_set(raw):
|
||||
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():
|
||||
if isosx:
|
||||
return '' # There is no primary selection on OS X
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user