refdb.c 4.73 KB
Newer Older
1 2 3 4 5 6 7
#include "clar_libgit2.h"
#include "git2/refdb.h"
#include "refdb.h"

static git_repository *g_repo;
static int g_expected = 0;

8 9 10 11 12 13
#ifdef GIT_WIN32
static bool concurrent_compress = false;
#else
static bool concurrent_compress = true;
#endif

14 15 16 17 18 19 20 21 22 23 24 25 26
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
27 28
/* Number of references to create or delete in each thread */
#define NREFS 10
29

30
struct th_data {
31
	cl_git_thread_err error;
32 33 34 35
	int id;
	const char *path;
};

36 37
static void *iterate_refs(void *arg)
{
38
	struct th_data *data = (struct th_data *) arg;
39 40
	git_reference_iterator *i;
	git_reference *ref;
41
	int count = 0, error;
42
	git_repository *repo;
43

44
	cl_git_thread_pass(data, git_repository_open(&repo, data->path));
45 46 47
	do {
		error = git_reference_iterator_new(&i, repo);
	} while (error == GIT_ELOCKED);
48
	cl_git_thread_pass(data, error);
49 50 51 52 53 54 55 56 57 58 59

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

60
	git_repository_free(repo);
61
	git_error_clear();
62 63 64 65 66
	return arg;
}

static void *create_refs(void *arg)
{
67
	int i, error;
68
	struct th_data *data = (struct th_data *) arg;
69 70
	git_oid head;
	char name[128];
71
	git_reference *ref[NREFS];
72
	git_repository *repo;
73

74
	cl_git_thread_pass(data, git_repository_open(&repo, data->path));
75

76 77 78
	do {
		error = git_reference_name_to_id(&head, repo, "HEAD");
	} while (error == GIT_ELOCKED);
79
	cl_git_thread_pass(data, error);
80

81
	for (i = 0; i < NREFS; ++i) {
82
		p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
83 84 85
		do {
			error = git_reference_create(&ref[i], repo, name, &head, 0, NULL);
		} while (error == GIT_ELOCKED);
86
		cl_git_thread_pass(data, error);
87

88
		if (concurrent_compress && i == NREFS/2) {
89
			git_refdb *refdb;
90
			cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
91 92 93
			do {
				error = git_refdb_compress(refdb);
			} while (error == GIT_ELOCKED);
94
			cl_git_thread_pass(data, error);
95 96 97 98
			git_refdb_free(refdb);
		}
	}

99
	for (i = 0; i < NREFS; ++i)
100 101
		git_reference_free(ref[i]);

102 103
	git_repository_free(repo);

104
	git_error_clear();
105 106 107 108 109
	return arg;
}

static void *delete_refs(void *arg)
{
110
	int i, error;
111
	struct th_data *data = (struct th_data *) arg;
112 113
	git_reference *ref;
	char name[128];
114 115
	git_repository *repo;

116
	cl_git_thread_pass(data, git_repository_open(&repo, data->path));
117

118
	for (i = 0; i < NREFS; ++i) {
119
		p_snprintf(
120
			name, sizeof(name), "refs/heads/thread-%03d-%02d", (data->id) & ~0x3, i);
121

122
		if (!git_reference_lookup(&ref, repo, name)) {
123 124 125
			do {
				error = git_reference_delete(ref);
			} while (error == GIT_ELOCKED);
126 127 128 129
			/* Sometimes we race with other deleter threads */
			if (error == GIT_ENOTFOUND)
				error = 0;

130
			cl_git_thread_pass(data, error);
131
			git_reference_free(ref);
132 133
		}

134
		if (concurrent_compress && i == NREFS/2) {
135
			git_refdb *refdb;
136
			cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
137 138 139
			do {
				error = git_refdb_compress(refdb);
			} while (error == GIT_ELOCKED);
140
			cl_git_thread_pass(data, error);
141 142 143 144
			git_refdb_free(refdb);
		}
	}

145
	git_repository_free(repo);
146
	git_error_clear();
147 148 149 150 151 152
	return arg;
}

void test_threads_refdb__edit_while_iterate(void)
{
	int r, t;
153
	struct th_data th_data[THREADS];
154 155 156 157 158
	git_oid head;
	git_reference *ref;
	char name[128];
	git_refdb *refdb;

159 160 161 162
#ifdef GIT_THREADS
	git_thread th[THREADS];
#endif

163 164 165 166 167 168 169
	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) {
170
		p_snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
171
		cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL));
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
		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;
		}

192 193 194 195 196 197 198 199
		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
200 201
	}

202
#ifdef GIT_THREADS
203 204
	for (t = 0; t < THREADS; ++t) {
		cl_git_pass(git_thread_join(&th[t], NULL));
205
		cl_git_thread_check(&th_data[t]);
206
	}
207 208 209 210

	memset(th, 0, sizeof(th));

	for (t = 0; t < THREADS; ++t) {
211 212
		th_data[t].id = t;
		cl_git_pass(git_thread_create(&th[t], iterate_refs, &th_data[t]));
213 214 215
	}

	for (t = 0; t < THREADS; ++t) {
216
		cl_git_pass(git_thread_join(&th[t], NULL));
217
		cl_git_thread_check(&th_data[t]);
218
	}
219
#endif
220
}