cache: Fix deadlock Do not try to adquire the same node lock twice when the cuckoo hashing resolves to the same node.
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
diff --git a/src/cache.c b/src/cache.c
index 0509afd..6e4ad4c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -110,7 +110,7 @@ void *git_cache_try_store(git_cache *cache, void *entry)
cache_node *nodes[GIT_CACHE_OPENADR], *lru_node;
const uint32_t *hash;
const git_oid *oid;
- size_t i;
+ size_t i, j, node_count;
oid = &((git_cached_obj*)entry)->oid;
hash = (const uint32_t *)oid->id;
@@ -119,16 +119,24 @@ void *git_cache_try_store(git_cache *cache, void *entry)
* the cache now owns it */
git_cached_obj_incref(entry);
+ node_count = 0;
for (i = 0; i < GIT_CACHE_OPENADR; ++i) {
size_t pos = hash[i] & cache->size_mask;
+ cache_node *node = &cache->nodes[pos];
+
+ for (j = 0; j < node_count; ++j)
+ if (nodes[j] == node)
+ break;
- nodes[i] = &cache->nodes[pos];
- git_mutex_lock(&nodes[i]->lock);
+ if (j == node_count) {
+ nodes[node_count++] = node;
+ git_mutex_lock(node);
+ }
}
lru_node = nodes[0];
- for (i = 0; i < GIT_CACHE_OPENADR; ++i) {
+ for (i = 0; i < node_count; ++i) {
if (nodes[i]->ptr == NULL) {
nodes[i]->ptr = entry;
@@ -145,7 +153,7 @@ void *git_cache_try_store(git_cache *cache, void *entry)
lru_node = nodes[i];
}
- if (i == GIT_CACHE_OPENADR) {
+ if (i == node_count) {
void *old_entry = lru_node->ptr;
assert(old_entry);
@@ -158,7 +166,7 @@ void *git_cache_try_store(git_cache *cache, void *entry)
* returning it to the user */
git_cached_obj_incref(entry);
- for (i = 0; i < GIT_CACHE_OPENADR; ++i)
+ for (i = 0; i < node_count; ++i)
git_mutex_unlock(&nodes[i]->lock);
return entry;