diff.c 4.89 KB
Newer Older
1
#include "clar_libgit2.h"
2
#include "thread_helpers.h"
3

4 5 6 7 8 9 10 11 12 13 14 15 16 17
#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

18 19 20 21
static git_repository *_repo;
static git_tree *_a, *_b;
static git_atomic _counts[4];
static int _check_counts;
22

23 24
#define THREADS 20

25 26 27 28 29 30 31
void test_threads_diff__cleanup(void)
{
	cl_git_sandbox_cleanup();
}

static void setup_trees(void)
{
32 33
	git_index *idx;

34 35
	_repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */

36 37 38 39
	/* avoid competing to load initial index */
	cl_git_pass(git_repository_index(&idx, _repo));
	git_index_free(idx);

40
	cl_git_pass(git_revparse_single(
41
		(git_object **)&_a, _repo, "0017bd4ab1^{tree}"));
42
	cl_git_pass(git_revparse_single(
43
		(git_object **)&_b, _repo, "26a125ee1b^{tree}"));
44

45
	memset(_counts, 0, sizeof(_counts));
46 47 48 49
}

static void free_trees(void)
{
50 51 52 53 54 55 56 57 58
	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]));
	}
59 60 61 62 63 64 65 66 67 68 69 70
}

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 */;
71
		cl_git_pass(git_diff_index_to_workdir(&diff, _repo, NULL, &opts));
72 73
		break;
	case 1: /* diff tree 'a' to index */;
74
		cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, NULL, &opts));
75 76
		break;
	case 2: /* diff tree 'b' to index */;
77
		cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, NULL, &opts));
78 79 80 81
		break;
	case 3: /* diff index to workdir (explicit index) */;
		{
			git_index *idx;
82 83
			cl_git_pass(git_repository_index(&idx, _repo));
			cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts));
84 85 86 87 88 89 90 91
			git_index_free(idx);
			break;
		}
	}

	/* keep some diff stats to make sure results are as expected */

	i = git_diff_num_deltas(diff);
92
	git_atomic_add(&_counts[0], (int32_t)i);
93 94 95 96
	exp[0] = (int)i;

	while (i > 0) {
		switch (git_diff_get_delta(diff, --i)->status) {
97 98 99
		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;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
		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);
Russell Belfer committed
120
	giterr_clear();
121 122 123 124 125 126

	return arg;
}

void test_threads_diff__concurrent_diffs(void)
{
127 128
	_repo = cl_git_sandbox_init("status");
	_check_counts = 1;
129 130

	run_in_parallel(
131
		5, 32, run_index_diffs, setup_trees, free_trees);
132
}
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

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();
		}

Russell Belfer committed
157
		goto done;
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
	}

	/* 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);
Russell Belfer committed
181 182

done:
183
	git_index_free(idx);
Russell Belfer committed
184
	giterr_clear();
185 186 187 188 189 190 191 192 193 194

	return arg;
}

void test_threads_diff__with_concurrent_index_modified(void)
{
	_repo = cl_git_sandbox_init("status");
	_check_counts = 0;

	run_in_parallel(
195
		5, 16, run_index_diffs_with_modifier, setup_trees, free_trees);
196
}