Commit 7bb0daa1028563181fe482524973f2a801da9261

Stefan Sperling 2018-06-22T01:53:06

add a very simple object cache

diff --git a/lib/got_lib_object.h b/lib/got_lib_object.h
index 37a0ffc..ed4dac1 100644
--- a/lib/got_lib_object.h
+++ b/lib/got_lib_object.h
@@ -31,6 +31,7 @@ struct got_object {
 	char *path_packfile;	/* if packed */
 	off_t pack_offset;	/* if packed */
 	struct got_delta_chain deltas; /* if deltified */
+	int refcnt;		/* > 0 if open and/or cached */
 };
 
 struct got_blob_object {
diff --git a/lib/got_lib_repository.h b/lib/got_lib_repository.h
index 3a9c223..b6db6c4 100644
--- a/lib/got_lib_repository.h
+++ b/lib/got_lib_repository.h
@@ -17,6 +17,14 @@
 #define GOT_PACKIDX_CACHE_SIZE	64
 #define GOT_PACK_CACHE_SIZE	GOT_PACKIDX_CACHE_SIZE
 
+#define GOT_OBJECT_CACHE_SIZE	8192
+
+struct got_objcache_entry {
+	SIMPLEQ_ENTRY(got_objcache_entry) entry;
+	struct got_object_id id;
+	struct got_object *obj;
+};
+
 struct got_repository {
 	char *path;
 	char *path_git_dir;
@@ -26,4 +34,15 @@ struct got_repository {
 
 	/* Open file handles for pack files. */
 	struct got_pack packs[GOT_PACK_CACHE_SIZE];
+
+	SIMPLEQ_HEAD(, got_objcache_entry) objcache;
+	int ncached;
+	int cache_hit;
+	int cache_miss;
 };
+
+const struct got_error*
+got_repo_cache_object(struct got_repository *, struct got_object_id *,
+    struct got_object *);
+struct got_object *got_repo_get_cached_object(struct got_repository *,
+    struct got_object_id *);
diff --git a/lib/object.c b/lib/object.c
index 92f261b..7fb9519 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -46,6 +46,7 @@
 #include "got_lib_zbuf.h"
 #include "got_lib_object.h"
 #include "got_lib_privsep.h"
+#include "got_lib_repository.h"
 
 #ifndef MIN
 #define	MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
@@ -365,6 +366,12 @@ got_object_open(struct got_object **obj, struct got_repository *repo,
 	char *path;
 	int fd;
 
+	*obj = got_repo_get_cached_object(repo, id);
+	if (*obj != NULL) {
+		(*obj)->refcnt++;
+		return NULL;
+	}
+
 	err = object_path(&path, id, repo);
 	if (err)
 		return err;
@@ -386,6 +393,11 @@ got_object_open(struct got_object **obj, struct got_repository *repo,
 			goto done;
 		memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
 	}
+
+	if (err == NULL) {
+		(*obj)->refcnt++;
+		err = got_repo_cache_object(repo, id, *obj);
+	}
 done:
 	free(path);
 	if (fd != -1)
@@ -409,6 +421,11 @@ got_object_open_by_id_str(struct got_object **obj, struct got_repository *repo,
 void
 got_object_close(struct got_object *obj)
 {
+	if (obj->refcnt > 0) {
+		obj->refcnt--;
+		return;
+	}
+
 	if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
 		struct got_delta *delta;
 		while (!SIMPLEQ_EMPTY(&obj->deltas.entries)) {
@@ -1508,9 +1525,11 @@ got_object_open_by_path(struct got_object **obj, struct got_repository *repo,
 		}
 	}
 
-	if (te)
+	if (te) {
 		err = got_object_open(obj, repo, te->id);
-	else
+		if (err)
+			goto done;
+	} else
 		err = got_error(GOT_ERR_NO_OBJ);
 done:
 	free(s0);
diff --git a/lib/repository.c b/lib/repository.c
index fa438db..bc4db37 100644
--- a/lib/repository.c
+++ b/lib/repository.c
@@ -28,6 +28,7 @@
 #include "got_reference.h"
 #include "got_repository.h"
 #include "got_worktree.h"
+#include "got_object.h"
 
 #include "got_lib_path.h"
 #include "got_lib_delta.h"
@@ -147,6 +148,47 @@ done:
 
 }
 
+const struct got_error*
+got_repo_cache_object(struct got_repository *repo, struct got_object_id *id,
+    struct got_object *obj)
+{
+	struct got_objcache_entry *ce;
+
+	if (repo->ncached >= GOT_OBJECT_CACHE_SIZE) {
+		ce = SIMPLEQ_FIRST(&repo->objcache);
+		SIMPLEQ_REMOVE_HEAD(&repo->objcache, entry);
+		got_object_close(ce->obj);
+		free(ce);
+		repo->ncached--;
+	}
+
+	ce = calloc(1, sizeof(*ce));
+	if (ce == NULL)
+		return got_error_from_errno();
+	memcpy(&ce->id, id, sizeof(ce->id));
+	ce->obj = obj;
+	obj->refcnt++;
+	SIMPLEQ_INSERT_HEAD(&repo->objcache, ce, entry);
+	repo->ncached++;
+	return NULL;
+}
+
+struct got_object *
+got_repo_get_cached_object(struct got_repository *repo,
+    struct got_object_id *id)
+{
+	struct got_objcache_entry *ce;
+
+	SIMPLEQ_FOREACH(ce, &repo->objcache, entry) {
+		if (got_object_id_cmp(&ce->id, id) != 0)
+			continue;
+		repo->cache_hit++;
+		return ce->obj;
+	}
+	repo->cache_miss++;
+	return NULL;
+}
+
 const struct got_error *
 got_repo_open(struct got_repository **ret, const char *path)
 {
@@ -238,6 +280,15 @@ got_repo_close(struct got_repository *repo)
 		got_pack_close(&repo->packs[i]);
 	}
 
+	while (!SIMPLEQ_EMPTY(&repo->objcache)) {
+		struct got_objcache_entry *ce;
+		ce = SIMPLEQ_FIRST(&repo->objcache);
+		SIMPLEQ_REMOVE_HEAD(&repo->objcache, entry);
+		got_object_close(ce->obj);
+		free(ce);
+	}
+	fprintf(stderr, "repo cache size: %d hit: %d miss: %d\n", repo->ncached, repo->cache_hit, repo->cache_miss);
+
 	free(repo->path);
 	free(repo->path_git_dir);
 	free(repo);