Commit 7915e6cc66a28d0767fc9899e58b2ca544ee12d0

Vicent Martí 2013-04-23T11:48:12

Merge pull request #1498 from libgit2/vmg/atomic64 64 bit atomic operations and shared cache memory usage

diff --git a/include/git2/common.h b/include/git2/common.h
index ccd252f..6101e13 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -133,7 +133,8 @@ enum {
 	GIT_OPT_SET_SEARCH_PATH,
 	GIT_OPT_SET_CACHE_OBJECT_LIMIT,
 	GIT_OPT_SET_CACHE_MAX_SIZE,
-	GIT_OPT_ENABLE_CACHING
+	GIT_OPT_ENABLE_CACHING,
+	GIT_OPT_GET_CACHED_MEMORY
 };
 
 /**
diff --git a/src/cache.c b/src/cache.c
index ca122fb..88f643b 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -18,7 +18,8 @@
 GIT__USE_OIDMAP
 
 bool git_cache__enabled = true;
-size_t git_cache__max_storage = (4 * 1024 * 1024);
+int64_t git_cache__max_storage = (256 * 1024 * 1024);
+git_atomic64 git_cache__current_storage = {0};
 
 static size_t git_cache__max_object_size[8] = {
 	0,     /* GIT_OBJ__EXT1 */
@@ -81,6 +82,7 @@ static void clear_cache(git_cache *cache)
 	});
 
 	kh_clear(oid, cache->map);
+	git_atomic64_add(&git_cache__current_storage, -cache->used_memory);
 	cache->used_memory = 0;
 }
 
@@ -106,7 +108,7 @@ void git_cache_free(git_cache *cache)
 static void cache_evict_entries(git_cache *cache)
 {
 	uint32_t seed = rand();
-	size_t evict_count = 8;
+	int64_t evicted_memory = 0, evict_count = 8;
 
 	/* do not infinite loop if there's not enough entries to evict  */
 	if (evict_count > kh_size(cache->map)) {
@@ -121,12 +123,15 @@ static void cache_evict_entries(git_cache *cache)
 			git_cached_obj *evict = kh_val(cache->map, pos);
 
 			evict_count--;
-			cache->used_memory -= evict->size;
+			evicted_memory += evict->size;
 			git_cached_obj_decref(evict);
 
 			kh_del(oid, cache->map, pos);
 		}
 	}
+
+	cache->used_memory -= evicted_memory;
+	git_atomic64_add(&git_cache__current_storage, -evicted_memory);
 }
 
 static bool cache_should_store(git_otype object_type, size_t object_size)
@@ -171,7 +176,8 @@ 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)
+	/* soften the load on the cache */
+	if (git_cache__current_storage.val > git_cache__max_storage)
 		cache_evict_entries(cache);
 
 	pos = kh_get(oid, cache->map, &entry->oid);
@@ -186,6 +192,7 @@ static void *cache_store(git_cache *cache, git_cached_obj *entry)
 			kh_val(cache->map, pos) = entry;
 			git_cached_obj_incref(entry);
 			cache->used_memory += entry->size;
+			git_atomic64_add(&git_cache__current_storage, (int64_t)entry->size);
 		}
 	}
 	/* found */
diff --git a/src/cache.h b/src/cache.h
index 1715c72..1fb87dc 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -31,11 +31,12 @@ typedef struct {
 typedef struct {
 	git_oidmap *map;
 	git_mutex   lock;
-	size_t      used_memory;
+	int64_t     used_memory;
 } git_cache;
 
 extern bool git_cache__enabled;
-extern size_t git_cache__max_storage;
+extern int64_t git_cache__max_storage;
+extern git_atomic64 git_cache__current_storage;
 
 int git_cache_set_max_object_size(git_otype type, size_t size);
 
diff --git a/src/thread-utils.h b/src/thread-utils.h
index dafe70a..28ecd29 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -18,6 +18,14 @@ typedef struct {
 #endif
 } git_atomic;
 
+typedef struct {
+#if defined(GIT_WIN32)
+	__int64 val;
+#else
+	int64_t val;
+#endif
+} git_atomic64;
+
 GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
 {
 	a->val = val;
@@ -57,6 +65,17 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a)
 #endif
 }
 
+GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
+{
+#if defined(GIT_WIN32)
+	return _InterlockedExchangeAdd(&a->val, addend);
+#elif defined(__GNUC__)
+	return __sync_add_and_fetch(&a->val, addend);
+#else
+#	error "Unsupported architecture for atomic operations"
+#endif
+}
+
 GIT_INLINE(int) git_atomic_dec(git_atomic *a)
 {
 #if defined(GIT_WIN32)
@@ -82,6 +101,17 @@ GIT_INLINE(void *) git___compare_and_swap(
 	return (foundval == oldval) ? oldval : newval;
 }
 
+GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend)
+{
+#if defined(GIT_WIN32)
+	return _InterlockedExchangeAdd64(&a->val, addend);
+#elif defined(__GNUC__)
+	return __sync_add_and_fetch(&a->val, addend);
+#else
+#	error "Unsupported architecture for atomic operations"
+#endif
+}
+
 #else
 
 #define git_thread unsigned int
@@ -110,6 +140,12 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a)
 	return ++a->val;
 }
 
+GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
+{
+	a->val += addend;
+	return a->val;
+}
+
 GIT_INLINE(int) git_atomic_dec(git_atomic *a)
 {
 	return --a->val;
@@ -125,6 +161,12 @@ GIT_INLINE(void *) git___compare_and_swap(
 	return oldval;
 }
 
+GIT_INLINE(int) git_atomic64_add(git_atomic64 *a, int64_t addend)
+{
+	a->val += addend;
+	return a->val;
+}
+
 #endif
 
 /* Atomically replace oldval with newval
diff --git a/src/util.c b/src/util.c
index c3fc697..ce67c7e 100644
--- a/src/util.c
+++ b/src/util.c
@@ -39,7 +39,6 @@ int git_libgit2_capabilities()
 /* Declarations for tuneable settings */
 extern size_t git_mwindow__window_size;
 extern size_t git_mwindow__mapped_limit;
-extern size_t git_odb__cache_size;
 
 static int config_level_to_futils_dir(int config_level)
 {
@@ -104,12 +103,17 @@ int git_libgit2_opts(int key, ...)
 		}
 
 	case GIT_OPT_SET_CACHE_MAX_SIZE:
-		git_cache__max_storage = va_arg(ap, size_t);
+		git_cache__max_storage = va_arg(ap, int64_t);
 		break;
 
 	case GIT_OPT_ENABLE_CACHING:
 		git_cache__enabled = (va_arg(ap, int) != 0);
 		break;
+
+	case GIT_OPT_GET_CACHED_MEMORY:
+		*(va_arg(ap, int64_t *)) = git_cache__current_storage.val;
+		*(va_arg(ap, int64_t *)) = git_cache__max_storage;
+		break;
 	}
 
 	va_end(ap);