Commit 4ae41f9c by lhchavez

Make the odb race-free

This change adds all the necessary locking to the odb to avoid races in
the backends.

Part of: #5592
parent ef6de8d5
...@@ -8,11 +8,6 @@ deadlock:attr_cache_lock ...@@ -8,11 +8,6 @@ deadlock:attr_cache_lock
# data races. # data races.
called_from_lib:libc.so.6 called_from_lib:libc.so.6
# TODO(#5595): Remove these once the fixes land. # TODO(#5592): Investigate and fix this. It can be triggered by the `thread`
race:src/odb.c
race:git_repository_odb__weakptr
race:cache_store
# TODO(#5595): Investigate and fix this. It can be triggered by the `thread`
# test suite. # test suite.
race:git_filter_list__load_ext race:git_filter_list__load_ext
...@@ -111,17 +111,18 @@ int git_config__configmap_lookup(int *out, git_config *config, git_configmap_ite ...@@ -111,17 +111,18 @@ int git_config__configmap_lookup(int *out, git_config *config, git_configmap_ite
int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item) int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item)
{ {
*out = repo->configmap_cache[(int)item]; *out = (int)(intptr_t)git__load(repo->configmap_cache[(int)item]);
if (*out == GIT_CONFIGMAP_NOT_CACHED) { if (*out == GIT_CONFIGMAP_NOT_CACHED) {
int error; int error;
int oldval = GIT_CONFIGMAP_NOT_CACHED;
git_config *config; git_config *config;
if ((error = git_repository_config__weakptr(&config, repo)) < 0 || if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
(error = git_config__configmap_lookup(out, config, item)) < 0) (error = git_config__configmap_lookup(out, config, item)) < 0)
return error; return error;
repo->configmap_cache[(int)item] = *out; git__compare_and_swap(&repo->configmap_cache[(int)item], &oldval, out);
} }
return 0; return 0;
......
...@@ -40,6 +40,7 @@ struct git_odb_object { ...@@ -40,6 +40,7 @@ struct git_odb_object {
/* EXPORT */ /* EXPORT */
struct git_odb { struct git_odb {
git_refcount rc; git_refcount rc;
git_mutex lock; /* protects backends */
git_vector backends; git_vector backends;
git_cache own_cache; git_cache own_cache;
unsigned int do_fsync :1; unsigned int do_fsync :1;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "mwindow.h" #include "mwindow.h"
#include "odb.h" #include "odb.h"
#include "oid.h" #include "oid.h"
#include "oidarray.h"
/* Option to bypass checking existence of '.keep' files */ /* Option to bypass checking existence of '.keep' files */
bool git_disable_pack_keep_file_checks = false; bool git_disable_pack_keep_file_checks = false;
...@@ -195,7 +196,8 @@ static void pack_index_free(struct git_pack_file *p) ...@@ -195,7 +196,8 @@ static void pack_index_free(struct git_pack_file *p)
} }
} }
static int pack_index_check(const char *path, struct git_pack_file *p) /* Run with the packfile lock held */
static int pack_index_check_locked(const char *path, struct git_pack_file *p)
{ {
struct git_pack_idx_header *hdr; struct git_pack_idx_header *hdr;
uint32_t version, nr, i, *index; uint32_t version, nr, i, *index;
...@@ -301,7 +303,8 @@ static int pack_index_check(const char *path, struct git_pack_file *p) ...@@ -301,7 +303,8 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
return 0; return 0;
} }
static int pack_index_open(struct git_pack_file *p) /* Run with the packfile lock held */
static int pack_index_open_locked(struct git_pack_file *p)
{ {
int error = 0; int error = 0;
size_t name_len; size_t name_len;
...@@ -324,18 +327,11 @@ static int pack_index_open(struct git_pack_file *p) ...@@ -324,18 +327,11 @@ static int pack_index_open(struct git_pack_file *p)
return -1; return -1;
} }
if ((error = git_mutex_lock(&p->lock)) < 0) {
git_buf_dispose(&idx_name);
return error;
}
if (p->index_version == -1) if (p->index_version == -1)
error = pack_index_check(idx_name.ptr, p); error = pack_index_check_locked(idx_name.ptr, p);
git_buf_dispose(&idx_name); git_buf_dispose(&idx_name);
git_mutex_unlock(&p->lock);
return error; return error;
} }
...@@ -1015,13 +1011,14 @@ static int packfile_open(struct git_pack_file *p) ...@@ -1015,13 +1011,14 @@ static int packfile_open(struct git_pack_file *p)
git_oid sha1; git_oid sha1;
unsigned char *idx_sha1; unsigned char *idx_sha1;
if (p->index_version == -1 && pack_index_open(p) < 0)
return git_odb__error_notfound("failed to open packfile", NULL, 0);
/* if mwf opened by another thread, return now */
if (git_mutex_lock(&p->lock) < 0) if (git_mutex_lock(&p->lock) < 0)
return packfile_error("failed to get lock for open"); return packfile_error("failed to get lock for open");
if (pack_index_open_locked(p) < 0) {
git_mutex_unlock(&p->lock);
return git_odb__error_notfound("failed to open packfile", NULL, 0);
}
if (p->mwf.fd >= 0) { if (p->mwf.fd >= 0) {
git_mutex_unlock(&p->lock); git_mutex_unlock(&p->lock);
return 0; return 0;
...@@ -1210,32 +1207,40 @@ int git_pack_foreach_entry( ...@@ -1210,32 +1207,40 @@ int git_pack_foreach_entry(
git_odb_foreach_cb cb, git_odb_foreach_cb cb,
void *data) void *data)
{ {
const unsigned char *index = p->index_map.data, *current; const unsigned char *index, *current;
uint32_t i; uint32_t i;
int error = 0; int error = 0;
git_array_oid_t oids = GIT_ARRAY_INIT;
git_oid *oid;
if (index == NULL) { if (git_mutex_lock(&p->lock) < 0)
if ((error = pack_index_open(p)) < 0) return packfile_error("failed to get lock for git_pack_foreach_entry");
return error;
GIT_ASSERT(p->index_map.data); if ((error = pack_index_open_locked(p)) < 0) {
index = p->index_map.data; git_mutex_unlock(&p->lock);
return error;
} }
if (p->index_version > 1) { GIT_ASSERT(p->index_map.data);
index = p->index_map.data;
if (p->index_version > 1)
index += 8; index += 8;
}
index += 4 * 256; index += 4 * 256;
if (p->oids == NULL) { if (p->oids == NULL) {
git_vector offsets, oids; git_vector offsets, oids;
if ((error = git_vector_init(&oids, p->num_objects, NULL))) if ((error = git_vector_init(&oids, p->num_objects, NULL))) {
git_mutex_unlock(&p->lock);
return error; return error;
}
if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4))) if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4))) {
git_mutex_unlock(&p->lock);
return error; return error;
}
if (p->index_version > 1) { if (p->index_version > 1) {
const unsigned char *off = index + 24 * p->num_objects; const unsigned char *off = index + 24 * p->num_objects;
...@@ -1256,10 +1261,33 @@ int git_pack_foreach_entry( ...@@ -1256,10 +1261,33 @@ int git_pack_foreach_entry(
p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids); p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids);
} }
for (i = 0; i < p->num_objects; i++) /* We need to copy the OIDs to another array before we relinquish the lock to avoid races. */
if ((error = cb(p->oids[i], data)) != 0) git_array_init_to_size(oids, p->num_objects);
return git_error_set_after_callback(error); if (!oids.ptr) {
git_mutex_unlock(&p->lock);
git_array_clear(oids);
GIT_ERROR_CHECK_ARRAY(oids);
}
for (i = 0; i < p->num_objects; i++) {
oid = git_array_alloc(oids);
if (!oid) {
git_mutex_unlock(&p->lock);
git_array_clear(oids);
GIT_ERROR_CHECK_ALLOC(oid);
}
git_oid_cpy(oid, p->oids[i]);
}
git_mutex_unlock(&p->lock);
git_array_foreach(oids, i, oid) {
if ((error = cb(oid, data)) != 0) {
git_error_set_after_callback(error);
break;
}
}
git_array_clear(oids);
return error; return error;
} }
...@@ -1297,18 +1325,17 @@ static int pack_entry_find_offset( ...@@ -1297,18 +1325,17 @@ static int pack_entry_find_offset(
int pos, found = 0; int pos, found = 0;
off64_t offset; off64_t offset;
const unsigned char *current = 0; const unsigned char *current = 0;
int error = 0;
*offset_out = 0; *offset_out = 0;
if (p->index_version == -1) { if (git_mutex_lock(&p->lock) < 0)
int error; return packfile_error("failed to get lock for pack_entry_find_offset");
if ((error = pack_index_open(p)) < 0)
return error;
GIT_ASSERT(p->index_map.data); if ((error = pack_index_open_locked(p)) < 0)
} goto cleanup;
GIT_ASSERT(p->index_map.data);
index = p->index_map.data; index = p->index_map.data;
level1_ofs = p->index_map.data; level1_ofs = p->index_map.data;
...@@ -1360,14 +1387,19 @@ static int pack_entry_find_offset( ...@@ -1360,14 +1387,19 @@ static int pack_entry_find_offset(
} }
} }
if (!found) if (!found) {
return git_odb__error_notfound("failed to find offset for pack entry", short_oid, len); error = git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
if (found > 1) goto cleanup;
return git_odb__error_ambiguous("found multiple offsets for pack entry"); }
if (found > 1) {
error = git_odb__error_ambiguous("found multiple offsets for pack entry");
goto cleanup;
}
if ((offset = nth_packed_object_offset(p, pos)) < 0) { if ((offset = nth_packed_object_offset(p, pos)) < 0) {
git_error_set(GIT_ERROR_ODB, "packfile index is corrupt"); git_error_set(GIT_ERROR_ODB, "packfile index is corrupt");
return -1; error = -1;
goto cleanup;
} }
*offset_out = offset; *offset_out = offset;
...@@ -1382,7 +1414,9 @@ static int pack_entry_find_offset( ...@@ -1382,7 +1414,9 @@ static int pack_entry_find_offset(
} }
#endif #endif
return 0; cleanup:
git_mutex_unlock(&p->lock);
return error;
} }
int git_pack_entry_find( int git_pack_entry_find(
......
...@@ -1107,7 +1107,8 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) ...@@ -1107,7 +1107,8 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
GIT_ASSERT_ARG(repo); GIT_ASSERT_ARG(repo);
GIT_ASSERT_ARG(out); GIT_ASSERT_ARG(out);
if (repo->_odb == NULL) { *out = git__load(repo->_odb);
if (*out == NULL) {
git_buf odb_path = GIT_BUF_INIT; git_buf odb_path = GIT_BUF_INIT;
git_odb *odb; git_odb *odb;
...@@ -1131,9 +1132,9 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) ...@@ -1131,9 +1132,9 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
} }
git_buf_dispose(&odb_path); git_buf_dispose(&odb_path);
*out = git__load(repo->_odb);
} }
*out = repo->_odb;
return error; return error;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment