Make termsize thread safe

This commit is contained in:
ridiculousfish 2019-04-28 16:56:48 -07:00
parent 0de1611bf1
commit e2c66a8131

View file

@ -97,11 +97,11 @@ int get_debug_stack_frames() { return debug_stack_frames; }
static relaxed_atomic_t<pid_t> initial_fg_process_group{-1}; static relaxed_atomic_t<pid_t> initial_fg_process_group{-1};
/// This struct maintains the current state of the terminal size. It is updated on demand after /// This struct maintains the current state of the terminal size. It is updated on demand after
/// receiving a SIGWINCH. Do not touch this struct directly, it's managed with a rwlock. Use /// receiving a SIGWINCH. Use common_get_width()/common_get_height() to read it lazily.
/// common_get_width()/common_get_height(). static constexpr struct winsize k_invalid_termsize = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
static std::mutex termsize_lock; static relaxed_atomic_t<struct winsize> s_termsize(k_invalid_termsize);
static struct winsize termsize = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
static volatile bool termsize_valid = false; static relaxed_atomic_bool_t s_termsize_valid{false};
static char *wcs2str_internal(const wchar_t *in, char *out); static char *wcs2str_internal(const wchar_t *in, char *out);
static void debug_shared(const wchar_t msg_level, const wcstring &msg); static void debug_shared(const wchar_t msg_level, const wcstring &msg);
@ -1738,20 +1738,17 @@ bool unescape_string(const wcstring &input, wcstring *output, unescape_flags_t e
/// COLUMNS or LINES variables are changed. This is also invoked when the shell regains control of /// COLUMNS or LINES variables are changed. This is also invoked when the shell regains control of
/// the tty since it is possible the terminal size changed while an external command was running. /// the tty since it is possible the terminal size changed while an external command was running.
void invalidate_termsize(bool invalidate_vars) { void invalidate_termsize(bool invalidate_vars) {
termsize_valid = false; s_termsize_valid = false;
if (invalidate_vars) { if (invalidate_vars) {
struct winsize termsize = s_termsize;
termsize.ws_col = termsize.ws_row = USHRT_MAX; termsize.ws_col = termsize.ws_row = USHRT_MAX;
termsize = s_termsize;
} }
} }
/// Handle SIGWINCH. This is also invoked when the shell regains control of the tty since it is /// Handle SIGWINCH. This is also invoked when the shell regains control of the tty since it is
/// possible the terminal size changed while an external command was running. /// possible the terminal size changed while an external command was running.
void common_handle_winch(int signal) { void common_handle_winch(int signal) { s_termsize_valid = false; }
// Don't run ioctl() here. Technically it's not safe to use in signals although in practice it
// is safe on every platform I've used. But we want to be conservative on such matters.
UNUSED(signal);
invalidate_termsize(false);
}
/// Validate the new terminal size. Fallback to the env vars if necessary. Ensure the values are /// Validate the new terminal size. Fallback to the env vars if necessary. Ensure the values are
/// sane and if not fallback to a default of 80x24. /// sane and if not fallback to a default of 80x24.
@ -1810,16 +1807,15 @@ static void export_new_termsize(struct winsize *new_termsize, env_stack_t &vars)
/// Updates termsize as needed, and returns a copy of the winsize. /// Updates termsize as needed, and returns a copy of the winsize.
struct winsize get_current_winsize() { struct winsize get_current_winsize() {
scoped_lock guard(termsize_lock); struct winsize termsize = s_termsize;
if (s_termsize_valid) return termsize;
if (termsize_valid) return termsize;
struct winsize new_termsize = {0, 0, 0, 0}; struct winsize new_termsize = {0, 0, 0, 0};
#ifdef HAVE_WINSIZE #ifdef HAVE_WINSIZE
errno = 0; errno = 0;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &new_termsize) != -1 && if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &new_termsize) != -1 &&
new_termsize.ws_col == termsize.ws_col && new_termsize.ws_row == termsize.ws_row) { new_termsize.ws_col == termsize.ws_col && new_termsize.ws_row == termsize.ws_row) {
termsize_valid = true; s_termsize_valid = true;
return termsize; return termsize;
} }
#endif #endif
@ -1828,7 +1824,8 @@ struct winsize get_current_winsize() {
export_new_termsize(&new_termsize, vars); export_new_termsize(&new_termsize, vars);
termsize.ws_col = new_termsize.ws_col; termsize.ws_col = new_termsize.ws_col;
termsize.ws_row = new_termsize.ws_row; termsize.ws_row = new_termsize.ws_row;
termsize_valid = true; s_termsize = termsize;
s_termsize_valid = true;
return termsize; return termsize;
} }