diff --git a/src/unix/linux.c b/src/unix/linux.c index 110964884..9e4df419a 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -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. diff --git a/src/unix/poll.c b/src/unix/poll.c index 60c3c2009..41429efc7 100644 --- a/src/unix/poll.c +++ b/src/unix/poll.c @@ -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); } diff --git a/src/unix/stream.c b/src/unix/stream.c index 31f22136e..d6f78f354 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -1218,17 +1218,16 @@ 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 (uv__stream_fd(stream) == -1) - return; /* read_cb closed stream. */ - if (events & (POLLOUT | POLLERR | POLLHUP)) { uv__write(stream); uv__write_callbacks(stream); diff --git a/src/unix/udp.c b/src/unix/udp.c index 0a41391ff..c520128bc 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -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. */