mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-10 08:48:47 +00:00

POSIX emulation retries should be configurable so that tests can disable them. In particular, maniacally threading tests may end up trying to open locked files and need retries, which will slow continuous integration tests significantly.
205 lines
5.0 KiB
C
205 lines
5.0 KiB
C
#include "clar_libgit2.h"
|
|
#include "thread_helpers.h"
|
|
|
|
#ifdef GIT_THREADS
|
|
|
|
# if defined(GIT_WIN32)
|
|
# define git_thread_yield() Sleep(0)
|
|
# elif defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__DragonFly__)
|
|
# define git_thread_yield() pthread_yield()
|
|
# else
|
|
# define git_thread_yield() sched_yield()
|
|
# endif
|
|
|
|
#else
|
|
# define git_thread_yield() (void)0
|
|
#endif
|
|
|
|
static git_repository *_repo;
|
|
static git_tree *_a, *_b;
|
|
static git_atomic _counts[4];
|
|
static int _check_counts;
|
|
static int _retries;
|
|
|
|
#define THREADS 20
|
|
|
|
void test_threads_diff__initialize(void)
|
|
{
|
|
_retries = git_win32__retries;
|
|
git_win32__retries = 1;
|
|
}
|
|
|
|
void test_threads_diff__cleanup(void)
|
|
{
|
|
cl_git_sandbox_cleanup();
|
|
git_win32__retries = _retries;
|
|
}
|
|
|
|
static void setup_trees(void)
|
|
{
|
|
git_index *idx;
|
|
|
|
_repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */
|
|
|
|
/* avoid competing to load initial index */
|
|
cl_git_pass(git_repository_index(&idx, _repo));
|
|
git_index_free(idx);
|
|
|
|
cl_git_pass(git_revparse_single(
|
|
(git_object **)&_a, _repo, "0017bd4ab1^{tree}"));
|
|
cl_git_pass(git_revparse_single(
|
|
(git_object **)&_b, _repo, "26a125ee1b^{tree}"));
|
|
|
|
memset(_counts, 0, sizeof(_counts));
|
|
}
|
|
|
|
static void free_trees(void)
|
|
{
|
|
git_tree_free(_a); _a = NULL;
|
|
git_tree_free(_b); _b = NULL;
|
|
|
|
if (_check_counts) {
|
|
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 };
|
|
|
|
switch (thread & 0x03) {
|
|
case 0: /* diff index to workdir */;
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, NULL, &opts));
|
|
break;
|
|
case 1: /* diff tree 'a' to index */;
|
|
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, NULL, &opts));
|
|
break;
|
|
case 2: /* diff tree 'b' to index */;
|
|
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, NULL, &opts));
|
|
break;
|
|
case 3: /* diff index to workdir (explicit index) */;
|
|
{
|
|
git_index *idx;
|
|
cl_git_pass(git_repository_index(&idx, _repo));
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
|
|
git_index_free(idx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
giterr_clear();
|
|
|
|
return arg;
|
|
}
|
|
|
|
void test_threads_diff__concurrent_diffs(void)
|
|
{
|
|
_repo = cl_git_sandbox_init("status");
|
|
_check_counts = 1;
|
|
|
|
run_in_parallel(
|
|
5, 32, run_index_diffs, setup_trees, free_trees);
|
|
}
|
|
|
|
static void *run_index_diffs_with_modifier(void *arg)
|
|
{
|
|
int thread = *(int *)arg;
|
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
|
git_diff *diff = NULL;
|
|
git_index *idx = NULL;
|
|
|
|
cl_git_pass(git_repository_index(&idx, _repo));
|
|
|
|
/* have first thread altering the index as we go */
|
|
if (thread == 0) {
|
|
int i;
|
|
|
|
for (i = 0; i < 300; ++i) {
|
|
switch (i & 0x03) {
|
|
case 0: (void)git_index_add_bypath(idx, "new_file"); break;
|
|
case 1: (void)git_index_remove_bypath(idx, "modified_file"); break;
|
|
case 2: (void)git_index_remove_bypath(idx, "new_file"); break;
|
|
case 3: (void)git_index_add_bypath(idx, "modified_file"); break;
|
|
}
|
|
git_thread_yield();
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
/* only use explicit index in this test to prevent reloading */
|
|
|
|
switch (thread & 0x03) {
|
|
case 0: /* diff index to workdir */;
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
|
|
break;
|
|
case 1: /* diff tree 'a' to index */;
|
|
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, idx, &opts));
|
|
break;
|
|
case 2: /* diff tree 'b' to index */;
|
|
cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, idx, &opts));
|
|
break;
|
|
case 3: /* diff index to workdir reversed */;
|
|
opts.flags |= GIT_DIFF_REVERSE;
|
|
cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
|
|
break;
|
|
}
|
|
|
|
/* results will be unpredictable with index modifier thread running */
|
|
|
|
git_diff_free(diff);
|
|
|
|
done:
|
|
git_index_free(idx);
|
|
giterr_clear();
|
|
|
|
return arg;
|
|
}
|
|
|
|
void test_threads_diff__with_concurrent_index_modified(void)
|
|
{
|
|
_repo = cl_git_sandbox_init("status");
|
|
_check_counts = 0;
|
|
|
|
run_in_parallel(
|
|
5, 16, run_index_diffs_with_modifier, setup_trees, free_trees);
|
|
}
|