Commit ea642d61 by Russell Belfer

Fix race checking for existing index items

In the threading tests, I was still seeing a race condition where
the same item could end up being inserted multiple times into the
index.  Preserving the sorted-ness of the index outside of the
`index_insert` call fixes the issue.
parent 2e9d813b
...@@ -292,6 +292,7 @@ static void index_entry_reuc_free(git_index_reuc_entry *reuc) ...@@ -292,6 +292,7 @@ static void index_entry_reuc_free(git_index_reuc_entry *reuc)
static void index_entry_free(git_index_entry *entry) static void index_entry_free(git_index_entry *entry)
{ {
memset(&entry->id, 0, sizeof(entry->id));
git__free(entry); git__free(entry);
} }
...@@ -415,9 +416,9 @@ int git_index_open(git_index **index_out, const char *index_path) ...@@ -415,9 +416,9 @@ int git_index_open(git_index **index_out, const char *index_path)
} }
if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 || if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 ||
git_vector_init(&index->names, 32, conflict_name_cmp) < 0 || git_vector_init(&index->names, 8, conflict_name_cmp) < 0 ||
git_vector_init(&index->reuc, 32, reuc_cmp) < 0 || git_vector_init(&index->reuc, 8, reuc_cmp) < 0 ||
git_vector_init(&index->deleted, 2, git_index_entry_cmp) < 0) git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0)
goto fail; goto fail;
index->entries_cmp_path = git__strcmp_cb; index->entries_cmp_path = git__strcmp_cb;
...@@ -477,7 +478,7 @@ static void index_free_deleted(git_index *index) ...@@ -477,7 +478,7 @@ static void index_free_deleted(git_index *index)
int readers = (int)git_atomic_get(&index->readers); int readers = (int)git_atomic_get(&index->readers);
size_t i; size_t i;
if (readers > 0) if (readers > 0 || !index->deleted.length)
return; return;
for (i = 0; i < index->deleted.length; ++i) { for (i = 0; i < index->deleted.length; ++i) {
...@@ -500,12 +501,11 @@ static int index_remove_entry(git_index *index, size_t pos) ...@@ -500,12 +501,11 @@ static int index_remove_entry(git_index *index, size_t pos)
error = git_vector_remove(&index->entries, pos); error = git_vector_remove(&index->entries, pos);
if (!error) { if (!error) {
int readers = (int)git_atomic_get(&index->readers); if (git_atomic_get(&index->readers) > 0) {
if (readers > 0)
error = git_vector_insert(&index->deleted, entry); error = git_vector_insert(&index->deleted, entry);
else } else {
index_entry_free(entry); index_entry_free(entry);
}
} }
return error; return error;
...@@ -529,13 +529,13 @@ int git_index_clear(git_index *index) ...@@ -529,13 +529,13 @@ int git_index_clear(git_index *index)
error = index_remove_entry(index, index->entries.length - 1); error = index_remove_entry(index, index->entries.length - 1);
index_free_deleted(index); index_free_deleted(index);
git_mutex_unlock(&index->lock);
git_index_reuc_clear(index); git_index_reuc_clear(index);
git_index_name_clear(index); git_index_name_clear(index);
git_futils_filestamp_set(&index->stamp, NULL); git_futils_filestamp_set(&index->stamp, NULL);
git_mutex_unlock(&index->lock);
return error; return error;
} }
...@@ -860,6 +860,7 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src) ...@@ -860,6 +860,7 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
GITERR_CHECK_ALLOC(entry); GITERR_CHECK_ALLOC(entry);
index_entry_cpy(entry, src); index_entry_cpy(entry, src);
return 0; return 0;
} }
...@@ -961,6 +962,15 @@ static int check_file_directory_collision(git_index *index, ...@@ -961,6 +962,15 @@ static int check_file_directory_collision(git_index *index,
return 0; return 0;
} }
static int index_no_dups(void **old, void *new)
{
const git_index_entry *entry = new;
GIT_UNUSED(old);
giterr_set(GITERR_INDEX, "'%s' appears multiple times at stage %d",
entry->path, GIT_IDXENTRY_STAGE(entry));
return GIT_EEXISTS;
}
/* index_insert takes ownership of the new entry - if it can't insert /* index_insert takes ownership of the new entry - if it can't insert
* it, then it will return an error **and also free the entry**. When * it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the * it replaces an existing entry, it will update the entry_ptr with the
...@@ -987,9 +997,9 @@ static int index_insert( ...@@ -987,9 +997,9 @@ static int index_insert(
else else
entry->flags |= GIT_IDXENTRY_NAMEMASK; entry->flags |= GIT_IDXENTRY_NAMEMASK;
if ((error = git_mutex_lock(&index->lock)) < 0) { if (git_mutex_lock(&index->lock) < 0) {
giterr_set(GITERR_OS, "Unable to acquire index lock"); giterr_set(GITERR_OS, "Unable to acquire index lock");
return error; return -1;
} }
git_vector_sort(&index->entries); git_vector_sort(&index->entries);
...@@ -1010,25 +1020,27 @@ static int index_insert( ...@@ -1010,25 +1020,27 @@ static int index_insert(
/* if we are replacing an existing item, overwrite the existing entry /* if we are replacing an existing item, overwrite the existing entry
* and return it in place of the passed in one. * and return it in place of the passed in one.
*/ */
else if (existing && replace) { else if (existing) {
index_entry_cpy(existing, entry); if (replace)
index_entry_cpy(existing, entry);
index_entry_free(entry); index_entry_free(entry);
*entry_ptr = existing; *entry_ptr = entry = existing;
} }
else { else {
/* if replacing is not requested or no existing entry exists, just /* if replace is not requested or no existing entry exists, insert
* insert entry at the end; the index is no longer sorted * at the sorted position. (Since we re-sort after each insert to
* check for dups, this is actually cheaper in the long run.)
*/ */
error = git_vector_insert(&index->entries, entry); error = git_vector_insert_sorted(&index->entries, entry, index_no_dups);
} }
git_mutex_unlock(&index->lock);
if (error < 0) { if (error < 0) {
index_entry_free(*entry_ptr); index_entry_free(*entry_ptr);
*entry_ptr = NULL; *entry_ptr = NULL;
} }
git_mutex_unlock(&index->lock);
return error; return error;
} }
......
...@@ -15,8 +15,14 @@ void test_threads_diff__cleanup(void) ...@@ -15,8 +15,14 @@ void test_threads_diff__cleanup(void)
static void setup_trees(void) static void setup_trees(void)
{ {
git_index *idx;
_repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */ _repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */
/* avoid competing to load initial index */
cl_git_pass(git_repository_index(&idx, _repo));
git_index_free(idx);
cl_git_pass(git_revparse_single( cl_git_pass(git_revparse_single(
(git_object **)&_a, _repo, "0017bd4ab1^{tree}")); (git_object **)&_a, _repo, "0017bd4ab1^{tree}"));
cl_git_pass(git_revparse_single( cl_git_pass(git_revparse_single(
...@@ -107,7 +113,7 @@ void test_threads_diff__concurrent_diffs(void) ...@@ -107,7 +113,7 @@ void test_threads_diff__concurrent_diffs(void)
_check_counts = 1; _check_counts = 1;
run_in_parallel( run_in_parallel(
20, 32, run_index_diffs, setup_trees, free_trees); 5, 32, run_index_diffs, setup_trees, free_trees);
} }
static void *run_index_diffs_with_modifier(void *arg) static void *run_index_diffs_with_modifier(void *arg)
...@@ -169,5 +175,5 @@ void test_threads_diff__with_concurrent_index_modified(void) ...@@ -169,5 +175,5 @@ void test_threads_diff__with_concurrent_index_modified(void)
_check_counts = 0; _check_counts = 0;
run_in_parallel( run_in_parallel(
20, 32, run_index_diffs_with_modifier, setup_trees, free_trees); 5, 16, run_index_diffs_with_modifier, setup_trees, free_trees);
} }
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