io: retry fgets on EINTR (#7632)

The calls to `fgets` in `src/nvim/if_cscope.c` (and elsewhere) can show
communication errors to the user if a signal is delivered during its
system calls. For plugins that proxy subprocess output into cscope
requests, a `SIGCHLD` might *always* interfere with calls into `fgets`.

To see this in a debugger, put a breakpoint on `cs_reading_emsg` and
watch signals come in (with lldb, using `process handle --notify true
--pass true`).  Next, run a subcommand from neovim that calls through
cscope when it returns.  A tag picker plugin, like vim-picker and fzy,
with `cscopetag` and `cscopetagorder=0` set, reproduced this reliably.
The breakpoint will hit after a `SIGCHLD` is delivered, and `errno` will
be set to 4, `EINTR`.

The caller of `fgets` should retry when `NULL` is returned with `errno`
set to `EINTR`.
This commit is contained in:
Matt Widmann
2017-11-25 13:59:07 -08:00
parent d9b3ebfede
commit 0f9c90e0ed
4 changed files with 43 additions and 5 deletions

View File

@@ -4448,7 +4448,12 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL
char tbuf[FGETS_SIZE];
buf[size - 2] = NUL;
retry:
errno = 0;
eof = fgets((char *)buf, size, fp);
if (eof == NULL && errno == EINTR) {
goto retry;
}
if (buf[size - 2] != NUL && buf[size - 2] != '\n') {
buf[size - 1] = NUL; /* Truncate the line */