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

[PATCH 17/19] index-helper: process management

From
David Turner <dturner@twopensource.com>
Date
Mar 9, 2016, 18:36 UTC
Message-ID
<1457548582-28302-18-git-send-email-dturner@twopensource.com>
In-Reply-To
<1457548582-28302-1-git-send-email-dturner@twopensource.com>
Don't start index-helper when it is already running for a repository.

Keep the index-helper pid file's mtime up-to-date. Before starting index-helper, check that (a) the pid file's mtime is recent and (b) there is some process with that pid. Of course, it's possible that the mtime is recent but the index-helper has died anyway (and some other process has taken its pid), but that's pretty unlikely.

Provide index-helper --kill (to kill the running index-helper) and index-helper --ignore-existing (to start up a new index-helper even if we believe that there's one already running).

Add a test for index-helper's pid-file writing and for --kill.
Signed-off-by: David Turner <dturner@twopensource.com>
---
 index-helper.c          | 95 +++++++++++++++++++++++++++++++++++++++++++++----
 t/t7900-index-helper.sh | 23 ++++++++++++
 2 files changed, 111 insertions(+), 7 deletions(-)
 create mode 100755 t/t7900-index-helper.sh
diff --git a/index-helper.c b/index-helper.c
index dc03e1e..a75da60 100644
--- a/index-helper.c
+++ b/index-helper.c
@@ -152,6 +152,17 @@ static void refresh(int sig)
 
 #ifdef HAVE_SHM
 
+static void touch_pid_file(void)
+{
+	/*
+	 * If we fail to update the times on index-helper.pid, we've
+	 * probably had the repo deleted out from under us or otherwise
+	 * lost access; might as well give up.
+	 */
+	if (utimes(git_path("index-helper.pid"), NULL))
+		exit(0);
+}
+
 #ifdef USE_WATCHMAN
 static void share_watchman(struct index_state *istate,
 			   struct shm *is, pid_t pid)
@@ -211,9 +222,10 @@ static void prepare_index(int sig, siginfo_t *si, void *context)
 
 #endif
 
-static void loop(const char *pid_file, int idle_in_seconds)
+static void loop(const char *pid_file, int idle_in_minutes)
 {
 	struct sigaction sa;
+	int timeout_count = 0;
 
 	sigchain_pop(SIGHUP);	/* pushed by sigchain_push_common */
 	sigchain_push(SIGHUP, refresh);
@@ -225,19 +237,25 @@ static void loop(const char *pid_file, int idle_in_seconds)
 	sigaction(SIGUSR1, &sa, NULL);
 
 	refresh(0);
-	while (sleep(idle_in_seconds))
-		; /* do nothing, all is handled by signal handlers already */
+	while (timeout_count < idle_in_minutes) {
+		if (sleep(60) == EINTR)
+			timeout_count = 0;
+		else
+			timeout_count ++;
+		touch_pid_file();
+	}
 }
 
 #elif defined(GIT_WINDOWS_NATIVE)
 
-static void loop(const char *pid_file, int idle_in_seconds)
+static void loop(const char *pid_file, int idle_in_minutes)
 {
 	HWND hwnd;
 	UINT_PTR timer = 0;
 	MSG msg;
 	HINSTANCE hinst = GetModuleHandle(NULL);
 	WNDCLASS wc;
+	int timeout_count = 0;
 
 	/*
 	 * Emulate UNIX signals by sending WM_USER+x to a
@@ -258,11 +276,18 @@ static void loop(const char *pid_file, int idle_in_seconds)
 
 	refresh(0);
 	while (1) {
-		timer = SetTimer(hwnd, timer, idle_in_seconds * 1000, NULL);
+		timer = SetTimer(hwnd, timer, 60 * 1000, NULL);
 		if (!timer)
 			die(_("no timer!"));
-		if (!GetMessage(&msg, hwnd, 0, 0) || msg.message == WM_TIMER)
+		if (!GetMessage(&msg, hwnd, 0, 0))
 			break;
+		if (msg.message == WM_TIMER) {
+			timeout_count ++;
+			if (timeout_count == idle_in_minutes)
+				break;
+		}
+		timeout_count = 0;
+		touch_pid_file();
 		switch (msg.message) {
 		case WM_USER:
 			refresh(0);
@@ -288,6 +313,44 @@ static const char * const usage_text[] = {
 	NULL
 };
 
+static int already_running(void)
+{
+	char contents[20] = {0};
+	char *end;
+	int fd, dead, i = 0;
+	time_t now;
+	pid_t pid;
+	struct stat st;
+
+	fd = open(git_path("index-helper.pid"), O_RDONLY);
+	if (fd < 0)
+		return 0;
+
+	if (fstat(fd, &st) < 0)
+		return 0;
+
+	now = time(&now);
+
+	/*
+	 * We touch the pid file every minute, so if a pid file hasn't
+	 * been updated in two minutes, it's out-of-date.
+	 */
+	if (st.st_mtime + 120 < now)
+		return 0;
+
+	read_in_full(fd, &contents, sizeof(contents));
+
+	while (!isdigit(contents[i]) && contents[i])
+		i++;
+	pid = strtoll(contents + i, &end, 10);
+	if (contents == end)
+		return 0;
+	dead = kill(pid, 0);
+	if (!dead)
+		return pid;
+	return 0;
+}
+
 static const char *write_pid(void)
 {
 	static struct strbuf sb = STRBUF_INIT;
@@ -314,6 +377,8 @@ int main(int argc, char **argv)
 {
 	const char *prefix;
 	int idle_in_minutes = 10, detach = 0;
+	int ignore_existing = 0;
+	int kill_existing = 0;
 	const char *pid_file;
 	struct option options[] = {
 		OPT_INTEGER(0, "exit-after", &idle_in_minutes,
@@ -321,6 +386,8 @@ int main(int argc, char **argv)
 		OPT_BOOL(0, "strict", &to_verify,
 			 "verify shared memory after creating"),
 		OPT_BOOL(0, "detach", &detach, "detach the process"),
+		OPT_BOOL(0, "ignore-existing", &ignore_existing, "run even if another index-helper seems to be running for this repo"),
+		OPT_BOOL(0, "kill", &kill_existing, "kill any running index-helper for this repo"),
 		OPT_END()
 	};
 
@@ -335,6 +402,20 @@ int main(int argc, char **argv)
 			  options, usage_text, 0))
 		die(_("too many arguments"));
 
+	if (ignore_existing && kill_existing)
+		die(_("--ignore-existing and --kill don't make sense together"));
+
+	if (!ignore_existing) {
+		pid_t pid = already_running();
+		if (pid) {
+			if (kill_existing)
+				exit(kill(pid, SIGKILL));
+			else if (!detach)
+				warning("index-helper is apparently already running with pid %d", (int)pid);
+			exit(0);
+		}
+	}
+
 	write_pid();
 
 	atexit(cleanup);
@@ -351,6 +432,6 @@ int main(int argc, char **argv)
 
 	if (!idle_in_minutes)
 		idle_in_minutes = 0xffffffff / 60;
-	loop(pid_file, idle_in_minutes * 60);
+	loop(pid_file, idle_in_minutes);
 	return 0;
 }
diff --git a/t/t7900-index-helper.sh b/t/t7900-index-helper.sh
new file mode 100755
index 0000000..6708180
--- /dev/null
+++ b/t/t7900-index-helper.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Copyright (c) 2016, Twitter, Inc
+#
+
+test_description='git-index-helper
+
+Testing git index-helper
+'
+
+. ./test-lib.sh
+
+test_expect_success 'index-helper creates usable pid file and can be killed' '
+	test_path_is_missing .git/index-helper.pid &&
+	git index-helper --detach &&
+	test_path_is_file .git/index-helper.pid &&
+	pid=$(sed s/[^0-9]//g .git/index-helper.pid) &&
+	kill -0 $pid &&
+	git index-helper --kill &&
+	! kill -0 $pid
+'
+
+test_done
-- 
2.4.2.767.g62658d5-twtrsrc
Previous: David TurnerNext: David Turner
Message 36 of 60 in “index-helper, watchman”
  1. 00/19 index-helper, watchmanDavid Turner, Mar 9, 2016
  2. 01/19 trace.c: add GIT_TRACE_PACK_STATS for pack usage statisticsDavid Turner, Mar 9, 2016
  3. Junio C HamanoMar 9, 2016
  4. David TurnerMar 10, 2016
  5. Duy NguyenMar 10, 2016
  6. 02/19 read-cache.c: fix constness of verify_hdr()David Turner, Mar 9, 2016
  7. 03/19 read-cache: allow to keep mmap'd memory after readingDavid Turner, Mar 9, 2016
  8. Junio C HamanoMar 9, 2016
  9. David TurnerMar 10, 2016
  10. 04/19 index-helper: new daemon for caching index and related stuffDavid Turner, Mar 9, 2016
  11. Junio C HamanoMar 9, 2016
  12. Junio C HamanoMar 9, 2016
  13. David TurnerMar 10, 2016
  14. Duy NguyenMar 10, 2016
  15. David TurnerMar 10, 2016
  16. Duy NguyenMar 11, 2016
  17. David TurnerMar 10, 2016
  18. Duy NguyenMar 15, 2016
  19. Junio C HamanoMar 15, 2016
  20. Duy NguyenMar 15, 2016
  21. 05/19 trace.c: add GIT_TRACE_INDEX_STATS for index statisticsDavid Turner, Mar 9, 2016
  22. 06/19 index-helper: add --strictDavid Turner, Mar 9, 2016
  23. 07/19 daemonize(): set a flag before exiting the main processDavid Turner, Mar 9, 2016
  24. 08/19 index-helper: add --detachDavid Turner, Mar 9, 2016
  25. 09/19 index-helper: add Windows supportDavid Turner, Mar 9, 2016
  26. Duy NguyenMar 16, 2016
  27. Johannes SchindelinMar 17, 2016
  28. Duy NguyenMar 17, 2016
  29. 10/19 read-cache: add watchman 'WAMA' extensionDavid Turner, Mar 9, 2016
  30. 11/19 Add watchman support to reduce index refresh costDavid Turner, Mar 9, 2016
  31. 12/19 read-cache: allow index-helper to prepare shm before git reads itDavid Turner, Mar 9, 2016
  32. 13/19 index-helper: use watchman to avoid refreshing index with lstat()David Turner, Mar 9, 2016
  33. 14/19 update-index: enable/disable watchman supportDavid Turner, Mar 9, 2016
  34. 15/19 unpack-trees: preserve index extensionsDavid Turner, Mar 9, 2016
  35. 16/19 index-helper: rewrite pidfile after daemonizingDavid Turner, Mar 9, 2016
  36. 17/19 index-helper: process managementDavid Turner, Mar 9, 2016
  37. 18/19 index-helper: autorunDavid Turner, Mar 9, 2016
  38. Duy NguyenMar 15, 2016
  39. Johannes SchindelinMar 15, 2016
  40. Duy NguyenMar 16, 2016
  41. David TurnerMar 16, 2016
  42. Johannes SchindelinMar 16, 2016
  43. Duy NguyenMar 17, 2016
  44. Johannes SchindelinMar 17, 2016
  45. David TurnerMar 17, 2016
  46. Duy NguyenMar 18, 2016
  47. Johannes SchindelinMar 18, 2016
  48. Duy NguyenMar 18, 2016
  49. David TurnerMar 18, 2016
  50. Duy NguyenMar 18, 2016
  51. Johannes SchindelinMar 18, 2016
  52. Duy NguyenMar 18, 2016
  53. Johannes SchindelinMar 18, 2016
  54. 19/19 hack: watchman/untracked cache mashupDavid Turner, Mar 9, 2016
  55. Duy NguyenMar 15, 2016
  56. David TurnerMar 17, 2016
  57. Duy NguyenMar 17, 2016
  58. David TurnerMar 17, 2016
  59. Torsten BögershausenMar 29, 2016
  60. David TurnerMar 29, 2016

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.