Use a pipe rather than a signal to wait for terminal ready

Simpler code, more robust since there is no longer a race between the
installation of the signal handler and the dispatch of the signal
This commit is contained in:
Kovid Goyal 2018-08-04 20:24:06 +05:30
parent 094ddd9333
commit eb2ec1833c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 31 additions and 31 deletions

View File

@ -47,35 +47,22 @@ write_to_stderr(const char *text) {
#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }
static sig_atomic_t sigwinch_arrived;
void handle_sigwinch(int signal) {
if (signal == SIGWINCH) sigwinch_arrived = 1;
}
static inline void
wait_for_sigwinch() {
sigwinch_arrived = 0;
sigset_t mask, oldmask;
struct sigaction sa;
sa.sa_handler = handle_sigwinch;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGWINCH, &sa, NULL) == -1) {
exit_on_err("Failed to set the SIGWINCH signal handler");
wait_for_terminal_ready(int fd) {
char data;
while(1) {
int ret = read(fd, &data, 1);
if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue;
break;
}
sigemptyset(&mask);
sigaddset(&mask, SIGWINCH);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while(!sigwinch_arrived) sigsuspend(&oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
}
static PyObject*
spawn(PyObject *self UNUSED, PyObject *args) {
PyObject *argv_p, *env_p;
int master, slave, stdin_read_fd, stdin_write_fd;
int master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd;
char *cwd, *exe;
if (!PyArg_ParseTuple(args, "ssO!O!iiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
if (!PyArg_ParseTuple(args, "ssO!O!iiiiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd, &ready_read_fd, &ready_write_fd)) return NULL;
char name[2048] = {0};
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
char **argv = serialize_string_tuple(argv_p);
@ -98,8 +85,10 @@ spawn(PyObject *self UNUSED, PyObject *args) {
#endif
close(tfd);
// Wait for SIGWINCH which indicates kitty has setup the screen object
wait_for_sigwinch();
// Wait for READY_SIGNAL which indicates kitty has setup the screen object
close(ready_write_fd);
wait_for_terminal_ready(ready_read_fd);
close(ready_read_fd);
// Redirect stdin/stdout/stderr to the pty
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");

View File

@ -101,6 +101,8 @@ def fork(self):
remove_cloexec(slave)
fast_data_types.set_iutf8(master, True)
stdin, self.stdin = self.stdin, None
ready_read_fd, ready_write_fd = os.pipe()
remove_cloexec(ready_read_fd)
if stdin is not None:
stdin_read_fd, stdin_write_fd = os.pipe()
remove_cloexec(stdin_read_fd)
@ -119,16 +121,22 @@ def fork(self):
# Some macOS machines need the shell to have argv[0] prefixed by
# hyphen, see https://github.com/kovidgoyal/kitty/issues/247
argv[0] = ('-' + exe.split('/')[-1])
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd)
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
os.close(slave)
self.pid = pid
self.child_fd = master
if stdin is not None:
os.close(stdin_read_fd)
fast_data_types.thread_write(stdin_write_fd, stdin)
os.close(ready_read_fd)
self.terminal_ready_fd = ready_write_fd
fcntl.fcntl(self.child_fd, fcntl.F_SETFL, fcntl.fcntl(self.child_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
return pid
def mark_terminal_ready(self):
os.close(self.terminal_ready_fd)
self.terminal_ready_fd = -1
@property
def cmdline(self):
try:

View File

@ -16,13 +16,12 @@
)
from .fast_data_types import (
BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM,
CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM,
GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM, OSC, REVERSE, SCROLL_FULL,
SCROLL_LINE, SCROLL_PAGE, STRIKETHROUGH, Screen, add_window,
cell_size_for_window, compile_program, get_clipboard_string,
glfw_post_empty_event, init_cell_program, set_clipboard_string,
set_titlebar_color, set_window_render_data, update_window_title,
update_window_visibility, viewport_for_window
CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM, GRAPHICS_PREMULT_PROGRAM,
GRAPHICS_PROGRAM, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
STRIKETHROUGH, Screen, add_window, cell_size_for_window, compile_program,
get_clipboard_string, glfw_post_empty_event, init_cell_program,
set_clipboard_string, set_titlebar_color, set_window_render_data,
update_window_title, update_window_visibility, viewport_for_window
)
from .keys import keyboard_mode_name
from .rgb import to_color
@ -104,6 +103,7 @@ class Window:
def __init__(self, tab, child, opts, args, override_title=None):
self.action_on_close = None
self.layout_data = None
self.pty_resized_once = False
self.needs_attention = False
self.override_title = override_title
self.overlay_window_id = None
@ -200,6 +200,9 @@ def set_geometry(self, window_idx, new_geometry):
sg = self.update_position(new_geometry)
self.needs_layout = False
boss.child_monitor.resize_pty(self.id, *current_pty_size)
if not self.pty_resized_once:
self.pty_resized_once = True
self.child.mark_terminal_ready()
else:
sg = self.update_position(new_geometry)
self.geometry = g = new_geometry