diff --git a/src/cache.c b/src/cache.c index e7f333577..6eb18dae5 100644 --- a/src/cache.c +++ b/src/cache.c @@ -11,100 +11,147 @@ #include "thread-utils.h" #include "util.h" #include "cache.h" +#include "odb.h" +#include "object.h" #include "git2/oid.h" -int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) +GIT__USE_OIDMAP + +int git_cache_init(git_cache *cache) { - if (size < 8) - size = 8; - size = git__size_t_powerof2(size); - - cache->size_mask = size - 1; - cache->lru_count = 0; - cache->free_obj = free_ptr; - + cache->map = git_oidmap_alloc(); git_mutex_init(&cache->lock); - - cache->nodes = git__malloc(size * sizeof(git_cached_obj *)); - GITERR_CHECK_ALLOC(cache->nodes); - - memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *)); return 0; } void git_cache_free(git_cache *cache) { - size_t i; - - for (i = 0; i < (cache->size_mask + 1); ++i) { - if (cache->nodes[i] != NULL) - git_cached_obj_decref(cache->nodes[i], cache->free_obj); - } - + git_oidmap_free(cache->map); git_mutex_free(&cache->lock); - git__free(cache->nodes); } -void *git_cache_get(git_cache *cache, const git_oid *oid) +static bool cache_should_store(git_cached_obj *entry) { - uint32_t hash; - git_cached_obj *node = NULL, *result = NULL; - - memcpy(&hash, oid->id, sizeof(hash)); - - if (git_mutex_lock(&cache->lock)) { - giterr_set(GITERR_THREAD, "unable to lock cache mutex"); - return NULL; - } - - { - node = cache->nodes[hash & cache->size_mask]; - - if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) { - git_cached_obj_incref(node); - result = node; - } - } - git_mutex_unlock(&cache->lock); - - return result; + return true; } -void *git_cache_try_store(git_cache *cache, void *_entry) +static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags) { - git_cached_obj *entry = _entry; - uint32_t hash; + khiter_t pos; + git_cached_obj *entry = NULL; - memcpy(&hash, &entry->oid, sizeof(uint32_t)); - - if (git_mutex_lock(&cache->lock)) { - giterr_set(GITERR_THREAD, "unable to lock cache mutex"); + if (git_mutex_lock(&cache->lock) < 0) return NULL; - } - { - git_cached_obj *node = cache->nodes[hash & cache->size_mask]; + pos = kh_get(oid, cache->map, oid); + if (pos != kh_end(cache->map)) { + entry = kh_val(cache->map, pos); - /* increase the refcount on this object, because - * the cache now owns it */ - git_cached_obj_incref(entry); - - if (node == NULL) { - cache->nodes[hash & cache->size_mask] = entry; - } else if (git_oid_cmp(&node->oid, &entry->oid) == 0) { - git_cached_obj_decref(entry, cache->free_obj); - entry = node; + if (flags && entry->flags != flags) { + entry = NULL; } else { - git_cached_obj_decref(node, cache->free_obj); - cache->nodes[hash & cache->size_mask] = entry; + git_cached_obj_incref(entry); } - - /* increase the refcount again, because we are - * returning it to the user */ - git_cached_obj_incref(entry); - } + git_mutex_unlock(&cache->lock); return entry; } + +static void *cache_store(git_cache *cache, git_cached_obj *entry) +{ + khiter_t pos; + + git_cached_obj_incref(entry); + + if (!cache_should_store(entry)) + return entry; + + if (git_mutex_lock(&cache->lock) < 0) + return entry; + + pos = kh_get(oid, cache->map, &entry->oid); + + /* not found */ + if (pos == kh_end(cache->map)) { + int rval; + + pos = kh_put(oid, cache->map, &entry->oid, &rval); + if (rval >= 0) { + kh_key(cache->map, pos) = &entry->oid; + kh_val(cache->map, pos) = entry; + git_cached_obj_incref(entry); + } + } + /* found */ + else { + git_cached_obj *stored_entry = kh_val(cache->map, pos); + + if (stored_entry->flags == entry->flags) { + git_cached_obj_decref(entry); + git_cached_obj_incref(stored_entry); + entry = stored_entry; + } else if (stored_entry->flags == GIT_CACHE_STORE_RAW && + entry->flags == GIT_CACHE_STORE_PARSED) { + git_cached_obj_decref(stored_entry); + git_cached_obj_incref(entry); + + kh_key(cache->map, pos) = &entry->oid; + kh_val(cache->map, pos) = entry; + } else { + /* NO OP */ + } + } + + git_mutex_unlock(&cache->lock); + return entry; +} + +void *git_cache_store_raw(git_cache *cache, git_odb_object *entry) +{ + entry->cached.flags = GIT_CACHE_STORE_RAW; + return cache_store(cache, (git_cached_obj *)entry); +} + +void *git_cache_store_parsed(git_cache *cache, git_object *entry) +{ + entry->cached.flags = GIT_CACHE_STORE_PARSED; + return cache_store(cache, (git_cached_obj *)entry); +} + +git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid) +{ + return cache_get(cache, oid, GIT_CACHE_STORE_RAW); +} + +git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid) +{ + return cache_get(cache, oid, GIT_CACHE_STORE_PARSED); +} + +void *git_cache_get_any(git_cache *cache, const git_oid *oid) +{ + return cache_get(cache, oid, GIT_CACHE_STORE_ANY); +} + +void git_cached_obj_decref(void *_obj) +{ + git_cached_obj *obj = _obj; + + if (git_atomic_dec(&obj->refcount) == 0) { + switch (obj->flags) { + case GIT_CACHE_STORE_RAW: + git_odb_object__free(_obj); + break; + + case GIT_CACHE_STORE_PARSED: + git_object__free(_obj); + break; + + default: + git__free(_obj); + break; + } + } +} diff --git a/src/cache.h b/src/cache.h index 7034ec268..f30af9c3e 100644 --- a/src/cache.h +++ b/src/cache.h @@ -12,30 +12,35 @@ #include "git2/odb.h" #include "thread-utils.h" +#include "oidmap.h" -#define GIT_DEFAULT_CACHE_SIZE 128 - -typedef void (*git_cached_obj_freeptr)(void *); +enum { + GIT_CACHE_STORE_ANY = 0, + GIT_CACHE_STORE_RAW = 1, + GIT_CACHE_STORE_PARSED = 2 +}; typedef struct { git_oid oid; git_atomic refcount; + uint32_t flags; } git_cached_obj; typedef struct { - git_cached_obj **nodes; + git_oidmap *map; git_mutex lock; - unsigned int lru_count; - size_t size_mask; - git_cached_obj_freeptr free_obj; } git_cache; -int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); +int git_cache_init(git_cache *cache); void git_cache_free(git_cache *cache); -void *git_cache_try_store(git_cache *cache, void *entry); -void *git_cache_get(git_cache *cache, const git_oid *oid); +void *git_cache_store_raw(git_cache *cache, git_odb_object *entry); +void *git_cache_store_parsed(git_cache *cache, git_object *entry); + +git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid); +git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid); +void *git_cache_get_any(git_cache *cache, const git_oid *oid); GIT_INLINE(void) git_cached_obj_incref(void *_obj) { @@ -43,12 +48,6 @@ GIT_INLINE(void) git_cached_obj_incref(void *_obj) git_atomic_inc(&obj->refcount); } -GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj) -{ - git_cached_obj *obj = _obj; - - if (git_atomic_dec(&obj->refcount) == 0) - free_obj(obj); -} +void git_cached_obj_decref(void *_obj); #endif diff --git a/src/object.c b/src/object.c index 80fe51152..5542ebc8e 100644 --- a/src/object.c +++ b/src/object.c @@ -121,12 +121,13 @@ int git_object__from_odb_object( break; } - if (error < 0) + if (error < 0) { git_object__free(object); - else - *object_out = git_cache_try_store(&repo->objects, object); + return error; + } - return error; + *object_out = git_cache_store_parsed(&repo->objects, object); + return 0; } int git_object_lookup_prefix( @@ -154,27 +155,38 @@ int git_object_lookup_prefix( len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { + git_cached_obj *cached = NULL; + /* We want to match the full id : we can first look up in the cache, * since there is no need to check for non ambiguousity */ - object = git_cache_get(&repo->objects, id); - if (object != NULL) { - if (type != GIT_OBJ_ANY && type != object->type) { - git_object_free(object); - giterr_set(GITERR_INVALID, "The requested type does not match the type in ODB"); - return GIT_ENOTFOUND; + cached = git_cache_get_any(&repo->objects, id); + if (cached != NULL) { + if (cached->flags == GIT_CACHE_STORE_PARSED) { + object = (git_object *)cached; + + if (type != GIT_OBJ_ANY && type != object->type) { + git_object_free(object); + giterr_set(GITERR_INVALID, + "The requested type does not match the type in ODB"); + return GIT_ENOTFOUND; + } + + *object_out = object; + return 0; + } else if (cached->flags == GIT_CACHE_STORE_RAW) { + odb_obj = (git_odb_object *)cached; + } else { + assert(!"Wrong caching type in the global object cache"); } - - *object_out = object; - return 0; + } else { + /* Object was not found in the cache, let's explore the backends. + * We could just use git_odb_read_unique_short_oid, + * it is the same cost for packed and loose object backends, + * but it may be much more costly for sqlite and hiredis. + */ + error = git_odb_read(&odb_obj, odb, id); } - - /* Object was not found in the cache, let's explore the backends. - * We could just use git_odb_read_unique_short_oid, - * it is the same cost for packed and loose object backends, - * but it may be much more costly for sqlite and hiredis. - */ - error = git_odb_read(&odb_obj, odb, id); } else { git_oid short_oid; @@ -245,7 +257,7 @@ void git_object_free(git_object *object) if (object == NULL) return; - git_cached_obj_decref((git_cached_obj *)object, git_object__free); + git_cached_obj_decref(object); } const git_oid *git_object_id(const git_object *obj) diff --git a/src/odb.c b/src/odb.c index d6ea8c29a..821fbd70c 100644 --- a/src/odb.c +++ b/src/odb.c @@ -14,6 +14,7 @@ #include "odb.h" #include "delta-apply.h" #include "filter.h" +#include "repository.h" #include "git2/odb_backend.h" #include "git2/oid.h" @@ -34,7 +35,15 @@ typedef struct ino_t disk_inode; } backend_internal; -size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE; +static git_cache *odb_cache(git_odb *odb) +{ + if (odb->rc.owner != NULL) { + git_repository *owner = odb->rc.owner; + return &owner->objects; + } + + return &odb->own_cache; +} static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth); @@ -83,10 +92,8 @@ static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) return object; } -static void free_odb_object(void *o) +void git_odb_object__free(git_odb_object *object) { - git_odb_object *object = (git_odb_object *)o; - if (object != NULL) { git__free(object->raw.data); git__free(object); @@ -118,7 +125,7 @@ void git_odb_object_free(git_odb_object *object) if (object == NULL) return; - git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); + git_cached_obj_decref(object); } int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) @@ -355,9 +362,8 @@ int git_odb_new(git_odb **out) git_odb *db = git__calloc(1, sizeof(*db)); GITERR_CHECK_ALLOC(db); - if (git_cache_init(&db->cache, git_odb__cache_size, &free_odb_object) < 0 || - git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) - { + if (git_cache_init(&db->own_cache) < 0 || + git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { git__free(db); return -1; } @@ -559,7 +565,7 @@ static void odb_free(git_odb *db) } git_vector_free(&db->backends); - git_cache_free(&db->cache); + git_cache_free(&db->own_cache); git__free(db); } @@ -580,7 +586,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) assert(db && id); - if ((object = git_cache_get(&db->cache, id)) != NULL) { + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { git_odb_object_free(object); return (int)true; } @@ -630,7 +636,7 @@ int git_odb__read_header_or_object( assert(db && id && out && len_p && type_p); - if ((object = git_cache_get(&db->cache, id)) != NULL) { + if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { *len_p = object->raw.len; *type_p = object->raw.type; *out = object; @@ -678,7 +684,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) return GIT_ENOTFOUND; } - *out = git_cache_get(&db->cache, id); + *out = git_cache_get_raw(odb_cache(db), id); if (*out != NULL) return 0; @@ -704,7 +710,7 @@ attempt_lookup: if (error && error != GIT_PASSTHROUGH) return error; - *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw)); + *out = git_cache_store_raw(odb_cache(db), new_odb_object(id, &raw)); return 0; } @@ -727,7 +733,7 @@ int git_odb_read_prefix( len = GIT_OID_HEXSZ; if (len == GIT_OID_HEXSZ) { - *out = git_cache_get(&db->cache, short_id); + *out = git_cache_get_raw(odb_cache(db), short_id); if (*out != NULL) return 0; } @@ -768,7 +774,7 @@ attempt_lookup: if (!found) return git_odb__error_notfound("no match for prefix", short_id); - *out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw)); + *out = git_cache_store_raw(odb_cache(db), new_odb_object(&found_full_oid, &raw)); return 0; } diff --git a/src/odb.h b/src/odb.h index 7c018cc50..9abf594e8 100644 --- a/src/odb.h +++ b/src/odb.h @@ -36,9 +36,11 @@ struct git_odb_object { struct git_odb { git_refcount rc; git_vector backends; - git_cache cache; + git_cache own_cache; }; +void git_odb_object__free(git_odb_object *object); + /* * Hash a git_rawobj internally. * The `git_rawobj` is supposed to be previously initialized diff --git a/src/oidmap.h b/src/oidmap.h index 40274cd19..dfa951af3 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -21,10 +21,8 @@ typedef khash_t(oid) git_oidmap; GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid) { - int i; - khint_t h = 0; - for (i = 0; i < 20; ++i) - h = (h << 5) - h + oid->id[i]; + khint_t h; + memcpy(&h, oid, sizeof(khint_t)); return h; } diff --git a/src/repository.c b/src/repository.c index a16f69da4..cda75b85f 100644 --- a/src/repository.c +++ b/src/repository.c @@ -119,7 +119,7 @@ static git_repository *repository_alloc(void) memset(repo, 0x0, sizeof(git_repository)); - if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) { + if (git_cache_init(&repo->objects) < 0) { git__free(repo); return NULL; } diff --git a/src/util.c b/src/util.c index 8e83d298e..c4a8c786d 100644 --- a/src/util.c +++ b/src/util.c @@ -93,14 +93,6 @@ int git_libgit2_opts(int key, ...) if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) error = git_futils_dirs_set(error, va_arg(ap, const char *)); break; - - case GIT_OPT_GET_ODB_CACHE_SIZE: - *(va_arg(ap, size_t *)) = git_odb__cache_size; - break; - - case GIT_OPT_SET_ODB_CACHE_SIZE: - git_odb__cache_size = va_arg(ap, size_t); - break; } va_end(ap); diff --git a/tests-clar/core/opts.c b/tests-clar/core/opts.c index 907339d51..3173c648b 100644 --- a/tests-clar/core/opts.c +++ b/tests-clar/core/opts.c @@ -16,15 +16,4 @@ void test_core_opts__readwrite(void) git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val); cl_assert(new_val == old_val); - - git_libgit2_opts(GIT_OPT_GET_ODB_CACHE_SIZE, &old_val); - - cl_assert(old_val == GIT_DEFAULT_CACHE_SIZE); - - git_libgit2_opts(GIT_OPT_SET_ODB_CACHE_SIZE, (size_t)GIT_DEFAULT_CACHE_SIZE*2); - git_libgit2_opts(GIT_OPT_GET_ODB_CACHE_SIZE, &new_val); - - cl_assert(new_val == (GIT_DEFAULT_CACHE_SIZE*2)); - - git_libgit2_opts(GIT_OPT_GET_ODB_CACHE_SIZE, &old_val); }