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