diff options
Diffstat (limited to 'tests/libgit2/threads/refdb.c')
-rw-r--r-- | tests/libgit2/threads/refdb.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/tests/libgit2/threads/refdb.c b/tests/libgit2/threads/refdb.c new file mode 100644 index 000000000..a4630df6a --- /dev/null +++ b/tests/libgit2/threads/refdb.c @@ -0,0 +1,220 @@ +#include "clar_libgit2.h" +#include "git2/refdb.h" +#include "refdb.h" + +static git_repository *g_repo; +static int g_expected = 0; + +#ifdef GIT_WIN32 +static bool concurrent_compress = false; +#else +static bool concurrent_compress = true; +#endif + +void test_threads_refdb__initialize(void) +{ + g_repo = NULL; +} + +void test_threads_refdb__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +#define REPEAT 20 +#define THREADS 20 +/* Number of references to create or delete in each thread */ +#define NREFS 10 + +struct th_data { + cl_git_thread_err error; + int id; + const char *path; +}; + +static void *iterate_refs(void *arg) +{ + struct th_data *data = (struct th_data *) arg; + git_reference_iterator *i; + git_reference *ref; + int count = 0, error; + git_repository *repo; + + cl_git_thread_pass(data, git_repository_open(&repo, data->path)); + do { + error = git_reference_iterator_new(&i, repo); + } while (error == GIT_ELOCKED); + cl_git_thread_pass(data, error); + + for (count = 0; !git_reference_next(&ref, i); ++count) { + cl_assert(ref != NULL); + git_reference_free(ref); + } + + if (g_expected > 0) + cl_assert_equal_i(g_expected, count); + + git_reference_iterator_free(i); + + git_repository_free(repo); + git_error_clear(); + return arg; +} + +static void *create_refs(void *arg) +{ + int i, error; + struct th_data *data = (struct th_data *) arg; + git_oid head; + char name[128]; + git_reference *ref[NREFS]; + git_repository *repo; + + cl_git_thread_pass(data, git_repository_open(&repo, data->path)); + + do { + error = git_reference_name_to_id(&head, repo, "HEAD"); + } while (error == GIT_ELOCKED); + cl_git_thread_pass(data, error); + + for (i = 0; i < NREFS; ++i) { + p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i); + do { + error = git_reference_create(&ref[i], repo, name, &head, 0, NULL); + } while (error == GIT_ELOCKED); + cl_git_thread_pass(data, error); + + if (concurrent_compress && i == NREFS/2) { + git_refdb *refdb; + cl_git_thread_pass(data, git_repository_refdb(&refdb, repo)); + do { + error = git_refdb_compress(refdb); + } while (error == GIT_ELOCKED); + cl_git_thread_pass(data, error); + git_refdb_free(refdb); + } + } + + for (i = 0; i < NREFS; ++i) + git_reference_free(ref[i]); + + git_repository_free(repo); + + git_error_clear(); + return arg; +} + +static void *delete_refs(void *arg) +{ + int i, error; + struct th_data *data = (struct th_data *) arg; + git_reference *ref; + char name[128]; + git_repository *repo; + + cl_git_thread_pass(data, git_repository_open(&repo, data->path)); + + for (i = 0; i < NREFS; ++i) { + p_snprintf( + name, sizeof(name), "refs/heads/thread-%03d-%02d", (data->id) & ~0x3, i); + + if (!git_reference_lookup(&ref, repo, name)) { + do { + error = git_reference_delete(ref); + } while (error == GIT_ELOCKED); + /* Sometimes we race with other deleter threads */ + if (error == GIT_ENOTFOUND) + error = 0; + + cl_git_thread_pass(data, error); + git_reference_free(ref); + } + + if (concurrent_compress && i == NREFS/2) { + git_refdb *refdb; + cl_git_thread_pass(data, git_repository_refdb(&refdb, repo)); + do { + error = git_refdb_compress(refdb); + } while (error == GIT_ELOCKED); + cl_git_thread_pass(data, error); + git_refdb_free(refdb); + } + } + + git_repository_free(repo); + git_error_clear(); + return arg; +} + +void test_threads_refdb__edit_while_iterate(void) +{ + int r, t; + struct th_data th_data[THREADS]; + git_oid head; + git_reference *ref; + char name[128]; + git_refdb *refdb; + +#ifdef GIT_THREADS + git_thread th[THREADS]; +#endif + + g_repo = cl_git_sandbox_init("testrepo2"); + + cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); + + /* make a bunch of references */ + + for (r = 0; r < 50; ++r) { + p_snprintf(name, sizeof(name), "refs/heads/starter-%03d", r); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL)); + git_reference_free(ref); + } + + cl_git_pass(git_repository_refdb(&refdb, g_repo)); + cl_git_pass(git_refdb_compress(refdb)); + git_refdb_free(refdb); + + g_expected = -1; + + g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */ + + for (t = 0; t < THREADS; ++t) { + void *(*fn)(void *arg); + + switch (t & 0x3) { + case 0: fn = create_refs; break; + case 1: fn = delete_refs; break; + default: fn = iterate_refs; break; + } + + th_data[t].id = t; + th_data[t].path = git_repository_path(g_repo); + +#ifdef GIT_THREADS + cl_git_pass(git_thread_create(&th[t], fn, &th_data[t])); +#else + fn(&th_data[t]); +#endif + } + +#ifdef GIT_THREADS + for (t = 0; t < THREADS; ++t) { + cl_git_pass(git_thread_join(&th[t], NULL)); + cl_git_thread_check(&th_data[t]); + } + + memset(th, 0, sizeof(th)); + + for (t = 0; t < THREADS; ++t) { + th_data[t].id = t; + cl_git_pass(git_thread_create(&th[t], iterate_refs, &th_data[t])); + } + + for (t = 0; t < THREADS; ++t) { + cl_git_pass(git_thread_join(&th[t], NULL)); + cl_git_thread_check(&th_data[t]); + } +#endif +} |