git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] Don't fflush(stdout) when it's not helpful

From
Theodore Tso <tytso@mit.edu>
Date
Jun 29, 2007, 17:40 UTC
Message-ID
<20070629174046.GC16268@thunk.org>
In-Reply-To
<alpine.LFD.0.98.0706290851480.8675@woody.linux-foundation.org>
On Fri, Jun 29, 2007 at 09:06:22AM -0700, Linus Torvalds wrote:
Show 5 quoted lines
> I think that patch looks fine, but I also think that there is a more 
> fundamental problem with this approach:
> 
>  - all these patches basically break the whole _point_ of Jim's original 
>    reason for wanting this!

Yeah, I pointed that out in my first patch. It had seemed that interactivity over a pipe was considered more important, though when we started talking about things. :-)

It looks like from my reading of the standard that ferror(f) should not change the state of the file handle f. So the following patch I think should work; it checks ferror(f), and if it indicates that there is an error, we try a flush to get the error message. I've tested under Linux and it gives the correct error message in the "git log > /mnt/full-filesystem" case, and I believe it should DTRT on other systems.

Comments?
						- Ted
commit 93a96f94028106687412acbb771bb18ee7ec5560
Author: Theodore Ts'o <tytso@mit.edu>
Date:   Thu Jun 28 14:10:58 2007 -0400
    Don't fflush(stdout) when it's not helpful
    
    This patch arose from a discussion started by Jim Meyering's patch
    whose intention was to provide better diagnostics for failed writes.
    Linus proposed a better way to do things, which also had the added
    benefit that adding a fflush() to git-log-* operations and incremental
    git-blame operations could improve interactive respose time feel, at
    the cost of making things a bit slower when we aren't piping the
    output to a downstream program.
    
    This patch skips the fflush() calls when stdout is a regular file, or
    if the environment variable GIT_FLUSH is set to "0".  This latter can
    speed up a command such as:
    
    GIT_FLUSH=0 strace -c -f -e write time git-rev-list HEAD | wc -l
    
    a tiny amount.
    
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 20b5b7b..8269148 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -396,6 +396,16 @@ other
 'GIT_PAGER'::
 	This environment variable overrides `$PAGER`.
 
+'GIT_FLUSH'::
+	If this environment variable is set to "1", then commands such
+	as git-blame (in incremental mode), git-rev-list, git-log,
+	git-whatchanged, etc., will force a flush of the output stream
+	after each commit-oriented record have been flushed.   If this
+	variable is set to "0", the output of these commands will be done
+	using completely buffered I/O.   If this environment variable is
+	not set, git will choose buffered or record-oriented flushing
+	based on whether stdout appears to be redirected to a file or not.
+
 'GIT_TRACE'::
 	If this variable is set to "1", "2" or "true" (comparison
 	is case insensitive), git will print `trace:` messages on
diff --git a/builtin-blame.c b/builtin-blame.c
index f7e2c13..da23a6f 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1459,6 +1459,7 @@ static void found_guilty_entry(struct blame_entry *ent)
 				printf("boundary\n");
 		}
 		write_filename_info(suspect->path);
+		maybe_flush_or_die(stdout, "stdout");
 	}
 }
 
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 813aadf..86db8b0 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -100,7 +100,7 @@ static void show_commit(struct commit *commit)
 		printf("%s%c", buf, hdr_termination);
 		free(buf);
 	}
-	fflush(stdout);
+	maybe_flush_or_die(stdout, "stdout");
 	if (commit->parents) {
 		free_commit_list(commit->parents);
 		commit->parents = NULL;
diff --git a/cache.h b/cache.h
index ed83d92..0525c4e 100644
--- a/cache.h
+++ b/cache.h
@@ -532,6 +532,8 @@ extern char git_default_name[MAX_GITNAME];
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
 
+/* IO helper functions */
+extern void maybe_flush_or_die(FILE *, const char *);
 extern int copy_fd(int ifd, int ofd);
 extern int read_in_full(int fd, void *buf, size_t count);
 extern int write_in_full(int fd, const void *buf, size_t count);
diff --git a/log-tree.c b/log-tree.c
index 0cf21bc..ced3f33 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -408,5 +408,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
 		shown = 1;
 	}
 	opt->loginfo = NULL;
