fix(process): handle poll() interrupted by a signal (#38024)

It is indeed possible that a signal may arrive during flush_stream()
(e.g. SIGCHLD from another child process), so poll() again on EINTR.

Fixes the following Coverity warning:
_____________________________________________________________________________________________
*** CID 644345:         Error handling issues  (CHECKED_RETURN)
/src/nvim/event/proc.c: 405             in flush_stream()
399
400     #ifdef __linux__
401         // On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending
402         // work on kernel workqueue, so use an explcit poll() before that. #37982
403         if (proc->type == kProcTypePty && !stream->did_eof) {
404           struct pollfd pollfd = { .fd = ((PtyProc *)proc)->tty_fd, .events = POLLIN };
>>>     CID 644345:         Error handling issues  (CHECKED_RETURN)
>>>     Calling "poll(&pollfd, 1UL, 0)" without checking return value. This library function may fail and return an error code.
405           poll(&pollfd, 1, 0);
406         }
407     #endif
408         // Poll for data and process the generated events.
409         loop_poll_events(proc->loop, 0);
410         if (stream->s.events) {

Another possible error is ENOMEM, which is probably not worth handling.
For reference, #23308 previously removed a case of ENOMEM handling, and
this PR also removes an outdated mention of that.

Also reduce the number of #ifdefs in non-OS-specific files.
This commit is contained in:
zeertzjq
2026-02-23 12:28:17 +08:00
committed by GitHub
parent ec24746406
commit 97509aa2be
5 changed files with 34 additions and 13 deletions

View File

@@ -849,10 +849,8 @@ static void term_resize(uint16_t width, uint16_t height, void *data)
static void term_resume(void *data)
{
#ifdef UNIX
Channel *chan = data;
pty_proc_resume(&chan->stream.pty);
#endif
}
static inline void term_delayed_free(void **argv)

View File

@@ -3,9 +3,6 @@
#include <signal.h>
#include <string.h>
#include <uv.h>
#ifdef __linux__
# include <poll.h>
#endif
#include "klib/kvec.h"
#include "nvim/channel.h"
@@ -397,14 +394,9 @@ static void flush_stream(Proc *proc, RStream *stream)
// Remember number of bytes before polling.
size_t num_bytes = stream->num_bytes;
#ifdef __linux__
// On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending
// work on kernel workqueue, so use an explcit poll() before that. #37982
if (proc->type == kProcTypePty && !stream->did_eof) {
struct pollfd pollfd = { .fd = ((PtyProc *)proc)->tty_fd, .events = POLLIN };
poll(&pollfd, 1, 0);
pty_proc_flush_master((PtyProc *)proc);
}
#endif
// Poll for data and process the generated events.
loop_poll_events(proc->loop, 0);
if (stream->s.events) {

View File

@@ -563,7 +563,7 @@ int os_open_stdin_fd(void)
/// Read from a file
///
/// Handles EINTR and ENOMEM, but not other errors.
/// Handles EINTR, but not other errors.
///
/// @param[in] fd File descriptor to read from.
/// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set

View File

@@ -31,6 +31,9 @@ int forkpty(int *, char *, const struct termios *, const struct winsize *);
#ifdef __APPLE__
# include <crt_externs.h>
#endif
#ifdef __linux__
# include <poll.h>
#endif
#include "auto/config.h"
#include "klib/kvec.h"
@@ -240,12 +243,29 @@ void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height)
}
void pty_proc_resume(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
// Send SIGCONT to the entire process group, as some shells (e.g. fish) don't
// propagate SIGCONT to suspended child processes.
killpg(((Proc *)ptyproc)->pid, SIGCONT);
}
/// On Linux, libuv's polling (which uses epoll) doesn't flush PTY master's pending
/// work on kernel workqueue, so use an explcit poll() before that. #37982
/// Note that poll() only flushes pending work if no data is immediately available,
/// so this function is needed before every libuv poll in flush_stream().
void pty_proc_flush_master(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
#ifdef __linux__
struct pollfd pollfd = { .fd = ptyproc->tty_fd, .events = POLLIN };
int n = 0;
do {
n = poll(&pollfd, 1, 0);
} while (n < 0 && errno == EINTR);
#endif
}
void pty_proc_close(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
@@ -256,7 +276,8 @@ void pty_proc_close(PtyProc *ptyproc)
}
}
void pty_proc_close_master(PtyProc *ptyproc) FUNC_ATTR_NONNULL_ALL
void pty_proc_close_master(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
if (ptyproc->tty_fd >= 0) {
close(ptyproc->tty_fd);

View File

@@ -180,6 +180,16 @@ void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height)
os_conpty_set_size(ptyproc->conpty, width, height);
}
void pty_proc_resume(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
}
void pty_proc_flush_master(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{
}
void pty_proc_close(PtyProc *ptyproc)
FUNC_ATTR_NONNULL_ALL
{