From 0c76cdb98ffa9a43f169e24b59a3df95f18803f3 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 8 Nov 2013 05:10:44 +0100 Subject: [PATCH] linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT 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. Fixes #982. This is a back-port of commit 24bfef2 from the master branch. --- src/unix/linux-core.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index cb6df789..6659dc4d 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -105,7 +105,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { uv__io_t* w; uint64_t base; uint64_t diff; - unsigned int masked_events; int nevents; int count; int nfds; @@ -209,16 +208,35 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; } - /* - * Give users only events they're interested in. Prevents spurious + /* Give users only events they're interested in. Prevents spurious * callbacks when previous callback invocation in this loop has stopped * the current watcher. Also, filters out events that users has not * requested us to watch. */ - masked_events = pe->events & w->pevents; - if (masked_events != 0) - w->cb(loop, w, masked_events); - nevents++; + pe->events &= w->pevents | UV__POLLERR | UV__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 == UV__EPOLLERR || pe->events == UV__EPOLLHUP) + pe->events |= w->pevents & (UV__EPOLLIN | UV__EPOLLOUT); + + if (pe->events != 0) { + w->cb(loop, w, pe->events); + nevents++; + } } if (nevents != 0) {