+	maybe_flush_or_die(stdout, "stdout");
 	return shown;
 }
diff --git a/write_or_die.c b/write_or_die.c
index 5c4bc85..e125e11 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -1,5 +1,45 @@
 #include "cache.h"
 
+/*
+ * Some cases use stdio, but want to flush after the write
+ * to get error handling (and to get better interactive
+ * behaviour - not buffering excessively).
+ *
+ * Of course, if the flush happened within the write itself,
+ * we've already lost the error code, and cannot report it any
+ * more. So we just ignore that case instead (and hope we get
+ * the right error code on the flush).
+ *
+ * If the file handle is stdout, and stdout is a file, then skip the
+ * flush entirely since it's not needed.
+ */
+void maybe_flush_or_die(FILE *f, const char *desc)
+{
+	static int skip_stdout_flush = -1;
+	struct stat st;
+	char *cp;
+
+	if (f == stdout) {
+		if (skip_stdout_flush < 0) {
+			cp = getenv("GIT_FLUSH");
+			if (cp)
+				skip_stdout_flush = (atoi(cp) == 0);
+			else if ((fstat(fileno(stdout), &st) == 0) &&
+				 S_ISREG(st.st_mode))
+				skip_stdout_flush = 1;
+			else
+				skip_stdout_flush = 0;
+		}
+		if (skip_stdout_flush && !ferror(f))
+			return;
+	}
+	if (fflush(f)) {
+		if (errno == EPIPE)
+			exit(0);
+		die("write failure on %s: %s", desc, strerror(errno));
+	}
+}
+
 int read_in_full(int fd, void *buf, size_t count)
 {
 	char *p = buf;
Previous: Linus TorvaldsNext: Linus Torvalds
Message 21 of 33 in “git-rev-list: give better diagnostic for failed write”
  1. git-rev-list: give better diagnostic for failed writeJim Meyering, Jun 25, 2007
  2. Linus TorvaldsJun 25, 2007
  3. Jim MeyeringJun 25, 2007
  4. Linus TorvaldsJun 25, 2007
  5. Linus TorvaldsJun 25, 2007
  6. Linus TorvaldsJun 25, 2007
  7. Jim MeyeringJun 27, 2007
  8. Linus TorvaldsJun 25, 2007
  9. Theodore TsoJun 26, 2007
  10. Linus TorvaldsJun 26, 2007
  11. Theodore TsoJun 26, 2007
  12. Linus TorvaldsJun 26, 2007
  13. Theodore TsoJun 28, 2007
  14. Jeff KingJun 28, 2007
  15. Don't fflush(stdout) when it's not helpfulTheodore Tso, Jun 28, 2007
  16. Frank LichtenheldJun 29, 2007
  17. Theodore TsoJun 29, 2007
  18. Jeff KingJun 29, 2007
  19. Junio C HamanoJun 29, 2007
  20. Linus TorvaldsJun 29, 2007
  21. Theodore TsoJun 29, 2007
  22. Linus TorvaldsJun 29, 2007
  23. Junio C HamanoJun 30, 2007
  24. Linus TorvaldsJun 30, 2007
  25. Theodore TsoJun 30, 2007
  26. Junio C HamanoJun 30, 2007
  27. Jeff KingJun 26, 2007
  28. Linus TorvaldsJun 26, 2007
  29. Jim MeyeringJun 27, 2007
  30. Linus TorvaldsJun 27, 2007
  31. Jim MeyeringJun 25, 2007
  32. Linus TorvaldsJun 25, 2007
  33. Jim MeyeringJun 25, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.