From dfa2198efb9f0d298b277e340ee93c9fead8a065 Mon Sep 17 00:00:00 2001
From: Devin Hussey <husseydevin@gmail.com>
Date: Fri, 8 Jun 2018 12:38:19 -0400
Subject: [PATCH] Use manual lock/getc_unlocked, use fixed size buffer globally

read_file() now locks the buffer manually and then uses
getc_unlocked() instead of getc() to read files much faster on
FreeBSD (BSD/macOS), and Bionic (Android) libc.

Unlike glibc which only locks the file in getc if it is needed,
FreeBSD and Bionic libc will always lock the file, causing a massive
slowdown, as the OS has to create and destroy a mutex each time
getc() is called.

This makes it so we only have to lock once and the OS can be as
careless as it desires.

On these systems, this patch results in read times being anywhere
from 2-6x faster.

Additionally, via setvbuf, we manually set the buffering size to
NANO_BUF_SIZE (formerly LOCKBUFSIZE) for each file we open.

Signed-off-by: Devin Hussey <husseydevin@gmail.com>
---
 src/files.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/src/files.c b/src/files.c
index 8cdf1959..cccc88c1 100644
--- a/src/files.c
+++ b/src/files.c
@@ -30,7 +30,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#define LOCKBUFSIZE 8192
+#define NANO_BUF_SIZE 8192
 
 /* Verify that the containing directory of the given filename exists. */
 bool has_valid_path(const char *filename)
@@ -226,6 +226,9 @@ int write_lockfile(const char *lockfilename, const char *origfilename, bool modi
 		goto free_the_data;
 	}
 
+	if (NANO_BUF_SIZE > BUFSIZ)
+		setvbuf(filestream, NULL, _IOFBF, NANO_BUF_SIZE);
+
 	/* This is the lock data we will store:
 	 *
 	 * byte 0        - 0x62
@@ -324,11 +327,11 @@ int do_lockfile(const char *filename)
 			goto free_the_name;
 		}
 
-		lockbuf = charalloc(LOCKBUFSIZE);
+		lockbuf = charalloc(NANO_BUF_SIZE);
 		do {
-			readamt = read(lockfd, &lockbuf[readtot], LOCKBUFSIZE - readtot);
+			readamt = read(lockfd, &lockbuf[readtot], NANO_BUF_SIZE - readtot);
 			readtot += readamt;
-		} while (readamt > 0 && readtot < LOCKBUFSIZE);
+		} while (readamt > 0 && readtot < NANO_BUF_SIZE);
 
 		close(lockfd);
 
@@ -760,8 +763,12 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable)
 	topline = make_new_node(NULL);
 	bottomline = topline;
 
+	/* Lock our file before we read it; that way we don't have to lock
+	 * it each time we read it. */
+	flockfile(f);
+
 	/* Read the entire file into the new buffer. */
-	while ((input_int = getc(f)) != EOF) {
+	while ((input_int = getc_unlocked(f)) != EOF) {
 		input = (char)input_int;
 
 		/* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file
@@ -828,6 +835,9 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable)
 #endif
 	}
 
+	/* We are done with the file, unlock it. */
+	funlockfile(f);
+
 	/* Perhaps this could use some better handling. */
 	if (ferror(f))
 		nperror(filename);
@@ -972,6 +982,9 @@ int open_file(const char *filename, bool newfie, bool quiet, FILE **f)
 			close(fd);
 		} else if (!inhelp)
 			statusbar(_("Reading File"));
+
+		if (NANO_BUF_SIZE > BUFSIZ)
+			setvbuf(*f, NULL, _IOFBF, NANO_BUF_SIZE);
 	}
 
 	free(full_filename);
@@ -1403,9 +1416,12 @@ char *safe_tempfile(FILE **f)
 
 	fd = mkstemp(full_tempdir);
 
-	if (fd != -1)
+	if (fd != -1) {
 		*f = fdopen(fd, "r+b");
-	else {
+
+		if (NANO_BUF_SIZE > BUFSIZ)
+			setvbuf(*f, NULL, _IOFBF, NANO_BUF_SIZE);
+	} else {
 		free(full_tempdir);
 		full_tempdir = NULL;
 	}
@@ -1631,6 +1647,9 @@ bool write_file(const char *name, FILE *f_open, bool tmp,
 				 * nothing. */
 				goto skip_backup;
 			}
+
+			if (NANO_BUF_SIZE > BUFSIZ)
+				setvbuf(f, NULL, _IOFBF, NANO_BUF_SIZE);
 		}
 
 		/* If backup_dir is set, we set backupname to
@@ -1708,6 +1727,9 @@ bool write_file(const char *name, FILE *f_open, bool tmp,
 			goto cleanup_and_exit;
 		}
 
+		if (NANO_BUF_SIZE > BUFSIZ)
+			setvbuf(backup_file, NULL, _IOFBF, NANO_BUF_SIZE);
+
 		/* Only try chowning the backup when we're root. */
 		if (geteuid() == NANO_ROOT_UID &&
 						fchown(backup_fd, openfile->current_stat->st_uid,
@@ -1786,6 +1808,9 @@ bool write_file(const char *name, FILE *f_open, bool tmp,
 						strerror(errno));
 				goto cleanup_and_exit;
 			}
+
+			if (NANO_BUF_SIZE > BUFSIZ)
+				setvbuf(f, NULL, _IOFBF, NANO_BUF_SIZE);
 		}
 
 		tempname = safe_tempfile(&f);
@@ -1809,6 +1834,9 @@ bool write_file(const char *name, FILE *f_open, bool tmp,
 					unlink(tempname);
 					goto cleanup_and_exit;
 				}
+
+				if (NANO_BUF_SIZE > BUFSIZ)
+					setvbuf(f_source, NULL, _IOFBF, NANO_BUF_SIZE);
 			}
 		}
 
@@ -1847,6 +1875,9 @@ bool write_file(const char *name, FILE *f_open, bool tmp,
 			close(fd);
 			goto cleanup_and_exit;
 		}
+
+		if (NANO_BUF_SIZE > BUFSIZ)
+			setvbuf(f, NULL, _IOFBF, NANO_BUF_SIZE);
 	}
 
 	while (fileptr != NULL) {
@@ -1911,6 +1942,9 @@ bool write_file(const char *name, FILE *f_open, bool tmp,
 			f_source = fdopen(fd_source, "rb");
 			if (f_source == NULL)
 				close(fd_source);
+
+			if (NANO_BUF_SIZE > BUFSIZ)
+				setvbuf(f_source, NULL, _IOFBF, NANO_BUF_SIZE);
 		}
 
 		if (f_source == NULL) {
-- 
2.17.0

