libgit2/tests/threads/diff.c
Russell Belfer 40ed499039 Add diff threading tests and attr file cache locks
This adds a basic test of doing simultaneous diffs on multiple
threads and adds basic locking for the attr file cache because
that was the immediate problem that arose from these tests.
2014-04-17 14:43:45 -07:00

153 lines
3.7 KiB
C

#include "clar_libgit2.h"
#include "thread-utils.h"
static git_repository *g_repo;
static git_tree *a, *b;
static git_atomic counts[4];
void test_threads_diff__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void run_in_parallel(
int repeats, int threads, void *(*func)(void *),
void (*before_test)(void), void (*after_test)(void))
{
int r, t, *id = git__calloc(threads, sizeof(int));
#ifdef GIT_THREADS
git_thread *th = git__calloc(threads, sizeof(git_thread));
#else
void *th = NULL;
#endif
cl_assert(id != NULL && th != NULL);
for (r = 0; r < repeats; ++r) {
g_repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */
if (before_test) before_test();
for (t = 0; t < threads; ++t) {
id[t] = t;
#ifdef GIT_THREADS
cl_git_pass(git_thread_create(&th[t], NULL, func, &id[t]));
#else
cl_assert(func(&id[t]) == &id[t]);
#endif
}
#ifdef GIT_THREADS
for (t = 0; t < threads; ++t)
cl_git_pass(git_thread_join(th[t], NULL));
memset(th, 0, threads * sizeof(git_thread));
#endif
if (after_test) after_test();
}
git__free(id);
git__free(th);
}
static void setup_trees(void)
{
cl_git_pass(git_revparse_single(
(git_object **)&a, g_repo, "0017bd4ab1^{tree}"));
cl_git_pass(git_revparse_single(
(git_object **)&b, g_repo, "26a125ee1b^{tree}"));
memset(counts, 0, sizeof(counts));
}
#define THREADS 20
static void free_trees(void)
{
git_tree_free(a); a = NULL;
git_tree_free(b); b = NULL;
cl_assert_equal_i(288, git_atomic_get(&counts[0]));
cl_assert_equal_i(112, git_atomic_get(&counts[1]));
cl_assert_equal_i( 80, git_atomic_get(&counts[2]));
cl_assert_equal_i( 96, git_atomic_get(&counts[3]));
}
static void *run_index_diffs(void *arg)
{
int thread = *(int *)arg;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
size_t i;
int exp[4] = { 0, 0, 0, 0 };
// fprintf(stderr, "%d >>>\n", thread);
switch (thread & 0x03) {
case 0: /* diff index to workdir */;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
break;
case 1: /* diff tree 'a' to index */;
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
break;
case 2: /* diff tree 'b' to index */;
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
break;
case 3: /* diff index to workdir (explicit index) */;
{
git_index *idx;
cl_git_pass(git_repository_index(&idx, g_repo));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, idx, &opts));
git_index_free(idx);
break;
}
}
// fprintf(stderr, "%d <<<\n", thread);
/* keep some diff stats to make sure results are as expected */
i = git_diff_num_deltas(diff);
git_atomic_add(&counts[0], (int32_t)i);
exp[0] = (int)i;
while (i > 0) {
switch (git_diff_get_delta(diff, --i)->status) {
case GIT_DELTA_MODIFIED: exp[1]++; git_atomic_inc(&counts[1]); break;
case GIT_DELTA_ADDED: exp[2]++; git_atomic_inc(&counts[2]); break;
case GIT_DELTA_DELETED: exp[3]++; git_atomic_inc(&counts[3]); break;
default: break;
}
}
// fprintf(stderr, "%2d: [%d] total %d (M %d A %d D %d)\n",
// thread, (int)(thread & 0x03), exp[0], exp[1], exp[2], exp[3]);
switch (thread & 0x03) {
case 0: case 3:
cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(4, exp[1]);
cl_assert_equal_i(0, exp[2]); cl_assert_equal_i(4, exp[3]);
break;
case 1:
cl_assert_equal_i(12, exp[0]); cl_assert_equal_i(3, exp[1]);
cl_assert_equal_i(7, exp[2]); cl_assert_equal_i(2, exp[3]);
break;
case 2:
cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(3, exp[1]);
cl_assert_equal_i(3, exp[2]); cl_assert_equal_i(2, exp[3]);
break;
}
git_diff_free(diff);
return arg;
}
void test_threads_diff__concurrent_diffs(void)
{
g_repo = cl_git_sandbox_init("status");
run_in_parallel(
20, 32, run_index_diffs, setup_trees, free_trees);
}