diff options
author | Eric Wong <e@80x24.org> | 2016-06-26 23:21:12 +0000 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2016-06-27 08:34:15 -0700 |
commit | ef1cf0167a362cd504afdcc0eef4c3200ea6dfbb (patch) | |
tree | 16d11b77fbdbcd297617073c3cedfe2070ddef3d | |
parent | xread: retry after poll on EAGAIN/EWOULDBLOCK (diff) | |
download | tgif-ef1cf0167a362cd504afdcc0eef4c3200ea6dfbb.tar.xz |
xwrite: poll on non-blocking FDs
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>
-rw-r--r-- | wrapper.c | 22 |
1 files changed, 20 insertions, 2 deletions
@@ -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; } } |