xwrite: poll on non-blocking FDs
authorEric Wong <e@80x24.org>
Sun, 26 Jun 2016 23:21:12 +0000 (23:21 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Jun 2016 15:34:15 +0000 (08:34 -0700)
write(2) can hit the same EAGAIN/EWOULDBLOCK errors as read(2),
so busy-looping on a non-blocking FD is a waste of resources.

Currently, I do not know of a way for this happen:

* the NonBlocking directive in systemd does not apply to stdin,
stdout, or stderr.

* xinetd provides no way to set the non-blocking flag at all

But theoretically, it's possible a careless C10K HTTP server
could use pipe2(..., O_NONBLOCK) to setup a pipe for
git-http-backend with only the intent to use non-blocking reads;
but accidentally leave non-blocking set on the write end passed
as stdout to git-upload-pack.

Followup-to: 1079c4be0b720 ("xread: poll on non blocking fds")

Signed-off-by: Eric Wong <e@80x24.org>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
wrapper.c
index 9b20eb9351601a9d2b609fe7959158ce98970171..f7ea6c43bee7b5483ed82d273832e7ac6ea32e24 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -271,8 +271,26 @@ ssize_t xwrite(int fd, const void *buf, size_t len)
            len = MAX_IO_SIZE;
        while (1) {
                nr = write(fd, buf, len);
-               if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
-                       continue;
+               if (nr < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                               struct pollfd pfd;
+                               pfd.events = POLLOUT;
+                               pfd.fd = fd;
+                               /*
+                                * it is OK if this poll() failed; we
+                                * want to leave this infinite loop
+                                * only when write() returns with
+                                * success, or an expected failure,
+                                * which would be checked by the next
+                                * call to write(2).
+                                */
+                               poll(&pfd, 1, -1);
+                               continue;
+                       }
+               }
+
                return nr;
        }
 }