summaryrefslogtreecommitdiff
path: root/tests/libgit2/threads/refdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/libgit2/threads/refdb.c')
-rw-r--r--tests/libgit2/threads/refdb.c220
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
+}