Commit dfcba09e by Russell Belfer

Merge pull request #2395 from libgit2/cmn/ref-iter-concurrent

Concurrent ref iterator access
parents bccb36eb 2d945f82
...@@ -458,6 +458,7 @@ typedef struct { ...@@ -458,6 +458,7 @@ typedef struct {
git_pool pool; git_pool pool;
git_vector loose; git_vector loose;
git_sortedcache *cache;
size_t loose_pos; size_t loose_pos;
size_t packed_pos; size_t packed_pos;
} refdb_fs_iter; } refdb_fs_iter;
...@@ -468,6 +469,7 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter) ...@@ -468,6 +469,7 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
git_vector_free(&iter->loose); git_vector_free(&iter->loose);
git_pool_clear(&iter->pool); git_pool_clear(&iter->pool);
git_sortedcache_free(iter->cache);
git__free(iter); git__free(iter);
} }
...@@ -539,10 +541,14 @@ static int refdb_fs_backend__iterator_next( ...@@ -539,10 +541,14 @@ static int refdb_fs_backend__iterator_next(
giterr_clear(); giterr_clear();
} }
git_sortedcache_rlock(backend->refcache); if (!iter->cache) {
if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
return error;
}
while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) { error = GIT_ITEROVER;
ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++); while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */ if (!ref) /* stop now if another thread deleted refs and we past end */
break; break;
...@@ -556,7 +562,6 @@ static int refdb_fs_backend__iterator_next( ...@@ -556,7 +562,6 @@ static int refdb_fs_backend__iterator_next(
break; break;
} }
git_sortedcache_runlock(backend->refcache);
return error; return error;
} }
...@@ -579,10 +584,14 @@ static int refdb_fs_backend__iterator_next_name( ...@@ -579,10 +584,14 @@ static int refdb_fs_backend__iterator_next_name(
giterr_clear(); giterr_clear();
} }
git_sortedcache_rlock(backend->refcache); if (!iter->cache) {
if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
return error;
}
while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) { error = GIT_ITEROVER;
ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++); while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */ if (!ref) /* stop now if another thread deleted refs and we past end */
break; break;
...@@ -596,7 +605,6 @@ static int refdb_fs_backend__iterator_next_name( ...@@ -596,7 +605,6 @@ static int refdb_fs_backend__iterator_next_name(
break; break;
} }
git_sortedcache_runlock(backend->refcache);
return error; return error;
} }
......
...@@ -186,3 +186,36 @@ void test_refs_iterator__foreach_name_can_cancel(void) ...@@ -186,3 +186,36 @@ void test_refs_iterator__foreach_name_can_cancel(void)
-333); -333);
cl_assert_equal_i(0, cancel_after); cl_assert_equal_i(0, cancel_after);
} }
void test_refs_iterator__concurrent_delete(void)
{
git_reference_iterator *iter;
size_t full_count = 0, concurrent_count = 0;
const char *name;
int error;
git_repository_free(repo);
repo = cl_git_sandbox_init("testrepo");
cl_git_pass(git_reference_iterator_new(&iter, repo));
while ((error = git_reference_next_name(&name, iter)) == 0) {
full_count++;
}
git_reference_iterator_free(iter);
cl_assert_equal_i(GIT_ITEROVER, error);
cl_git_pass(git_reference_iterator_new(&iter, repo));
while ((error = git_reference_next_name(&name, iter)) == 0) {
cl_git_pass(git_reference_remove(repo, name));
concurrent_count++;
}
git_reference_iterator_free(iter);
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert_equal_i(full_count, concurrent_count);
cl_git_sandbox_cleanup();
repo = NULL;
}
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