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.
This commit is contained in:
Ben Noordhuis 2013-11-08 05:10:44 +01:00
parent 991409e461
commit 0c76cdb98f

View File

@ -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) {