All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: tr@thomasrast.ch, "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH/WIP v2 11/14] file-watcher: support inotify
Date: Fri, 17 Jan 2014 16:47:37 +0700	[thread overview]
Message-ID: <1389952060-12297-12-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1389952060-12297-1-git-send-email-pclouds@gmail.com>

"git diff" on webkit:

        no file watcher  1st run   subsequent runs
real        0m1.361s    0m1.445s      0m0.691s
user        0m0.889s    0m0.940s      0m0.649s
sys         0m0.469s    0m0.495s      0m0.040s

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 config.mak.uname  |   1 +
 file-watcher.c    | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 git-compat-util.h |   3 +
 3 files changed, 198 insertions(+)

diff --git a/config.mak.uname b/config.mak.uname
index 7d31fad..ee548f5 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -33,6 +33,7 @@ ifeq ($(uname_S),Linux)
 	HAVE_PATHS_H = YesPlease
 	LIBC_CONTAINS_LIBINTL = YesPlease
 	HAVE_DEV_TTY = YesPlease
+	BASIC_CFLAGS += -DHAVE_INOTIFY
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	NO_STRLCPY = YesPlease
diff --git a/file-watcher.c b/file-watcher.c
index f334e23..356b58a 100644
--- a/file-watcher.c
+++ b/file-watcher.c
@@ -15,6 +15,166 @@ static char index_signature[41];
 static struct string_list updated = STRING_LIST_INIT_DUP;
 static int updated_sorted;
 
