Subject: [PATCH 2/5] sha1_file: stuff various pack reading variables into a struct

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Tue, 10 Apr 2012 21:39:28 +0700

This requires any threads (including the main one) to call
init_pack_context() first. All pack data is per thread.

Original patch is written by Thomas Rast <[email-masked]>

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 cache.h     |    1 +
 git.c       |    1 +
 sha1_file.c |  251 +++++++++++++++++++++++++++++++++++------------------------
 3 files changed, 152 insertions(+), 101 deletions(-)

diff --git a/cache.h b/cache.h
index 26d14b4..868ef48 100644
--- a/cache.h
+++ b/cache.h
@@ -997,6 +997,7 @@ struct packed_git {
 
 extern struct packed_git *get_packed_git(void);
 extern void set_packed_git(struct packed_git *);
+extern void init_pack_context(void);
 
 struct pack_entry {
 	off_t offset;
diff --git a/git.c b/git.c
index 3805616..7eee270 100644
--- a/git.c
+++ b/git.c
@@ -533,6 +533,7 @@ int main(int argc, const char **argv)
 	const char *cmd;
 
 	startup_info = &git_startup_info;
+	init_pack_context();
 
 	cmd = git_extract_argv0_path(argv[0]);
 	if (!cmd)
diff --git a/sha1_file.c b/sha1_file.c
index 4fd4e2c..170c0b1 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -33,16 +33,79 @@ static inline uintmax_t sz_fmt(size_t s) { return s; }
 
 const unsigned char null_sha1[20];
 
-static struct packed_git *packed_git_;
+#define MAX_DELTA_CACHE (256)
+
+struct delta_base_cache_lru_list {
+	struct delta_base_cache_lru_list *prev;
+	struct delta_base_cache_lru_list *next;
+};
+
+struct delta_base_cache_entry {
+	struct delta_base_cache_lru_list lru;
+	void *data;
+	struct packed_git *p;
+	off_t base_offset;
+	unsigned long size;
+	enum object_type type;
+};
+
+struct pack_context {
+	size_t delta_base_cached;
+	struct delta_base_cache_entry *delta_base_cache;
+	struct delta_base_cache_lru_list *delta_base_cache_lru;
+	struct packed_git *last_found_pack;
+
+	unsigned int pack_used_ctr;
+	unsigned int pack_mmap_calls;
+	unsigned int peak_pack_open_windows;
+	unsigned int pack_open_windows;
+	unsigned int pack_open_fds;
+	unsigned int pack_max_fds;
+	size_t peak_pack_mapped;
+	size_t pack_mapped;
+	struct packed_git *packed_git;
+	int prepare_packed_git_run_once;
+
+	char sha1_file_name_buf[PATH_MAX];
+	char *sha1_pack_name, *sha1_pack_base;
+	char *sha1_pack_index_name, *sha1_pack_index_base;
+};
+
+static pthread_key_t pack_key;
+struct pack_context *get_thread_pack_context(void)
+{
+	return pthread_getspecific(pack_key);
+}
+
 struct packed_git *get_packed_git(void)
 {
-	return packed_git_;
+	return get_thread_pack_context()->packed_git;
 }
 void set_packed_git(struct packed_git *p)
 {
-	packed_git_ = p;
+	get_thread_pack_context()->packed_git = p;
 }
 
+void init_pack_context(void)
+{
+	static int initialized = 0;
+	struct pack_context *ctx = xmalloc(sizeof(struct pack_context));
+
+	if (!initialized) {
+		pthread_key_create(&pack_key, NULL);
+		initialized = 1;
+	}
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->delta_base_cached = 0;
+	ctx->delta_base_cache_lru = xmalloc(sizeof(struct delta_base_cache_lru_list));
+	ctx->delta_base_cache_lru->prev = ctx->delta_base_cache_lru;
+	ctx->delta_base_cache_lru->next = ctx->delta_base_cache_lru;
+	ctx->delta_base_cache = xcalloc(MAX_DELTA_CACHE, sizeof(struct delta_base_cache_entry));
+	ctx->last_found_pack = NULL;
+	ctx->packed_git = NULL;
+	pthread_setspecific(pack_key, ctx);
+}
 
 /*
  * This is meant to hold a *small* number of objects that you would
@@ -65,8 +128,6 @@ static struct cached_object empty_tree = {
 	0
 };
 
-static struct packed_git *last_found_pack;
-
 static struct cached_object *find_cached_object(const unsigned char *sha1)
 {
 	int i;
@@ -177,7 +238,7 @@ static void fill_sha1_path(char *pathbuf, const unsigned char *sha1)
  */
 char *sha1_file_name(const unsigned char *sha1)
 {
-	static char buf[PATH_MAX];
+	char *buf = get_thread_pack_context()->sha1_file_name_buf;
 	const char *objdir;
 	int len;
 
@@ -224,16 +285,14 @@ static char *sha1_get_pack_name(const unsigned char *sha1,
 
 char *sha1_pack_name(const unsigned char *sha1)
 {
-	static char *name, *base;
-
-	return sha1_get_pack_name(sha1, &name, &base, "pack");
+	struct pack_context *ctx = get_thread_pack_context();
+	return sha1_get_pack_name(sha1, &ctx->sha1_pack_name, &ctx->sha1_pack_base, "pack");
 }
 
 char *sha1_pack_index_name(const unsigned char *sha1)
 {
-	static char *name, *base;
-
-	return sha1_get_pack_name(sha1, &name, &base, "idx");
+	struct pack_context *ctx = get_thread_pack_context();
+	return sha1_get_pack_name(sha1, &ctx->sha1_pack_index_name, &ctx->sha1_pack_index_base, "idx");
 }
 
 struct alternate_object_database *alt_odb_list;
@@ -455,18 +514,10 @@ static int has_loose_object(const unsigned char *sha1)
 	       has_loose_object_nonlocal(sha1);
 }
 
-static unsigned int pack_used_ctr;
-static unsigned int pack_mmap_calls;
-static unsigned int peak_pack_open_windows;
-static unsigned int pack_open_windows;
-static unsigned int pack_open_fds;
-static unsigned int pack_max_fds;
-static size_t peak_pack_mapped;
-static size_t pack_mapped;
-struct packed_git *packed_git;
-
-void pack_report(void)
+void pack_report()
 {
+	struct pack_context *ctx = get_thread_pack_context();
+
 	fprintf(stderr,
 		"pack_report: getpagesize()            = %10" SZ_FMT "\n"
 		"pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n"
@@ -480,10 +531,10 @@ void pack_report(void)
 		"pack_report: pack_open_windows        = %10u / %10u\n"
 		"pack_report: pack_mapped              = "
 			"%10" SZ_FMT " / %10" SZ_FMT "\n",
-		pack_used_ctr,
-		pack_mmap_calls,
-		pack_open_windows, peak_pack_open_windows,
-		sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
+		ctx->pack_used_ctr,
+		ctx->pack_mmap_calls,
+		ctx->pack_open_windows, ctx->peak_pack_open_windows,
+		sz_fmt(ctx->pack_mapped), sz_fmt(ctx->peak_pack_mapped));
 }
 
 static int check_packed_git_idx(const char *path,  struct packed_git *p)
@@ -624,6 +675,7 @@ static void scan_windows(struct packed_git *p,
 
 static int unuse_one_window(struct packed_git *current, int keep_fd)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	struct packed_git *p, *lru_p = NULL;
 	struct pack_window *lru_w = NULL, *lru_l = NULL;
 
@@ -633,7 +685,7 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
 		scan_windows(p, &lru_p, &lru_w, &lru_l);
 	if (lru_p) {
 		munmap(lru_w->base, lru_w->len);
-		pack_mapped -= lru_w->len;
+		ctx->pack_mapped -= lru_w->len;
 		if (lru_l)
 			lru_l->next = lru_w->next;
 		else {
@@ -641,12 +693,12 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
 			if (!lru_p->windows && lru_p->pack_fd != -1
 				&& lru_p->pack_fd != keep_fd) {
 				close(lru_p->pack_fd);
-				pack_open_fds--;
+				ctx->pack_open_fds--;
 				lru_p->pack_fd = -1;
 			}
 		}
 		free(lru_w);
-		pack_open_windows--;
+		ctx->pack_open_windows--;
 		return 1;
 	}
 	return 0;
@@ -654,8 +706,9 @@ static int unuse_one_window(struct packed_git *current, int keep_fd)
 
 void release_pack_memory(size_t need, int fd)
 {
-	size_t cur = pack_mapped;
-	while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd))
+	struct pack_context *ctx = get_thread_pack_context();
+	size_t cur = ctx->pack_mapped;
+	while (need >= (cur - ctx->pack_mapped) && unuse_one_window(NULL, fd))
 		; /* nothing */
 }
 
@@ -676,6 +729,7 @@ void *xmmap(void *start, size_t length,
 
 void close_pack_windows(struct packed_git *p)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	while (p->windows) {
 		struct pack_window *w = p->windows;
 
@@ -683,8 +737,8 @@ void close_pack_windows(struct packed_git *p)
 			die("pack '%s' still has open windows to it",
 			    p->pack_name);
 		munmap(w->base, w->len);
-		pack_mapped -= w->len;
-		pack_open_windows--;
+		ctx->pack_mapped -= w->len;
+		ctx->pack_open_windows--;
 		p->windows = w->next;
 		free(w);
 	}
@@ -719,7 +773,8 @@ void close_pack_index(struct packed_git *p)
  */
 void free_pack_by_name(const char *pack_name)
 {
-	struct packed_git *p, **pp = &packed_git_;
+	struct pack_context *ctx = get_thread_pack_context();
+	struct packed_git *p, **pp = &ctx->packed_git;
 
 	while (*pp) {
 		p = *pp;
@@ -728,13 +783,13 @@ void free_pack_by_name(const char *pack_name)
 			close_pack_windows(p);
 			if (p->pack_fd != -1) {
 				close(p->pack_fd);
-				pack_open_fds--;
+				ctx->pack_open_fds--;
 			}
 			close_pack_index(p);
 			free(p->bad_object_sha1);
 			*pp = p->next;
-			if (last_found_pack == p)
-				last_found_pack = NULL;
+			if (ctx->last_found_pack == p)
+				ctx->last_found_pack = NULL;
 			free(p);
 			return;
 		}
@@ -748,6 +803,7 @@ void free_pack_by_name(const char *pack_name)
  */
 static int open_packed_git_1(struct packed_git *p)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	struct stat st;
 	struct pack_header hdr;
 	unsigned char sha1[20];
@@ -757,7 +813,7 @@ static int open_packed_git_1(struct packed_git *p)
 	if (!p->index_data && open_pack_index(p))
 		return error("packfile %s index unavailable", p->pack_name);
 
-	if (!pack_max_fds) {
+	if (!ctx->pack_max_fds) {
 		struct rlimit lim;
 		unsigned int max_fds;
 
@@ -768,18 +824,18 @@ static int open_packed_git_1(struct packed_git *p)
 
 		/* Save 3 for stdin/stdout/stderr, 22 for work */
 		if (25 < max_fds)
-			pack_max_fds = max_fds - 25;
+			ctx->pack_max_fds = max_fds - 25;
 		else
-			pack_max_fds = 1;
+			ctx->pack_max_fds = 1;
 	}
 
-	while (pack_max_fds <= pack_open_fds && unuse_one_window(NULL, -1))
+	while (ctx->pack_max_fds <= ctx->pack_open_fds && unuse_one_window(NULL, -1))
 		; /* nothing */
 
 	p->pack_fd = git_open_noatime(p->pack_name);
 	if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
 		return -1;
-	pack_open_fds++;
+	ctx->pack_open_fds++;
 
 	/* If we created the struct before we had the pack we lack size. */
 	if (!p->pack_size) {
@@ -827,11 +883,12 @@ static int open_packed_git_1(struct packed_git *p)
 
 static int open_packed_git(struct packed_git *p)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	if (!open_packed_git_1(p))
 		return 0;
 	if (p->pack_fd != -1) {
 		close(p->pack_fd);
-		pack_open_fds--;
+		ctx->pack_open_fds--;
 		p->pack_fd = -1;
 	}
 	return -1;
@@ -855,6 +912,7 @@ unsigned char *use_pack(struct packed_git *p,
 		off_t offset,
 		unsigned long *left)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	struct pack_window *win = *w_cursor;
 
 	/* Since packfiles end in a hash of their content and it's
@@ -887,8 +945,8 @@ unsigned char *use_pack(struct packed_git *p,
 			if (len > packed_git_window_size)
 				len = packed_git_window_size;
 			win->len = (size_t)len;
-			pack_mapped += win->len;
-			while (packed_git_limit < pack_mapped
+			ctx->pack_mapped += win->len;
+			while (packed_git_limit < ctx->pack_mapped
 				&& unuse_one_window(p, p->pack_fd))
 				; /* nothing */
 			win->base = xmmap(NULL, win->len,
@@ -901,21 +959,21 @@ unsigned char *use_pack(struct packed_git *p,
 			if (!win->offset && win->len == p->pack_size
 				&& !p->do_not_close) {
 				close(p->pack_fd);
-				pack_open_fds--;
+				ctx->pack_open_fds--;
 				p->pack_fd = -1;
 			}
-			pack_mmap_calls++;
-			pack_open_windows++;
-			if (pack_mapped > peak_pack_mapped)
-				peak_pack_mapped = pack_mapped;
-			if (pack_open_windows > peak_pack_open_windows)
-				peak_pack_open_windows = pack_open_windows;
+			ctx->pack_mmap_calls++;
+			ctx->pack_open_windows++;
+			if (ctx->pack_mapped > ctx->peak_pack_mapped)
+				ctx->peak_pack_mapped = ctx->pack_mapped;
+			if (ctx->pack_open_windows > ctx->peak_pack_open_windows)
+				ctx->peak_pack_open_windows = ctx->pack_open_windows;
 			win->next = p->windows;
 			p->windows = win;
 		}
 	}
 	if (win != *w_cursor) {
-		win->last_used = pack_used_ctr++;
+		win->last_used = ctx->pack_used_ctr++;
 		win->inuse_cnt++;
 		*w_cursor = win;
 	}
@@ -998,8 +1056,9 @@ struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
 
 void install_packed_git(struct packed_git *pack)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	if (pack->pack_fd != -1)
-		pack_open_fds++;
+		ctx->pack_open_fds++;
 
 	pack->next = get_packed_git();
 	set_packed_git(pack);
@@ -1108,12 +1167,12 @@ static void rearrange_packed_git(void)
 	free(ary);
 }
 
-static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
 	struct alternate_object_database *alt;
+	struct pack_context *ctx = get_thread_pack_context();
 
-	if (prepare_packed_git_run_once)
+	if (ctx->prepare_packed_git_run_once)
 		return;
 	prepare_packed_git_one(get_object_directory(), 1);
 	prepare_alt_odb();
@@ -1123,13 +1182,14 @@ void prepare_packed_git(void)
 		alt->name[-1] = '/';
 	}
 	rearrange_packed_git();
-	prepare_packed_git_run_once = 1;
+	ctx->prepare_packed_git_run_once = 1;
 }
 
 void reprepare_packed_git(void)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	discard_revindex();
-	prepare_packed_git_run_once = 0;
+	ctx->prepare_packed_git_run_once = 0;
 	prepare_packed_git();
 }
 
@@ -1664,24 +1724,6 @@ static void *unpack_compressed_entry(struct packed_git *p,
 	return buffer;
 }
 
-#define MAX_DELTA_CACHE (256)
-
-static size_t delta_base_cached;
-
-static struct delta_base_cache_lru_list {
-	struct delta_base_cache_lru_list *prev;
-	struct delta_base_cache_lru_list *next;
-} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
-
-static struct delta_base_cache_entry {
-	struct delta_base_cache_lru_list lru;
-	void *data;
-	struct packed_git *p;
-	off_t base_offset;
-	unsigned long size;
-	enum object_type type;
-} delta_base_cache[MAX_DELTA_CACHE];
-
 static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
 {
 	unsigned long hash;
@@ -1694,7 +1736,8 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
 static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
 {
 	unsigned long hash = pack_entry_hash(p, base_offset);
-	struct delta_base_cache_entry *ent = delta_base_cache + hash;
+	struct delta_base_cache_entry *ent
+		= get_thread_pack_context()->delta_base_cache + hash;
 	return (ent->data && ent->p == p && ent->base_offset == base_offset);
 }
 
@@ -1703,7 +1746,8 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
 {
 	void *ret;
 	unsigned long hash = pack_entry_hash(p, base_offset);
-	struct delta_base_cache_entry *ent = delta_base_cache + hash;
+	struct pack_context *ctx = get_thread_pack_context();
+	struct delta_base_cache_entry *ent = ctx->delta_base_cache + hash;
 
 	ret = ent->data;
 	if (!ret || ent->p != p || ent->base_offset != base_offset)
@@ -1713,7 +1757,7 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
 		ent->data = NULL;
 		ent->lru.next->prev = ent->lru.prev;
 		ent->lru.prev->next = ent->lru.next;
-		delta_base_cached -= ent->size;
+		ctx->delta_base_cached -= ent->size;
 	} else {
 		ret = xmemdupz(ent->data, ent->size);
 	}
@@ -1722,48 +1766,52 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
 	return ret;
 }
 
-static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+static inline void release_delta_base_cache(struct pack_context *ctx,
+					    struct delta_base_cache_entry *ent)
 {
 	if (ent->data) {
 		free(ent->data);
 		ent->data = NULL;
 		ent->lru.next->prev = ent->lru.prev;
 		ent->lru.prev->next = ent->lru.next;
-		delta_base_cached -= ent->size;
+		ctx->delta_base_cached -= ent->size;
 	}
 }
 
 void clear_delta_base_cache(void)
 {
 	unsigned long p;
+	struct pack_context *ctx = get_thread_pack_context();
+	struct delta_base_cache_entry *delta_base_cache = ctx->delta_base_cache;
 	for (p = 0; p < MAX_DELTA_CACHE; p++)
-		release_delta_base_cache(&delta_base_cache[p]);
+		release_delta_base_cache(ctx, &delta_base_cache[p]);
 }
 
 static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
 	void *base, unsigned long base_size, enum object_type type)
 {
 	unsigned long hash = pack_entry_hash(p, base_offset);
-	struct delta_base_cache_entry *ent = delta_base_cache + hash;
+	struct pack_context *ctx = get_thread_pack_context();
+	struct delta_base_cache_entry *ent = ctx->delta_base_cache + hash;
 	struct delta_base_cache_lru_list *lru;
 
-	release_delta_base_cache(ent);
-	delta_base_cached += base_size;
+	release_delta_base_cache(ctx, ent);
+	ctx->delta_base_cached += base_size;
 
-	for (lru = delta_base_cache_lru.next;
-	     delta_base_cached > delta_base_cache_limit
-	     && lru != &delta_base_cache_lru;
+	for (lru = ctx->delta_base_cache_lru->next;
+	     ctx->delta_base_cached > delta_base_cache_limit
+	     && lru != ctx->delta_base_cache_lru;
 	     lru = lru->next) {
 		struct delta_base_cache_entry *f = (void *)lru;
 		if (f->type == OBJ_BLOB)
-			release_delta_base_cache(f);
+			release_delta_base_cache(ctx, f);
 	}
-	for (lru = delta_base_cache_lru.next;
-	     delta_base_cached > delta_base_cache_limit
-	     && lru != &delta_base_cache_lru;
+	for (lru = ctx->delta_base_cache_lru->next;
+	     ctx->delta_base_cached > delta_base_cache_limit
+	     && lru != ctx->delta_base_cache_lru;
 	     lru = lru->next) {
 		struct delta_base_cache_entry *f = (void *)lru;
-		release_delta_base_cache(f);
+		release_delta_base_cache(ctx, f);
 	}
 
 	ent->p = p;
@@ -1771,10 +1819,10 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
 	ent->type = type;
 	ent->data = base;
 	ent->size = base_size;
-	ent->lru.next = &delta_base_cache_lru;
-	ent->lru.prev = delta_base_cache_lru.prev;
-	delta_base_cache_lru.prev->next = &ent->lru;
-	delta_base_cache_lru.prev = &ent->lru;
+	ent->lru.next = ctx->delta_base_cache_lru;
+	ent->lru.prev = ctx->delta_base_cache_lru->prev;
+	ctx->delta_base_cache_lru->prev->next = &ent->lru;
+	ctx->delta_base_cache_lru->prev = &ent->lru;
 }
 
 static void *read_object(const unsigned char *sha1, enum object_type *type,
@@ -2066,20 +2114,21 @@ static int fill_pack_entry(const unsigned char *sha1,
 
 static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
+	struct pack_context *ctx = get_thread_pack_context();
 	struct packed_git *p;
 
 	prepare_packed_git();
 	if (!get_packed_git())
 		return 0;
 
-	if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
+	if (ctx->last_found_pack && fill_pack_entry(sha1, e, ctx->last_found_pack))
 		return 1;
 
 	for (p = get_packed_git(); p; p = p->next) {
-		if (p == last_found_pack || !fill_pack_entry(sha1, e, p))
+		if (p == ctx->last_found_pack || !fill_pack_entry(sha1, e, p))
 			continue;
 
-		last_found_pack = p;
+		ctx->last_found_pack = p;
 		return 1;
 	}
 	return 0;
-- 
1.7.8.36.g69ee2