Commit f6be5c308af39aaf5dd86f08ed270f009ba12ed8

Stefan Sperling 2018-06-22T10:33:04

add a tree object cache to struct got_repository

diff --git a/include/got_object.h b/include/got_object.h
index 9268ff8..32c6146 100644
--- a/include/got_object.h
+++ b/include/got_object.h
@@ -28,6 +28,8 @@ struct got_tree_entry {
 struct got_tree_object {
 	int nentries;
 	SIMPLEQ_HEAD(, got_tree_entry) entries;
+
+	int refcnt; /* for internal use only */
 };
 
 struct got_object_qid {
diff --git a/lib/got_lib_repository.h b/lib/got_lib_repository.h
index db28762..4eb219f 100644
--- a/lib/got_lib_repository.h
+++ b/lib/got_lib_repository.h
@@ -19,12 +19,21 @@
 
 #define GOT_OBJECT_CACHE_SIZE	8192
 
+enum got_object_chache_type {
+	GOT_OBJECT_CACHE_TYPE_OBJ,
+	GOT_OBJECT_CACHE_TYPE_TREE
+};
+
 struct got_object_cache_entry {
 	struct got_object_id id;
-	struct got_object *obj;
+	union {
+		struct got_object *obj;
+		struct got_tree_object *tree;
+	} data;
 };
 
 struct got_object_cache {
+	enum got_object_chache_type type;
 	struct got_object_idset *set;
 	int ncached;
 	int cache_hit;
@@ -41,11 +50,16 @@ struct got_repository {
 	/* Open file handles for pack files. */
 	struct got_pack packs[GOT_PACK_CACHE_SIZE];
 
+	/* Caches for opened objects. */
 	struct got_object_cache objcache;
+	struct got_object_cache treecache;
 };
 
-const struct got_error*
-got_repo_cache_object(struct got_repository *, struct got_object_id *,
-    struct got_object *);
+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 *);
+const struct got_error*got_repo_cache_tree(struct got_repository *,
+    struct got_object_id *, struct got_tree_object *);
+struct got_tree_object *got_repo_get_cached_tree(struct got_repository *,
+    struct got_object_id *);
diff --git a/lib/object.c b/lib/object.c
index 7fb9519..07109c3 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -1121,6 +1121,12 @@ got_object_tree_open(struct got_tree_object **tree,
 {
 	const struct got_error *err = NULL;
 
+	*tree = got_repo_get_cached_tree(repo, &obj->id);
+	if (*tree != NULL) {
+		(*tree)->refcnt++;
+		return NULL;
+	}
+
 	if (obj->type != GOT_OBJ_TYPE_TREE)
 		return got_error(GOT_ERR_OBJ_TYPE);
 
@@ -1141,6 +1147,12 @@ got_object_tree_open(struct got_tree_object **tree,
 		err = read_tree_object_privsep(tree, obj, fd);
 		close(fd);
 	}
+
+	if (err == NULL) {
+		(*tree)->refcnt++;
+		err = got_repo_cache_tree(repo, &obj->id, *tree);
+	}
+
 	return err;
 }
 
@@ -1172,6 +1184,11 @@ got_object_tree_close(struct got_tree_object *tree)
 {
 	struct got_tree_entry *te;
 
+	if (tree->refcnt > 0) {
+		tree->refcnt--;
+		return;
+	}
+
 	while (!SIMPLEQ_EMPTY(&tree->entries)) {
 		te = SIMPLEQ_FIRST(&tree->entries);
 		SIMPLEQ_REMOVE_HEAD(&tree->entries, entry);
diff --git a/lib/repository.c b/lib/repository.c
index 9a2fb62..5900746 100644
--- a/lib/repository.c
+++ b/lib/repository.c
@@ -149,42 +149,67 @@ done:
 
 }
 
-const struct got_error *
-got_repo_cache_object(struct got_repository *repo, struct got_object_id *id,
-    struct got_object *obj)
+static const struct got_error *
+cache_add(struct got_object_cache *cache, struct got_object_id *id, void *item)
 {
 	const struct got_error *err = NULL;
 	struct got_object_cache_entry *ce;
 
-	if (repo->objcache.ncached >= GOT_OBJECT_CACHE_SIZE) {
+	if (cache->ncached >= GOT_OBJECT_CACHE_SIZE) {
 		err = got_object_idset_remove_random((void **)&ce,
-		    repo->objcache.set);
+		    cache->set);
 		if (err)
 			return err;
-		got_object_close(ce->obj);
+		switch (cache->type) {
+		case GOT_OBJECT_CACHE_TYPE_OBJ:
+			got_object_close(ce->data.obj);
+			break;
+		case GOT_OBJECT_CACHE_TYPE_TREE:
+			got_object_tree_close(ce->data.tree);
+			break;
+		}
 		free(ce);
-		repo->objcache.ncached--;
+		cache->ncached--;
 	}
 
 	ce = calloc(1, sizeof(*ce));
 	if (ce == NULL)
 		return got_error_from_errno();
 	memcpy(&ce->id, id, sizeof(ce->id));
-	ce->obj = obj;
-	err = got_object_idset_add(NULL, repo->objcache.set, id, ce);
+	switch (cache->type) {
+	case GOT_OBJECT_CACHE_TYPE_OBJ:
+		ce->data.obj = (struct got_object *)item;
+		break;
+	case GOT_OBJECT_CACHE_TYPE_TREE:
+		ce->data.tree = (struct got_tree_object *)item;
+		break;
+	}
+	err = got_object_idset_add(NULL, cache->set, id, ce);
 	if (err) {
 		if (err->code == GOT_ERR_OBJ_EXISTS) {
 			free(ce);
 			err = NULL;
 		}
-	} else {
-		obj->refcnt++;
-		repo->objcache.ncached++;
-	}
+	} else
+		cache->ncached++;
 
 	return err;
 }
 
+const struct got_error *
+got_repo_cache_object(struct got_repository *repo, struct got_object_id *id,
+    struct got_object *obj)
+{
+	const struct got_error *err = NULL;
+
+	err = cache_add(&repo->objcache, id, obj);
+	if (err)
+		return err;
+
+	obj->refcnt++;
+	return NULL;
+}
+
 struct got_object *
 got_repo_get_cached_object(struct got_repository *repo,
     struct got_object_id *id)
@@ -194,13 +219,45 @@ got_repo_get_cached_object(struct got_repository *repo,
 	ce = got_object_idset_get(repo->objcache.set, id);
 	if (ce) {
 		repo->objcache.cache_hit++;
-		return ce->obj;
+		return ce->data.obj;
 	}
+
 	repo->objcache.cache_miss++;
 	return NULL;
 }
 
 const struct got_error *
+got_repo_cache_tree(struct got_repository *repo, struct got_object_id *id,
+    struct got_tree_object *tree)
+{
+	const struct got_error *err = NULL;
+
+	err = cache_add(&repo->treecache, id, tree);
+	if (err)
+		return err;
+
+	tree->refcnt++;
+	return NULL;
+}
+
+struct got_tree_object *
+got_repo_get_cached_tree(struct got_repository *repo,
+    struct got_object_id *id)
+{
+	struct got_object_cache_entry *ce;
+
+	ce = got_object_idset_get(repo->treecache.set, id);
+	if (ce) {
+		repo->treecache.cache_hit++;
+		return ce->data.tree;
+	}
+
+	repo->treecache.cache_miss++;
+	return NULL;
+}
+
+
+const struct got_error *
 got_repo_open(struct got_repository **ret, const char *path)
 {
 	struct got_repository *repo = NULL;
@@ -225,6 +282,14 @@ got_repo_open(struct got_repository **ret, const char *path)
 		err = got_error_from_errno();
 		goto done;
 	}
+	repo->objcache.type = GOT_OBJECT_CACHE_TYPE_OBJ;
+
+	repo->treecache.set = got_object_idset_alloc();
+	if (repo->treecache.set == NULL) {
+		err = got_error_from_errno();
+		goto done;
+	}
+	repo->treecache.type = GOT_OBJECT_CACHE_TYPE_TREE;
 
 	repo->path = got_path_normalize(abspath);
 	if (repo->path == NULL) {
@@ -301,5 +366,7 @@ got_repo_close(struct got_repository *repo)
 	free(repo->path_git_dir);
 	if (repo->objcache.set)
 		got_object_idset_free(repo->objcache.set);
+	if (repo->treecache.set)
+		got_object_idset_free(repo->treecache.set);
 	free(repo);
 }