From 12210fe578623995d13cc5126427c1c67de4b6e0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 8 Jun 2013 03:20:29 +0200 Subject: [PATCH] unix: fix busy loop, write if POLLERR or POLLHUP This fixes a busy loop by working around a quirk with Linux kernels <= 2.6.32 where an EPIPE or ECONNRESET error on a file descriptor that is polled for EPOLLOUT but not EPOLLIN gets reported by epoll_wait() as just EPOLLERR|EPOLLHUP, like this: epoll_wait(5, {{EPOLLERR|EPOLLHUP, {u32=12, u64=12}}}, 1024, 433) = 1 Before this commit, libuv called uv__read() which attempts to read from the file descriptor. With newer kernels and on other operating systems that fails like this: read(12, "", 65536) = -1 EPIPE (Broken pipe) Which tells libuv there is a connection error and it should notify the user of that. On the affected Linux kernels however, the read succeeds with an EOF: read(12, "", 65536) = 0 Which is subsequently passed on to the user. In most cases, the user will close the handle and everything is fine. Node.js however sometimes keeps the handle open in an attempt to flush pending write requests. While libuv doesn't officially support this, unofficially it works... ...except on those older kernels. Because the kernel keeps waking up the event loop without setting POLLOUT and because the read calls EOF but don't error, libuv's I/O state machine doesn't progress. That's why this commit changes uv__stream_io() to also write pending data. While the read() system call doesn't error, the write() system call will. Fixes joyent/node#5504. --- src/unix/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/stream.c b/src/unix/stream.c index ae2e6a04..c9df9797 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -1118,7 +1118,7 @@ static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { return; /* read_cb closed stream. */ } - if (events & UV__POLLOUT) { + if (events & (UV__POLLOUT | UV__POLLERR | UV__POLLHUP)) { assert(uv__stream_fd(stream) >= 0); uv__write(stream); uv__write_callbacks(stream);