On Mon, Jun 27, 2016 at 05:49:48PM -0400, Jeff King wrote: > So in general I would say that handing non-blocking descriptors to git > is not safe. I think it's possible to loop on getdelim() when we see > EAGAIN, but I'm not sure if it's worth it. The patch for that is below, for the curious. It works with even this: { for i in H E A D; do sleep 1 printf $i done printf "\n" } | nonblock git cat-file --batch-check Note that I folded the "did we see EAGAIN" check into my sub-function (which is the equivalent of your io_wait). You might want to do that in the xread() code path, too, as it makes the resulting code there a very nice: if (errno == EINTR) continue; if (handle_nonblock(fd, POLLIN)) continue; --- diff --git a/strbuf.c b/strbuf.c index 1ba600b..2147873 100644 --- a/strbuf.c +++ b/strbuf.c @@ -452,6 +452,38 @@ int strbuf_getcwd(struct strbuf *sb) return -1; } +int handle_nonblock(FILE *fp, short poll_events) +{ + if (ferror(fp) && (errno == EAGAIN || errno == EWOULDBLOCK)) { + struct pollfd pfd; + + pfd.events = poll_events; + pfd.fd = fileno(fp); + poll(&pfd, 1, -1); + clearerr(fp); + return 1; + } + return 0; +} + +static int getline_stdio_loop(struct strbuf *sb, FILE *fp, int term) +{ + int ch; + do { + errno = 0; + flockfile(fp); + while ((ch = getc_unlocked(fp)) != EOF) { + if (!strbuf_avail(sb)) + strbuf_grow(sb, 1); + sb->buf[sb->len++] = ch; + if (ch == term) + break; + } + funlockfile(fp); + } while (handle_nonblock(fp, POLLIN)); + return ch; +} + #ifdef HAVE_GETDELIM int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) { @@ -465,12 +497,31 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) /* Translate slopbuf to NULL, as we cannot call realloc on it */ if (!sb->alloc) sb->buf = NULL; + + errno = 0; r = getdelim(&sb->buf, &sb->alloc, term, fp); if (r > 0) { sb->len = r; - return 0; } + + /* + * getdelim will return characters to us even if it sees EAGAIN; + * we want to read all the way to an actual delimiter or EOF. + * + * We can't just loop on getdelim, though, as it wants to write + * the whole buffer itself. So fall back to a stdio loop, which + * can incrementally add to our strbuf. + */ + if (handle_nonblock(fp, POLLIN)) { + getline_stdio_loop(sb, fp, term); + /* Propagate real errors */ + if (ferror(fp)) + r = -1; + } + if (r > 0) + return 0; + assert(r == -1); /* @@ -507,15 +558,7 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) return EOF; strbuf_reset(sb); - flockfile(fp); - while ((ch = getc_unlocked(fp)) != EOF) { - if (!strbuf_avail(sb)) - strbuf_grow(sb, 1); - sb->buf[sb->len++] = ch; - if (ch == term) - break; - } - funlockfile(fp); + ch = getline_stdio_loop(sb, fp, term); if (ch == EOF && sb->len == 0) return EOF; -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html