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

[PATCH 3/5] index-helper: add Windows support

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Jun 10, 2014, 13:24 UTC
Message-ID
<1402406665-27988-4-git-send-email-pclouds@gmail.com>
In-Reply-To
<1402406665-27988-1-git-send-email-pclouds@gmail.com>

Windows supports shared memory, but a bit different that POSIX shm. The most noticeable thing is there's no way to get the shared memory's size from the reader. So the size is added near the end in the shared memory, hidden away from shm users (storing it in headers would cause more problems with munmap, storing it as a separate shm is even worse).

PostMessage is used instead of UNIX signals for notification. Lightweight (at least code-wise) on the client side.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-index-helper.txt |  2 +-
 config.mak.uname                   |  2 +
 index-helper.c                     | 49 +++++++++++++++++++
 read-cache.c                       |  6 +++
 shm.c                              | 96 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 154 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-index-helper.txt b/Documentation/git-index-helper.txt
index d0b1365..afeb334 100644
--- a/Documentation/git-index-helper.txt
+++ b/Documentation/git-index-helper.txt
@@ -14,7 +14,7 @@ DESCRIPTION
 -----------
 Keep the index file in memory for faster access. This daemon is per
 repository. This daemon is only available on POSIX system with
-shared memory support (e.g. Linux)
+shared memory support (e.g. Linux or Windows)
 
 OPTIONS
 -------
diff --git a/config.mak.uname b/config.mak.uname
index 8de61a4..17d35e3 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -376,6 +376,7 @@ ifndef DEBUG
 else
 	BASIC_CFLAGS += -Zi -MTd
 endif
+	PROGRAM_OBJS += index-helper.o
 	X = .exe
 endif
 ifeq ($(uname_S),Interix)
@@ -529,6 +530,7 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
 else
 	NO_CURL = YesPlease
 endif
+	PROGRAM_OBJS += index-helper.o
 endif
 ifeq ($(uname_S),QNX)
 	COMPAT_CFLAGS += -DSA_RESTART=0
diff --git a/index-helper.c b/index-helper.c
index e10d0c3..f92326a 100644
--- a/index-helper.c
+++ b/index-helper.c
@@ -95,6 +95,52 @@ static void loop(const char *pid_file, int idle_in_seconds)
 		utime(git_path("read-cache--daemon.pid"), NULL);
 }
 
+#elif defined(GIT_WINDOWS_NATIVE)
+
+static void loop(const char *pid_file, int idle_in_seconds)
+{
+	HWND hwnd;
+	UINT_PTR timer = 0;
+	MSG msg;
+	HINSTANCE hinst = GetModuleHandle(NULL);
+	WNDCLASS wc;
+
+	/*
+	 * Emulate UNIX signals by sending WM_USER+x to a
+	 * window. Register window class and create a new window to
+	 * catch these messages.
+	 */
+	memset(&wc, 0, sizeof(wc));
+	wc.lpfnWndProc	 = DefWindowProc;
+	wc.hInstance	 = hinst;
+	wc.lpszClassName = "git-read-cache--daemon";
+	if (!RegisterClass(&wc))
+		die_errno("could not register new window class");
+
+	hwnd = CreateWindow("git-read-cache--daemon", pid_file,
+			    0, 0, 0, 1, 1, NULL, NULL, hinst, NULL);
+	if (!hwnd)
+		die_errno("could not register new window");
+
+	refresh(0);
+	while (1) {
+		timer = SetTimer(hwnd, timer, idle_in_seconds * 1000, NULL);
+		if (!timer)
+			die("no timer!");
+		if (!GetMessage(&msg, hwnd, 0, 0) || msg.message == WM_TIMER)
+			break;
+		switch (msg.message) {
+		case WM_USER:
+			refresh(0);
+			break;
+		default:
+			/* just reset the timer */
+			break;
+		}
+		utime(git_path("read-cache--daemon.pid"), NULL);
+	}
+}
+
 #else
 
 static void loop(const char *pid_file, int idle_in_seconds)
@@ -129,6 +175,9 @@ int main(int argc, char **argv)
 				       git_path("read-cache--daemon.pid"),
 				       LOCK_DIE_ON_ERROR);
 	strbuf_addf(&sb,
+#ifdef GIT_WINDOWS_NATIVE
+		    "HWND"
+#endif
 		    "%" PRIuMAX, (uintmax_t) getpid());
 	write_in_full(fd, sb.buf, sb.len);
 	commit_lock_file(&lock);
diff --git a/read-cache.c b/read-cache.c
index f9df984..8001c29 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1483,6 +1483,12 @@ static void poke_daemon(struct stat *st, int refresh_cache)
 		pid_t pid = strtoul(sb.buf, &end, 10);
 		if (end && !*end)
 			kill(pid, refresh_cache ? SIGHUP : SIGUSR1);
