diff --git a/kitty/boss.py b/kitty/boss.py index cd8e155c8..07736244c 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -49,6 +49,7 @@ from .notify import notification_activated from .options.types import Options from .options.utils import MINIMUM_FONT_SIZE, KeyMap, SubSequenceMap from .os_window_size import initial_window_size_func +from .prewarm import PrewarmProcess from .rgb import color_from_int from .session import Session, create_sessions, get_os_window_sizing_data from .tabs import ( @@ -253,10 +254,11 @@ class Boss: self.allow_remote_control = opts.allow_remote_control if args.listen_on and (self.allow_remote_control in ('y', 'socket-only')): listen_fd = listen_on(args.listen_on) + self.prewarm = PrewarmProcess() self.child_monitor = ChildMonitor( self.on_child_death, DumpCommands(args) if args.dump_commands or args.dump_bytes else None, - talk_fd, listen_fd + talk_fd, listen_fd, self.prewarm.take_from_worker_fd() ) set_boss(self) self.args = args @@ -1957,6 +1959,7 @@ class Boss: for w in self.all_windows: self.default_bg_changed_for(w.id) w.refresh() + self.prewarm.reload_kitty_config() @ac('misc', ''' Reload the config file diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index e38c02d85..7a9ad04c0 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -34,7 +34,7 @@ extern PyTypeObject Screen_Type; #define EVDBG(...) #endif -#define EXTRA_FDS 2 +#define EXTRA_FDS 3 #ifndef MSG_NOSIGNAL // Apple does not implement MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -57,7 +57,7 @@ typedef struct { bool shutting_down; pthread_t io_thread, talk_thread; - int talk_fd, listen_fd; + int talk_fd, listen_fd, prewarm_fd; Message *messages; size_t messages_capacity, messages_count; LoopData io_loop_data; @@ -124,11 +124,11 @@ static PyObject * new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { ChildMonitor *self; PyObject *dump_callback, *death_notify; - int talk_fd = -1, listen_fd = -1; + int talk_fd = -1, listen_fd = -1, prewarm_fd = -1; int ret; if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; } - if (!PyArg_ParseTuple(args, "OO|ii", &death_notify, &dump_callback, &talk_fd, &listen_fd)) return NULL; + if (!PyArg_ParseTuple(args, "OO|iii", &death_notify, &dump_callback, &talk_fd, &listen_fd, &prewarm_fd)) return NULL; if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) { PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret)); return NULL; @@ -141,6 +141,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { if (!init_loop_data(&self->io_loop_data, SIGINT, SIGHUP, SIGTERM, SIGCHLD, SIGUSR1, SIGUSR2, 0)) return PyErr_SetFromErrno(PyExc_OSError); self->talk_fd = talk_fd; self->listen_fd = listen_fd; + self->prewarm_fd = prewarm_fd; if (self == NULL) return PyErr_NoMemory(); self->death_notify = death_notify; Py_INCREF(death_notify); if (dump_callback != Py_None) { @@ -149,7 +150,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { } else parse_func = parse_worker; self->count = 0; children_fds[0].fd = self->io_loop_data.wakeup_read_fd; children_fds[1].fd = self->io_loop_data.signal_read_fd; - children_fds[0].events = POLLIN; children_fds[1].events = POLLIN; + children_fds[2].fd = self->prewarm_fd; + children_fds[0].events = POLLIN; children_fds[1].events = POLLIN; children_fds[2].events = POLLIN; the_monitor = self; return (PyObject*) self; @@ -175,6 +177,7 @@ dealloc(ChildMonitor* self) { FREE_CHILD(add_queue[add_queue_count]); } free_loop_data(&self->io_loop_data); + safe_close(self->prewarm_fd, __FILE__, __LINE__); self->prewarm_fd = -1; Py_TYPE(self)->tp_free((PyObject*)self); } @@ -1302,6 +1305,34 @@ mark_monitored_pids(pid_t pid, int status) { children_mutex(unlock); } +static void +reap_prewarmed_children(ChildMonitor *self, int fd, bool enable_close_on_child_death) { + static char buf[256]; + static size_t buf_pos = 0; + while(true) { + ssize_t len = read(fd, buf + buf_pos, sizeof(buf) - buf_pos); + if (len < 0) { + if (errno == EINTR) continue; + if (errno != EIO && errno != EAGAIN) log_error("Call to read() from reap_prewarmed_children() failed with error: %s", strerror(errno)); + break; + } + buf_pos += len; + char *nl; + while (buf_pos > 1 && (nl = memchr(buf, '\n', buf_pos)) != NULL) { + size_t sz = nl - buf; + if (enable_close_on_child_death) { + *nl = 0; + int pid = atoi(buf); + if (pid) mark_child_for_removal(self, pid); + } + memmove(buf, buf + sz, sz); + buf_pos -= sz; + } + if (len == 0) break; + } + +} + static void reap_children(ChildMonitor *self, bool enable_close_on_child_death) { int status; @@ -1392,6 +1423,9 @@ io_loop(void *data) { } if (ss.child_died) reap_children(self, OPT(close_on_child_death)); } + if (children_fds[2].revents && POLLIN) { + reap_prewarmed_children(self, children_fds[2].fd, OPT(close_on_child_death)); + } for (i = 0; i < self->count; i++) { if (children_fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) { data_received = true; diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 93c229951..c45a153ee 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1193,7 +1193,8 @@ class ChildMonitor: death_notify: Callable[[int], None], dump_callback: Optional[Callable[[bytes], None]], talk_fd: int = -1, - listen_fd: int = -1 + listen_fd: int = -1, + prewarm_fd: int = -1, ): pass