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. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									ee36856d8c
								
							
						
					
					
						commit
						06f59e9f5d
					
				|  | @ -396,6 +396,16 @@ other | ||||||
| 'GIT_PAGER':: | 'GIT_PAGER':: | ||||||
| 	This environment variable overrides `$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':: | 'GIT_TRACE':: | ||||||
| 	If this variable is set to "1", "2" or "true" (comparison | 	If this variable is set to "1", "2" or "true" (comparison | ||||||
| 	is case insensitive), git will print `trace:` messages on | 	is case insensitive), git will print `trace:` messages on | ||||||
|  |  | ||||||
|  | @ -1459,6 +1459,7 @@ static void found_guilty_entry(struct blame_entry *ent) | ||||||
| 				printf("boundary\n"); | 				printf("boundary\n"); | ||||||
| 		} | 		} | ||||||
| 		write_filename_info(suspect->path); | 		write_filename_info(suspect->path); | ||||||
|  | 		maybe_flush_or_die(stdout, "stdout"); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -100,7 +100,7 @@ static void show_commit(struct commit *commit) | ||||||
| 		printf("%s%c", buf, hdr_termination); | 		printf("%s%c", buf, hdr_termination); | ||||||
| 		free(buf); | 		free(buf); | ||||||
| 	} | 	} | ||||||
| 	fflush(stdout); | 	maybe_flush_or_die(stdout, "stdout"); | ||||||
| 	if (commit->parents) { | 	if (commit->parents) { | ||||||
| 		free_commit_list(commit->parents); | 		free_commit_list(commit->parents); | ||||||
| 		commit->parents = NULL; | 		commit->parents = NULL; | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								cache.h
								
								
								
								
							
							
						
						
									
										2
									
								
								cache.h
								
								
								
								
							|  | @ -532,6 +532,8 @@ extern char git_default_name[MAX_GITNAME]; | ||||||
| extern const char *git_commit_encoding; | extern const char *git_commit_encoding; | ||||||
| extern const char *git_log_output_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 copy_fd(int ifd, int ofd); | ||||||
| extern int read_in_full(int fd, void *buf, size_t count); | 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); | extern int write_in_full(int fd, const void *buf, size_t count); | ||||||
|  |  | ||||||
|  | @ -408,5 +408,6 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit) | ||||||
| 		shown = 1; | 		shown = 1; | ||||||
| 	} | 	} | ||||||
| 	opt->loginfo = NULL; | 	opt->loginfo = NULL; | ||||||
|  | 	maybe_flush_or_die(stdout, "stdout"); | ||||||
| 	return shown; | 	return shown; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,45 @@ | ||||||
| #include "cache.h" | #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) | int read_in_full(int fd, void *buf, size_t count) | ||||||
| { | { | ||||||
| 	char *p = buf; | 	char *p = buf; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Theodore Ts'o
						Theodore Ts'o