+#elif defined(GIT_WINDOWS_NATIVE)
+		if (starts_with(sb.buf, "HWND")) {
+			HWND hwnd = FindWindow("git-read-cache--daemon", sb.buf);
+			PostMessage(hwnd, refresh_cache ? WM_USER : WM_USER + 1,
+				    0, 0);
+		}
 #endif
 	}
 	close(fd);
diff --git a/shm.c b/shm.c
index 4ec1a00..04d8a35 100644
--- a/shm.c
+++ b/shm.c
@@ -52,6 +52,102 @@ void git_shm_unlink(const char *fmt, ...)
 	shm_unlink(path);
 }
 
+#elif defined(GIT_WINDOWS_NATIVE)
+
+#define SHM_PATH_LEN 82	/* a little bit longer than POSIX because of "Local\\" */
+
+static ssize_t create_shm_map(int oflag, int perm, ssize_t length,
+			      void **mmap, int prot, int flags,
+			      const char *path, unsigned long page_size)
+{
+	size_t real_length;
+	void *last_page;
+	HANDLE h;
+
+	assert(perm   == 0700);
+	assert(oflag  == (O_CREAT | O_EXCL | O_RDWR));
+	assert(prot   == (PROT_READ | PROT_WRITE));
+	assert(flags  == MAP_SHARED);
+	assert(length >= 0);
+
+	real_length = length;
+	if (real_length % page_size)
+		real_length += page_size - (real_length % page_size);
+	real_length += page_size;
+	h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+			      real_length, path);
+	if (!h)
+		return -1;
+	*mmap = MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, real_length);
+	CloseHandle(h);
+	if (!*mmap)
+		return -1;
+	last_page = (unsigned char *)*mmap + real_length - page_size;
+	*(unsigned long *)last_page = length;
+	return length;
+}
+
+static ssize_t open_shm_map(int oflag, int perm, ssize_t length, void **mmap,
+			    int prot, int flags, const char *path,
+			    unsigned long page_size)
+{
+	void *last_page;
+	HANDLE h;
+
+	assert(perm   == 0700);
+	assert(oflag  == O_RDONLY);
+	assert(prot   == PROT_READ);
+	assert(flags  == MAP_SHARED);
+	assert(length <= 0);
+
+	h = OpenFileMapping(FILE_MAP_READ, FALSE, path);
+	if (!h)
+		return -1;
+	*mmap = MapViewOfFile(h, FILE_MAP_READ, 0, 0, 0);
+	CloseHandle(h);
+	if (!*mmap)
+		return -1;
+	if (length < 0) {
+		MEMORY_BASIC_INFORMATION mbi;
+		if (!VirtualQuery(*mmap, &mbi, sizeof(mbi))) {
+			UnmapViewOfFile(*mmap);
+			return -1;
+		}
+		if (mbi.RegionSize % page_size)
+			die("expected size %lu to be %lu aligned",
+				    mbi.RegionSize, page_size);
+		last_page = (unsigned char *)*mmap + mbi.RegionSize - page_size;
+		length = *(unsigned long *)last_page;
+	}
+	return length;
+}
+
+ssize_t git_shm_map(int oflag, int perm, ssize_t length, void **mmap,
+		    int prot, int flags, const char *fmt, ...)
+{
+	SYSTEM_INFO si;
+	va_list ap;
+	char path[SHM_PATH_LEN];
+
+	GetSystemInfo(&si);
+
+	strcpy(path, "Local\\");
+	va_start(ap, fmt);
+	vsprintf(path + strlen(path), fmt, ap);
+	va_end(ap);
+
+	if (oflag & O_CREAT)
+		return create_shm_map(oflag, perm, length, mmap, prot,
+				      flags, path, si.dwPageSize);
+	else
+		return open_shm_map(oflag, perm, length, mmap, prot,
+				    flags, path, si.dwPageSize);
+}
+
+void git_shm_unlink(const char *fmt, ...)
+{
+}
+
 #else
 
 ssize_t git_shm_map(int oflag, int perm, ssize_t length, void **mmap,
-- 
1.9.1.346.ga2b5940
Previous: David TurnerNext: Nguyễn Thái Ngọc Duy
Message 5 of 7 in “Speed up cache loading time”
  1. 0/5 Speed up cache loading timeNguyễn Thái Ngọc Duy, Jun 10, 2014
  2. 1/5 read-cache: allow to keep mmap'd memory after readingNguyễn Thái Ngọc Duy, Jun 10, 2014
  3. 2/5 index-helper: new daemon for caching index and related stuffNguyễn Thái Ngọc Duy, Jun 10, 2014
  4. David TurnerJun 10, 2014
  5. 3/5 index-helper: add Windows supportNguyễn Thái Ngọc Duy, Jun 10, 2014
  6. 4/5 daemonize(): set a flag before exiting the main processNguyễn Thái Ngọc Duy, Jun 10, 2014
  7. 5/5 index-helper: add --detachNguyễn Thái Ngọc Duy, Jun 10, 2014

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.