+#ifdef HAVE_INOTIFY
+
+static struct string_list watched_dirs = STRING_LIST_INIT_DUP;
+static int watched_dirs_sorted;
+static int inotify_fd;
+
+struct dir_info {
+	int wd;
+	struct string_list names;
+	int names_sorted;
+};
+
+static void reset_watches(void)
+{
+	int i;
+	for (i = 0; i < watched_dirs.nr; i++) {
+		struct dir_info *dir = watched_dirs.items[i].util;
+		inotify_rm_watch(inotify_fd, dir->wd);
+		string_list_clear(&dir->names, 0);
+	}
+	string_list_clear(&watched_dirs, 1);
+}
+
+static void update(const char *base, const char *name)
+{
+	if (!strcmp(base, "."))
+		string_list_append(&updated, name);
+	else {
+		static struct strbuf sb = STRBUF_INIT;
+		strbuf_addf(&sb, "%s/%s", base, name);
+		string_list_append(&updated, sb.buf);
+		strbuf_reset(&sb);
+	}
+	updated_sorted = 0;
+}
+
+static int do_handle_inotify(const struct inotify_event *event)
+{
+	struct dir_info *dir;
+	struct string_list_item *item;
+	int i;
+
+	if (event->mask & (IN_Q_OVERFLOW | IN_UNMOUNT)) {
+		/*
+		 * The connectionless nature of file watcher means we
+		 * can never tell we are reset in the middle of a
+		 * "session" because there are no "sessions". Close
+		 * the socket so all clients can react on it.
+		 */
+		exit(0);
+	}
+
+	/* Should have indexed them for faster access like trast's watch */
+	for (i = 0; i < watched_dirs.nr; i++) {
+		struct dir_info *dir = watched_dirs.items[i].util;
+		if (dir->wd == event->wd)
+			break;
+	}
+	if (i == watched_dirs.nr)
+		return 0;
+	dir = watched_dirs.items[i].util;
+
+	/*
+	 * If something happened to the watched directory, consider
+	 * everything inside modified
+	 */
+	if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {
+		int dir_idx = i;
+		for (i = 0; i < dir->names.nr; i++)
+			update(watched_dirs.items[dir_idx].string,
+			       dir->names.items[i].string);
+		inotify_rm_watch(inotify_fd, dir->wd);
+		unsorted_string_list_delete_item(&watched_dirs, dir_idx, 1);
+		return 0;
+	}
+
+	if (!dir->names_sorted) {
+		sort_string_list(&dir->names);
+		dir->names_sorted = 1;
+	}
+	item = string_list_lookup(&dir->names, event->name);
+	if (item) {
+		update(watched_dirs.items[i].string, item->string);
+		unsorted_string_list_delete_item(&dir->names,
+						 item - dir->names.items, 0);
+		if (dir->names.nr == 0) {
+			inotify_rm_watch(inotify_fd, dir->wd);
+			unsorted_string_list_delete_item(&watched_dirs, i, 1);
+		}
+	}
+	return 0;
+}
+
+static int handle_inotify(int fd)
+{
+	static char buf[10 * (sizeof(struct inotify_event) + NAME_MAX + 1)];
+	struct inotify_event *event;
+	int offset = 0;
+	int len = read(fd, buf, sizeof(buf));
+	if (len <= 0)
+		return -1;
+	for (event = (struct inotify_event *)(buf + offset);
+	     offset < len;
+	     offset += sizeof(struct inotify_event) + event->len) {
+		if (do_handle_inotify(event))
+			return -1;
+	}
+	return 0;
+}
+
+static int watch_path(char *path)
+{
+	struct string_list_item *item;
+	char *sep = strrchr(path, '/');
+	struct dir_info *dir;
+	const char *dirname = ".";
+
+	if (sep) {
+		*sep = '\0';
+		dirname = path;
+	}
+
+	if (!watched_dirs_sorted) {
+		sort_string_list(&watched_dirs);
+		watched_dirs_sorted = 1;
+	}
+	item = string_list_lookup(&watched_dirs, dirname);
+	if (!item) {
+		/*
+		 * IN_CREATE is not included because we're targetting
+		 * lstat() for index vs worktree. If a file is not
+		 * tracked in index, it's not worth watching. If the
+		 * index has it, but the worktree is already gone
+		 * before watching, the file has already been marked
+		 * modified and should _not_ be watched.
+		 *
+		 * Problematic: IN_DONT_FOLLOW
+		 */
+		int ret = inotify_add_watch(inotify_fd, dirname,
+					    IN_DELETE_SELF | IN_MOVE_SELF |
+					    IN_ATTRIB | IN_DELETE | IN_MODIFY |
+					    IN_MOVED_FROM | IN_MOVED_TO);
+		if (ret < 0)
+			return -1;
+		dir = xmalloc(sizeof(*dir));
+		memset(dir, 0, sizeof(*dir));
+		dir->wd = ret;
+		dir->names.strdup_strings = 1;
+		item = string_list_append(&watched_dirs, dirname);
+		item->util = dir;
+		watched_dirs_sorted = 0;
+	}
+	dir = item->util;
+	string_list_append(&dir->names, sep ? sep + 1 : path);
+	dir->names_sorted = 0;
+	return 0;
+}
+
+#else
+
 static int watch_path(char *path)
 {
 	/*
@@ -26,8 +186,15 @@ static int watch_path(char *path)
 	return -1;
 }
 
+static void reset_watches(void)
+{
+}
+
+#endif
+
 static void reset(void)
 {
+	reset_watches();
 	string_list_clear(&updated, 0);
 	index_signature[0] = '\0';
 }
@@ -180,6 +347,7 @@ int main(int argc, const char **argv)
 	const char *log_string = NULL;
 	struct stat socket_st;
 	struct timeval tv_last_command;
+	struct timeval tv_last_inotify;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet")),
 		OPT_BOOL(0, "daemon", &daemon,
@@ -193,6 +361,15 @@ int main(int argc, const char **argv)
 
 	git_extract_argv0_path(argv[0]);
 	git_setup_gettext();
+
+#ifdef HAVE_INOTIFY
+	inotify_fd = inotify_init();
+	if (inotify_fd < 0)
+		die_errno("unable to initialize inotify");
+#else
+	die("no file watching mechanism is supported");
+#endif
+
 	prefix = setup_git_directory();
 	argc = parse_options(argc, argv, prefix, options,
 			     file_watcher_usage, 0);
@@ -245,8 +422,13 @@ int main(int argc, const char **argv)
 	nr = 0;
 	pfd[nr].fd = fd;
 	pfd[nr++].events = POLLIN;
+#ifdef HAVE_INOTIFY
+	pfd[nr].fd = inotify_fd;
+	pfd[nr++].events = POLLIN;
+#endif
 
 	gettimeofday(&tv_last_command, NULL);
+	gettimeofday(&tv_last_inotify, NULL);
 	for (;;) {
 		int check_exit = check_exit_please;
 		int ret = poll(pfd, nr, check_exit ? 0 : 60 * 1000);
@@ -265,6 +447,18 @@ int main(int argc, const char **argv)
 			gettimeofday(&tv_last_command, NULL);
 		}
 
+#ifdef HAVE_INOTIFY
+		if ((pfd[1].revents & POLLIN)) {
+			struct timeval now;
+			if (handle_inotify(inotify_fd))
+				break;
+			gettimeofday(&now, NULL);
+			if (tv_last_inotify.tv_sec + 60 < now.tv_sec) {
+				check_exit = 1;
+				tv_last_inotify = now;
+			}
+		}
+#endif
 		if (check_exit) {
 			struct stat st;
 			struct timeval now;
diff --git a/git-compat-util.h b/git-compat-util.h
index cbd86c3..de5996a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -128,6 +128,9 @@
 #else
 #include <poll.h>
 #endif
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
 
 #if defined(__MINGW32__)
 /* pull in Windows compatibility stuff */
-- 
1.8.5.1.208.g05b12ea

  parent reply	other threads:[~2014-01-17  9:49 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-01-12 11:03 [PATCH 0/6] inotify support Nguyễn Thái Ngọc Duy
2014-01-12 11:03 ` [PATCH 1/6] read-cache: save trailing sha-1 Nguyễn Thái Ngọc Duy
2014-01-12 11:03 ` [PATCH 2/6] read-cache: new extension to mark what file is watched Nguyễn Thái Ngọc Duy
2014-01-13 17:02   ` Jonathan Nieder
2014-01-14  1:25     ` Duy Nguyen
2014-01-14  1:39   ` Duy Nguyen
2014-01-12 11:03 ` [PATCH 3/6] read-cache: connect to file watcher Nguyễn Thái Ngọc Duy
2014-01-15 10:58   ` Jeff King
2014-01-12 11:03 ` [PATCH 4/6] read-cache: get "updated" path list from " Nguyễn Thái Ngọc Duy
2014-01-12 11:03 ` [PATCH 5/6] read-cache: ask file watcher to watch files Nguyễn Thái Ngọc Duy
2014-01-12 11:03 ` [PATCH 6/6] file-watcher: support inotify Nguyễn Thái Ngọc Duy
2014-01-17  9:47 ` [PATCH/WIP v2 00/14] inotify support Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` [PATCH/WIP v2 01/14] read-cache: save trailing sha-1 Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` [PATCH/WIP v2 02/14] read-cache: new extension to mark what file is watched Nguyễn Thái Ngọc Duy
2014-01-17 11:19     ` Thomas Gummerer
2014-01-19 17:06     ` Thomas Rast
2014-01-20  1:38       ` Duy Nguyen
2014-01-17  9:47   ` [PATCH/WIP v2 03/14] read-cache: connect to file watcher Nguyễn Thái Ngọc Duy
2014-01-17 15:24     ` Torsten Bögershausen
2014-01-17 16:21       ` Duy Nguyen
2014-01-17  9:47   ` [PATCH/WIP v2 04/14] read-cache: ask file watcher to watch files Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` [PATCH/WIP v2 05/14] read-cache: put some limits on file watching Nguyễn Thái Ngọc Duy
2014-01-19 17:06     ` Thomas Rast
2014-01-20  1:36       ` Duy Nguyen
2014-01-17  9:47   ` [PATCH/WIP v2 06/14] read-cache: get modified file list from file watcher Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` [PATCH/WIP v2 07/14] read-cache: add config to start file watcher automatically Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` [PATCH/WIP v2 08/14] read-cache: add GIT_TEST_FORCE_WATCHER for testing Nguyễn Thái Ngọc Duy
2014-01-19 17:04     ` Thomas Rast
2014-01-20  1:32       ` Duy Nguyen
2014-01-17  9:47   ` [PATCH/WIP v2 09/14] file-watcher: add --shutdown and --log options Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` [PATCH/WIP v2 10/14] file-watcher: automatically quit Nguyễn Thái Ngọc Duy
2014-01-17  9:47   ` Nguyễn Thái Ngọc Duy [this message]
2014-01-19 17:04   ` [PATCH/WIP v2 00/14] inotify support Thomas Rast
2014-01-20  1:28     ` Duy Nguyen
2014-01-20 21:51       ` Thomas Rast
2014-01-28 10:46     ` Duy Nguyen
2014-02-03  4:28   ` [PATCH v3 00/26] " Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 01/26] pkt-line.c: rename global variable buffer[] to something less generic Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 02/26] pkt-line.c: add packet_write_timeout() Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 03/26] pkt-line.c: add packet_read_line_timeout() Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 04/26] unix-socket: make unlink() optional in unix_stream_listen() Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 05/26] Add git-file-watcher and basic connection handling logic Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 06/26] file-watcher: check socket directory permission Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 07/26] file-watcher: remove socket on exit Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 08/26] file-watcher: add --detach Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 09/26] read-cache: save trailing sha-1 Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 10/26] read-cache: new flag CE_WATCHED to mark what file is watched Nguyễn Thái Ngọc Duy
2014-02-03  4:28     ` [PATCH v3 11/26] Clear CE_WATCHED when set CE_VALID alone Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 12/26] read-cache: basic hand shaking to the file watcher Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 13/26] read-cache: ask file watcher to watch files Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 14/26] read-cache: put some limits on file watching Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 15/26] read-cache: get changed file list from file watcher Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 16/26] git-compat-util.h: add inotify stubs on non-Linux platforms Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 17/26] file-watcher: inotify support, watching part Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 18/26] file-watcher: inotify support, notification part Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 19/26] Wrap CE_VALID test with ce_valid() Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 20/26] read-cache: new variable to verify file-watcher results Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 21/26] Support running file watcher with the test suite Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 22/26] file-watcher: quit if $WATCHER/socket is gone Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 23/26] file-watcher: tests for the daemon Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 24/26] ls-files: print CE_WATCHED as W (or "w" with CE_VALID) Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 25/26] file-watcher: tests for the client side Nguyễn Thái Ngọc Duy
2014-02-03  4:29     ` [PATCH v3 26/26] Disable file-watcher with system inotify on some tests Nguyễn Thái Ngọc Duy
2014-02-08  8:04     ` [PATCH v3 00/26] inotify support Torsten Bögershausen
2014-02-08  8:53       ` Duy Nguyen
2014-02-09 20:19         ` Torsten Bögershausen
2014-02-10 10:37           ` Duy Nguyen
2014-02-10 16:55             ` Torsten Bögershausen
2014-02-10 23:34               ` Duy Nguyen
2014-02-17 12:36           ` Duy Nguyen
2014-02-19 20:35 ` [PATCH 0/6] " Shawn Pearce
2014-02-19 23:45   ` Duy Nguyen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1389952060-12297-12-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=tr@thomasrast.ch \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.