pack: refcount entries and add a mutex around cache access
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
diff --git a/src/pack.c b/src/pack.c
index f7ef42d..5e08509 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -47,13 +47,13 @@ static int packfile_error(const char *message)
}
-static git_pack_cache_entry *new_cache_object(git_off_t off, git_rawobj *source)
+static git_pack_cache_entry *new_cache_object(git_rawobj *source)
{
git_pack_cache_entry *e = git__malloc(sizeof(git_pack_cache_entry));
if (!e)
return NULL;
- e->off = off;
+ e->refcount.val = 0;
memcpy(&e->raw, source, sizeof(git_rawobj));
return e;
@@ -63,6 +63,7 @@ static void free_cache_object(void *o)
{
git_pack_cache_entry *e = (git_pack_cache_entry *)o;
+ assert(e->refcount.val == 0);
if (e != NULL) {
git__free(e->raw.data);
git__free(e);
@@ -361,7 +362,7 @@ static int packfile_unpack_delta(
{
git_off_t base_offset, base_key;
git_rawobj base, delta;
- git_pack_cache_entry *cached;
+ git_pack_cache_entry *cached = NULL;
int error, found_base = 0;
khiter_t k;
@@ -375,15 +376,21 @@ static int packfile_unpack_delta(
if (!p->bases) {
p->bases = git_offmap_alloc();
GITERR_CHECK_ALLOC(p->bases);
+ git_mutex_init(&p->bases_lock);
}
base_key = base_offset; /* git_packfile_unpack modifies base_offset */
+ git_mutex_lock(&p->bases_lock);
k = kh_get(off, p->bases, base_offset);
if (k != kh_end(p->bases)) { /* found it */
cached = kh_value(p->bases, k);
+ git_atomic_inc(&cached->refcount);
found_base = 1;
memcpy(&base, &cached->raw, sizeof(git_rawobj));
- } else { /* have to inflate it */
+ }
+ git_mutex_unlock(&p->bases_lock);
+
+ if (!cached) { /* have to inflate it */
error = git_packfile_unpack(&base, p, &base_offset);
/*
@@ -410,12 +417,27 @@ static int packfile_unpack_delta(
if (error < 0)
goto on_error;
- if (!found_base) {
- cached = new_cache_object(base_key, &base);
+ if (found_base) {
+ git_atomic_dec(&cached->refcount);
+ } else {
+ cached = new_cache_object(&base);
if (cached) {
- k = kh_put(off, p->bases, base_key, &error);
- assert(error != 0);
- kh_value(p->bases, k) = cached;
+ int exists;
+
+ git_mutex_lock(&p->bases_lock);
+ /* Add it to the cache if nobody else has */
+ exists = kh_exist(p->bases, kh_get(off, p->bases, base_key));
+ if (!exists) {
+ k = kh_put(off, p->bases, base_key, &error);
+ assert(error != 0);
+ kh_value(p->bases, k) = cached;
+ }
+ git_mutex_unlock(&p->bases_lock);
+ /* Somebody beat us to adding it into the cache */
+ if (exists) {
+ free_cache_object(cached);
+ git__free(base.data);
+ }
}
}
@@ -738,6 +760,8 @@ static int packfile_open(struct git_pack_file *p)
p->bases = git_offmap_alloc();
GITERR_CHECK_ALLOC(p->bases);
+ git_mutex_init(&p->bases_lock);
+
/* TODO: open with noatime */
p->mwf.fd = git_futils_open_ro(p->pack_name);
diff --git a/src/pack.h b/src/pack.h
index 0f795f6..732bfc3 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -54,7 +54,7 @@ struct git_pack_idx_header {
};
typedef struct git_pack_cache_entry {
- git_off_t off;
+ git_atomic refcount;
git_rawobj raw;
} git_pack_cache_entry;
@@ -77,6 +77,7 @@ struct git_pack_file {
git_vector cache;
git_oid **oids;
+ git_mutex bases_lock;
git_offmap *bases; /* delta base cache */
/* something like ".git/objects/pack/xxxxx.pack" */