linux: close streams without an extra read (#3250)

This commit is contained in:
Jameson Nash 2026-03-21 15:10:05 -04:00 committed by GitHub
parent 14f6c4cc1f
commit 9f0101dcb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 14 additions and 24 deletions

View File

@ -1516,25 +1516,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
*/
pe->events &= w->pevents | POLLERR | POLLHUP;
/* Work around an epoll quirk where it sometimes reports just the
* EPOLLERR or EPOLLHUP event. In order to force the event loop to
* move forward, we merge in the read/write events that the watcher
* is interested in; uv__read() and uv__write() will then deal with
* the error or hangup in the usual fashion.
*
* Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
* reads the available data, calls uv_read_stop(), then sometime later
* calls uv_read_start() again. By then, libuv has forgotten about the
* hangup and the kernel won't report EPOLLIN again because there's
* nothing left to read. If anything, libuv is to blame here. The
* current hack is just a quick bandaid; to properly fix it, libuv
* needs to remember the error/hangup event. We should get that for
* free when we switch over to edge-triggered I/O.
*/
if (pe->events == POLLERR || pe->events == POLLHUP)
pe->events |=
w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI);
if (pe->events != 0) {
/* Run signal watchers last. This also affects child process watchers
* because those are implemented in terms of signal watchers.

View File

@ -60,6 +60,12 @@ void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
if (events & UV__POLLRDHUP)
pevents |= UV_DISCONNECT;
/* On error or hangup, mix in events the user is interested in so the
* appropriate read/write callbacks are invoked. */
if (events & (POLLERR | POLLHUP))
pevents |=
w->pevents & (UV_READABLE | UV_PRIORITIZED | UV_WRITABLE | UV_DISCONNECT);
handle->poll_cb(handle, 0, pevents);
}

View File

@ -1218,16 +1218,15 @@ void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
* operating systems, devices like PTYs sometimes produce partial reads even
* when more data is available.
*/
if ((events & POLLHUP) &&
if ((events & (POLLHUP | UV__POLLRDHUP)) &&
!(events & POLLIN) &&
(stream->flags & UV_HANDLE_READING) &&
!(stream->flags & UV_HANDLE_READ_EOF)) {
uv_buf_t buf = { NULL, 0 };
uv__stream_eof(stream, &buf);
}
if (uv__stream_fd(stream) == -1)
return; /* read_cb closed stream. */
}
if (events & (POLLOUT | POLLERR | POLLHUP)) {
uv__write(stream);

View File

@ -178,7 +178,11 @@ void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
handle = container_of(w, uv_udp_t, io_watcher);
assert(handle->type == UV_UDP);
if (revents & POLLIN)
/* Trigger a recv and send to find out what POLLERR occurred. */
if (revents & POLLERR)
revents |= POLLIN | POLLOUT;
if (revents & (POLLIN | POLLERR))
uv__udp_recvmsg(handle, 0);
/* Just Linux support for now. */