mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-25 17:52:02 +02:00
Move signal handling code into the C module
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user