Move code to handle loop wakeup and signals into its own module
This commit is contained in:
parent
556992a117
commit
5dc4e79c8d
@ -5,6 +5,7 @@
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "loop-utils.h"
|
||||
#include "state.h"
|
||||
#include "threading.h"
|
||||
#include "screen.h"
|
||||
@ -16,7 +17,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
extern PyTypeObject Screen_Type;
|
||||
|
||||
@ -46,6 +46,7 @@ typedef struct {
|
||||
int talk_fd, listen_fd;
|
||||
Message *messages;
|
||||
size_t messages_capacity, messages_count;
|
||||
LoopData io_loop_data;
|
||||
} ChildMonitor;
|
||||
|
||||
|
||||
@ -77,7 +78,6 @@ static pthread_mutex_t children_lock;
|
||||
static bool kill_signal_received = false;
|
||||
static ChildMonitor *the_monitor = NULL;
|
||||
static uint8_t drain_buf[1024];
|
||||
static int signal_fds[2], wakeup_fds[2];
|
||||
|
||||
|
||||
typedef struct {
|
||||
@ -110,43 +110,6 @@ set_maximum_wait(double val) {
|
||||
if (val >= 0 && (val < maximum_wait || maximum_wait < 0)) maximum_wait = val;
|
||||
}
|
||||
|
||||
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], bool nonblock) {
|
||||
#ifdef __APPLE__
|
||||
int flags;
|
||||
flags = pipe(fds);
|
||||
if (flags != 0) return false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
flags = fcntl(fds[i], F_GETFD);
|
||||
if (flags == -1) { return false; }
|
||||
if (fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC) == -1) { return false; }
|
||||
if (nonblock) {
|
||||
flags = fcntl(fds[i], F_GETFL);
|
||||
if (flags == -1) { return false; }
|
||||
if (fcntl(fds[i], F_SETFL, flags | O_NONBLOCK) == -1) { return false; }
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
int flags = O_CLOEXEC;
|
||||
if (nonblock) flags |= O_NONBLOCK;
|
||||
return pipe2(fds, flags) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
ChildMonitor *self;
|
||||
@ -160,13 +123,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret));
|
||||
return NULL;
|
||||
}
|
||||
if (!self_pipe(wakeup_fds, true)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (!self_pipe(signal_fds, true)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
struct sigaction act = {.sa_handler=handle_signal};
|
||||
#define SA(which) { if (sigaction(which, &act, NULL) != 0) return PyErr_SetFromErrno(PyExc_OSError); if (siginterrupt(which, false) != 0) return PyErr_SetFromErrno(PyExc_OSError);}
|
||||
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
|
||||
#undef SA
|
||||
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
||||
if (!init_loop_data(&self->io_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (!install_signal_handlers(&self->io_loop_data)) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
self->talk_fd = talk_fd;
|
||||
self->listen_fd = listen_fd;
|
||||
if (self == NULL) return PyErr_NoMemory();
|
||||
@ -176,7 +135,7 @@ 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_fds[0];
|
||||
fds[0].fd = self->io_loop_data.wakeup_read_fd; fds[1].fd = self->io_loop_data.signal_read_fd;
|
||||
fds[0].events = POLLIN; fds[1].events = POLLIN;
|
||||
the_monitor = self;
|
||||
|
||||
@ -197,22 +156,12 @@ dealloc(ChildMonitor* self) {
|
||||
add_queue_count--;
|
||||
FREE_CHILD(add_queue[add_queue_count]);
|
||||
}
|
||||
close(wakeup_fds[0]);
|
||||
close(wakeup_fds[1]);
|
||||
close(signal_fds[0]);
|
||||
close(signal_fds[1]);
|
||||
free_loop_data(&self->io_loop_data);
|
||||
}
|
||||
|
||||
void
|
||||
wakeup_io_loop(bool in_signal_handler) {
|
||||
while(true) {
|
||||
ssize_t ret = write(wakeup_fds[1], "w", 1);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (!in_signal_handler) perror("Failed to write to wakeup fd with error");
|
||||
}
|
||||
break;
|
||||
}
|
||||
static void
|
||||
wakeup_io_loop(ChildMonitor *self, bool in_signal_handler) {
|
||||
wakeup_loop(&self->io_loop_data, in_signal_handler);
|
||||
}
|
||||
|
||||
static void* io_loop(void *data);
|
||||
@ -236,9 +185,9 @@ start(PyObject *s, PyObject *a UNUSED) {
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
wakeup(PYNOARG) {
|
||||
wakeup(ChildMonitor *self, PyObject *args UNUSED) {
|
||||
#define wakeup_doc "wakeup() -> wakeup the ChildMonitor I/O thread, forcing it to exit from poll() if it is waiting there."
|
||||
wakeup_io_loop(false);
|
||||
wakeup_io_loop(self, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -257,7 +206,7 @@ add_child(ChildMonitor *self, PyObject *args) {
|
||||
INCREF_CHILD(add_queue[add_queue_count]);
|
||||
add_queue_count++;
|
||||
children_mutex(unlock);
|
||||
wakeup_io_loop(false);
|
||||
wakeup_io_loop(self, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -304,7 +253,7 @@ schedule_write_to_child(unsigned long id, unsigned int num, ...) {
|
||||
screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz);
|
||||
if (screen->write_buf == NULL) { fatal("Out of memory."); }
|
||||
}
|
||||
if (screen->write_buf_used) wakeup_io_loop(false);
|
||||
if (screen->write_buf_used) wakeup_io_loop(self, false);
|
||||
screen_mutex(unlock, write);
|
||||
break;
|
||||
}
|
||||
@ -326,12 +275,9 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) {
|
||||
static PyObject *
|
||||
shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
|
||||
#define shutdown_monitor_doc "shutdown_monitor() -> Shutdown the monitor loop."
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
self->shutting_down = true;
|
||||
wakeup_talk_loop(false);
|
||||
wakeup_io_loop(false);
|
||||
wakeup_io_loop(self, false);
|
||||
int ret = pthread_join(self->io_thread, NULL);
|
||||
if (ret != 0) return PyErr_Format(PyExc_OSError, "Failed to join() I/O thread with error: %s", strerror(ret));
|
||||
if (talk_thread_started) {
|
||||
@ -350,7 +296,7 @@ do_parse(ChildMonitor *self, Screen *screen, double now) {
|
||||
if (time_since_new_input >= OPT(input_delay)) {
|
||||
bool read_buf_full = screen->read_buf_sz >= READ_BUF_SZ;
|
||||
parse_func(screen, self->dump_callback, now);
|
||||
if (read_buf_full) wakeup_io_loop(false); // Ensure the read fd has POLLIN set
|
||||
if (read_buf_full) wakeup_io_loop(self, false); // Ensure the read fd has POLLIN set
|
||||
screen->new_input_at = 0;
|
||||
if (screen->pending_mode.activated_at) {
|
||||
double time_since_pending = MAX(0, now - screen->pending_mode.activated_at);
|
||||
@ -435,7 +381,7 @@ mark_child_for_close(ChildMonitor *self, id_type window_id) {
|
||||
}
|
||||
}
|
||||
children_mutex(unlock);
|
||||
wakeup_io_loop(false);
|
||||
wakeup_io_loop(self, false);
|
||||
}
|
||||
|
||||
|
||||
@ -1106,28 +1052,21 @@ drain_fd(int fd) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
read_signals(int fd, bool *kill_signal, bool *child_died) {
|
||||
static char buf[256];
|
||||
while(true) {
|
||||
ssize_t len = read(fd, buf, sizeof(buf));
|
||||
if (len < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno != EIO) perror("Call to read() from read_signals() failed");
|
||||
typedef struct { bool kill_signal, child_died; } SignalSet;
|
||||
|
||||
static void
|
||||
handle_signal(int signum, void *data) {
|
||||
SignalSet *ss = data;
|
||||
switch(signum) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
ss->kill_signal = true;
|
||||
break;
|
||||
case SIGCHLD:
|
||||
ss->child_died = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (ssize_t i = 0; i < len; i++) {
|
||||
switch(buf[i]) {
|
||||
case SIGCHLD:
|
||||
*child_died = true; break;
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
*kill_signal = true; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1237,11 +1176,11 @@ io_loop(void *data) {
|
||||
if (ret > 0) {
|
||||
if (fds[0].revents && POLLIN) drain_fd(fds[0].fd); // wakeup
|
||||
if (fds[1].revents && POLLIN) {
|
||||
SignalSet ss = {0};
|
||||
data_received = true;
|
||||
bool kill_signal = false, child_died = false;
|
||||
read_signals(fds[1].fd, &kill_signal, &child_died);
|
||||
if (kill_signal) { children_mutex(lock); kill_signal_received = true; children_mutex(unlock); }
|
||||
if (child_died) reap_children(self, OPT(close_on_child_death));
|
||||
read_signals(fds[1].fd, handle_signal, &ss);
|
||||
if (ss.kill_signal) { children_mutex(lock); kill_signal_received = true; children_mutex(unlock); }
|
||||
if (ss.child_died) reap_children(self, OPT(close_on_child_death));
|
||||
}
|
||||
for (i = 0; i < self->count; i++) {
|
||||
if (fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) {
|
||||
@ -1321,7 +1260,7 @@ typedef struct {
|
||||
PeerReadData *reads;
|
||||
PeerWriteData *writes;
|
||||
PeerWriteData *queued_writes;
|
||||
int wakeup_fds[2];
|
||||
LoopData loop_data;
|
||||
pthread_mutex_t peer_lock;
|
||||
} TalkData;
|
||||
|
||||
@ -1461,15 +1400,7 @@ prune_finished_writes(void) {
|
||||
|
||||
static void
|
||||
wakeup_talk_loop(bool in_signal_handler) {
|
||||
if (talk_data.wakeup_fds[1] <= 0) return;
|
||||
while(true) {
|
||||
ssize_t ret = write(talk_data.wakeup_fds[1], "w", 1);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (!in_signal_handler) perror("Failed to write to talk wakeup fd with error");
|
||||
}
|
||||
break;
|
||||
}
|
||||
wakeup_loop(&talk_data.loop_data, in_signal_handler);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1498,7 +1429,7 @@ talk_loop(void *data) {
|
||||
ChildMonitor *self = (ChildMonitor*)data;
|
||||
set_thread_name("KittyPeerMon");
|
||||
if ((pthread_mutex_init(&talk_data.peer_lock, NULL)) != 0) { perror("Failed to create peer mutex"); return 0; }
|
||||
if (!self_pipe(talk_data.wakeup_fds, true)) { perror("Failed to create wakeup fds for talk thread"); return 0; }
|
||||
if (!init_loop_data(&talk_data.loop_data)) { log_error("Failed to create wakeup fd for talk thread with error: %s", strerror(errno)); }
|
||||
ensure_space_for(&talk_data, fds, PollFD, 8, fds_capacity, 8, false);
|
||||
#define add_listener(which) \
|
||||
if (self->which > -1) { \
|
||||
@ -1506,7 +1437,7 @@ talk_loop(void *data) {
|
||||
}
|
||||
add_listener(talk_fd); add_listener(listen_fd);
|
||||
#undef add_listener
|
||||
talk_data.fds[talk_data.num_listen_fds].fd = talk_data.wakeup_fds[0]; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN;
|
||||
talk_data.fds[talk_data.num_listen_fds].fd = talk_data.loop_data.wakeup_read_fd; talk_data.fds[talk_data.num_listen_fds++].events = POLLIN;
|
||||
|
||||
while (LIKELY(!self->shutting_down)) {
|
||||
for (size_t i = 0; i < talk_data.num_listen_fds + talk_data.num_talk_fds; i++) { talk_data.fds[i].revents = 0; }
|
||||
@ -1529,7 +1460,7 @@ talk_loop(void *data) {
|
||||
} else if (ret < 0) { if (errno != EAGAIN && errno != EINTR) perror("poll() on talk fds failed"); }
|
||||
}
|
||||
end:
|
||||
close(talk_data.wakeup_fds[0]); close(talk_data.wakeup_fds[1]);
|
||||
free_loop_data(&talk_data.loop_data);
|
||||
free(talk_data.fds); free(talk_data.reads); free(talk_data.writes); free(talk_data.queued_writes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -301,7 +301,6 @@ void set_mouse_cursor(MouseShape);
|
||||
void enter_event(void);
|
||||
void mouse_event(int, int, int);
|
||||
void focus_in_event(void);
|
||||
void wakeup_io_loop(bool);
|
||||
void scroll_event(double, double, int);
|
||||
void fake_scroll(int, bool);
|
||||
void set_special_key_combo(int glfw_key, int mods, bool is_native);
|
||||
|
||||
94
kitty/loop-utils.c
Normal file
94
kitty/loop-utils.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* loop-utils.c
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "loop-utils.h"
|
||||
#include <signal.h>
|
||||
|
||||
bool
|
||||
init_loop_data(LoopData *ld) {
|
||||
if (!self_pipe(ld->wakeup_fds, true)) return false;
|
||||
ld->wakeup_read_fd = ld->wakeup_fds[0];
|
||||
ld->signal_fds[0] = -1; ld->signal_fds[1] = -1; ld->signal_read_fd = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int signal_write_fd = -1;
|
||||
|
||||
static void
|
||||
handle_signal(int sig_num) {
|
||||
int save_err = errno;
|
||||
unsigned char byte = (unsigned char)sig_num;
|
||||
while(signal_write_fd != -1) {
|
||||
ssize_t ret = write(signal_write_fd, &byte, 1);
|
||||
if (ret < 0 && errno == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
errno = save_err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
free_loop_data(LoopData *ld) {
|
||||
#define CLOSE(which, idx) if (ld->which[idx] > -1) close(ld->which[idx]); ld->which[idx] = -1;
|
||||
CLOSE(wakeup_fds, 0); CLOSE(wakeup_fds, 1);
|
||||
CLOSE(signal_fds, 0); CLOSE(signal_fds, 1);
|
||||
#undef CLOSE
|
||||
if (ld->signal_read_fd) {
|
||||
signal_write_fd = -1;
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
}
|
||||
ld->signal_read_fd = -1; ld->wakeup_read_fd = -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
wakeup_loop(LoopData *ld, bool in_signal_handler) {
|
||||
while(true) {
|
||||
ssize_t ret = write(ld->wakeup_fds[1], "w", 1);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (!in_signal_handler) log_error("Failed to write to loop wakeup fd with error: %s", strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
install_signal_handlers(LoopData *ld) {
|
||||
if (!self_pipe(ld->signal_fds, true)) return false;
|
||||
signal_write_fd = ld->signal_fds[1];
|
||||
struct sigaction act = {.sa_handler=handle_signal};
|
||||
#define SA(which) { if (sigaction(which, &act, NULL) != 0) return false; if (siginterrupt(which, false) != 0) return false; }
|
||||
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD);
|
||||
#undef SA
|
||||
ld->signal_read_fd = ld->signal_fds[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
read_signals_from_pipe_fd(int fd, handle_signal_func callback, void *data) {
|
||||
static char buf[256];
|
||||
while(true) {
|
||||
ssize_t len = read(fd, buf, sizeof(buf));
|
||||
if (len < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno != EIO && errno != EAGAIN) log_error("Call to read() from read_signals_from_pipe_fd() failed with error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
for (ssize_t i = 0; i < len; i++) callback(buf[i], data);
|
||||
if (len == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
read_signals(int fd, handle_signal_func callback, void *data) {
|
||||
read_signals_from_pipe_fd(fd, callback, data);
|
||||
}
|
||||
49
kitty/loop-utils.h
Normal file
49
kitty/loop-utils.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data-types.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct {
|
||||
int wakeup_fds[2];
|
||||
int signal_fds[2];
|
||||
int wakeup_read_fd;
|
||||
int signal_read_fd;
|
||||
} LoopData;
|
||||
typedef void(*handle_signal_func)(int, void *data);
|
||||
|
||||
bool init_loop_data(LoopData *ld);
|
||||
void free_loop_data(LoopData *ld);
|
||||
void wakeup_loop(LoopData *ld, bool in_signal_handler);
|
||||
bool install_signal_handlers(LoopData *ld);
|
||||
void read_signals(int fd, handle_signal_func callback, void *data);
|
||||
|
||||
static inline bool
|
||||
self_pipe(int fds[2], bool nonblock) {
|
||||
#ifdef __APPLE__
|
||||
int flags;
|
||||
flags = pipe(fds);
|
||||
if (flags != 0) return false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
flags = fcntl(fds[i], F_GETFD);
|
||||
if (flags == -1) { return false; }
|
||||
if (fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC) == -1) { return false; }
|
||||
if (nonblock) {
|
||||
flags = fcntl(fds[i], F_GETFL);
|
||||
if (flags == -1) { return false; }
|
||||
if (fcntl(fds[i], F_SETFL, flags | O_NONBLOCK) == -1) { return false; }
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
int flags = O_CLOEXEC;
|
||||
if (nonblock) flags |= O_NONBLOCK;
|
||||
return pipe2(fds, flags) == 0;
|
||||
#endif
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user