Commit d87715926049390a2417a2476742114ec966686a

Vicent Marti 2013-04-22T17:04:52

cache: Max cache size, and evict when the cache fills up

diff --git a/include/git2/common.h b/include/git2/common.h
index 80d83d3..ccd252f 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -131,7 +131,8 @@ enum {
 	GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
 	GIT_OPT_GET_SEARCH_PATH,
 	GIT_OPT_SET_SEARCH_PATH,
-	GIT_OPT_SET_CACHE_LIMIT,
+	GIT_OPT_SET_CACHE_OBJECT_LIMIT,
+	GIT_OPT_SET_CACHE_MAX_SIZE,
 	GIT_OPT_ENABLE_CACHING
 };
 
diff --git a/src/cache.c b/src/cache.c
index c51be89..ca122fb 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -18,6 +18,7 @@
 GIT__USE_OIDMAP
 
 bool git_cache__enabled = true;
+size_t git_cache__max_storage = (4 * 1024 * 1024);
 
 static size_t git_cache__max_object_size[8] = {
 	0,     /* GIT_OBJ__EXT1 */
@@ -70,19 +71,25 @@ int git_cache_init(git_cache *cache)
 	return 0;
 }
 
-void git_cache_clear(git_cache *cache)
+/* called with lock */
+static void clear_cache(git_cache *cache)
 {
 	git_cached_obj *evict = NULL;
 
-	if (git_mutex_lock(&cache->lock) < 0)
-		return;
-
 	kh_foreach_value(cache->map, evict, {
 		git_cached_obj_decref(evict);
 	});
 
 	kh_clear(oid, cache->map);
 	cache->used_memory = 0;
+}
+
+void git_cache_clear(git_cache *cache)
+{
+	if (git_mutex_lock(&cache->lock) < 0)
+		return;
+
+	clear_cache(cache);
 
 	git_mutex_unlock(&cache->lock);
 }
@@ -95,14 +102,15 @@ void git_cache_free(git_cache *cache)
 	git_mutex_free(&cache->lock);
 }
 
-/* Call with lock, yo */
-static void cache_evict_entries(git_cache *cache, size_t evict_count)
+/* Called with lock */
+static void cache_evict_entries(git_cache *cache)
 {
 	uint32_t seed = rand();
+	size_t evict_count = 8;
 
 	/* do not infinite loop if there's not enough entries to evict  */
 	if (evict_count > kh_size(cache->map)) {
-		git_cache_clear(cache);
+		clear_cache(cache);
 		return;
 	}
 
@@ -163,6 +171,9 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry)
 	if (git_mutex_lock(&cache->lock) < 0)
 		return entry;
 
+	if (cache->used_memory > git_cache__max_storage)
+		cache_evict_entries(cache);
+
 	pos = kh_get(oid, cache->map, &entry->oid);
 
 	/* not found */
diff --git a/src/cache.h b/src/cache.h
index e95d521..1715c72 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -35,6 +35,7 @@ typedef struct {
 } git_cache;
 
 extern bool git_cache__enabled;
+extern size_t git_cache__max_storage;
 
 int git_cache_set_max_object_size(git_otype type, size_t size);
 
diff --git a/src/util.c b/src/util.c
index 1ed5d5d..c3fc697 100644
--- a/src/util.c
+++ b/src/util.c
@@ -95,7 +95,7 @@ int git_libgit2_opts(int key, ...)
 			error = git_futils_dirs_set(error, va_arg(ap, const char *));
 		break;
 
-	case GIT_OPT_SET_CACHE_LIMIT:
+	case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
 		{
 			git_otype type = (git_otype)va_arg(ap, int);
 			size_t size = va_arg(ap, size_t);
@@ -103,6 +103,10 @@ int git_libgit2_opts(int key, ...)
 			break;
 		}
 
+	case GIT_OPT_SET_CACHE_MAX_SIZE:
+		git_cache__max_storage = va_arg(ap, size_t);
+		break;
+
 	case GIT_OPT_ENABLE_CACHING:
 		git_cache__enabled = (va_arg(ap, int) != 0);
 		break;
diff --git a/tests-clar/object/cache.c b/tests-clar/object/cache.c
index b36bf27..a3eba87 100644
--- a/tests-clar/object/cache.c
+++ b/tests-clar/object/cache.c
@@ -13,7 +13,7 @@ void test_object_cache__cleanup(void)
 	git_repository_free(g_repo);
 	g_repo = NULL;
 
-	git_libgit2_opts(GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0);
+	git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0);
 }
 
 static struct {
@@ -54,7 +54,7 @@ void test_object_cache__cache_everything(void)
 	git_odb *odb;
 
 	git_libgit2_opts(
-		GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767);
+		GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767);
 
 	cl_git_pass(git_repository_odb(&odb, g_repo));
 
@@ -103,7 +103,7 @@ void test_object_cache__cache_no_blobs(void)
 	git_object *obj;
 	git_odb *odb;
 
-	git_libgit2_opts(GIT_OPT_SET_CACHE_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0);
+	git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0);
 
 	cl_git_pass(git_repository_odb(&odb, g_repo));