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); } #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 static inline void
wait_for_sigwinch() { wait_for_terminal_ready(int fd) {
sigwinch_arrived = 0; char data;
sigset_t mask, oldmask; while(1) {
struct sigaction sa; int ret = read(fd, &data, 1);
sa.sa_handler = handle_sigwinch; if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue;
sa.sa_flags = SA_RESTART; break;
if (sigaction(SIGWINCH, &sa, NULL) == -1) {
exit_on_err("Failed to set the SIGWINCH signal handler");
} }
sigemptyset(&mask);
sigaddset(&mask, SIGWINCH);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while(!sigwinch_arrived) sigsuspend(&oldmask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
} }
static PyObject* static PyObject*
spawn(PyObject *self UNUSED, PyObject *args) { spawn(PyObject *self UNUSED, PyObject *args) {
PyObject *argv_p, *env_p; 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; 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}; char name[2048] = {0};
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
char **argv = serialize_string_tuple(argv_p); char **argv = serialize_string_tuple(argv_p);
@ -98,8 +85,10 @@ spawn(PyObject *self UNUSED, PyObject *args) {
#endif #endif
close(tfd); close(tfd);
// Wait for SIGWINCH which indicates kitty has setup the screen object // Wait for READY_SIGNAL which indicates kitty has setup the screen object
wait_for_sigwinch(); close(ready_write_fd);
wait_for_terminal_ready(ready_read_fd);
close(ready_read_fd);
// Redirect stdin/stdout/stderr to the pty // Redirect stdin/stdout/stderr to the pty
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1"); if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");

View File

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

View File

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