<commit_before>#include "clar_libgit2.h"
#include "git2/refdb.h"
#include "refdb.h"

static git_repository *g_repo;
static int g_expected = 0;

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

struct th_data {
	int id;
	const char *path;
};

static void *iterate_refs(void *arg)
{
	struct th_data *data = (struct th_data *) arg;
	git_reference_iterator *i;
	git_reference *ref;
	int count = 0;
	git_repository *repo;

	cl_git_pass(git_repository_open(&repo, data->path));
	cl_git_pass(git_reference_iterator_new(&i, repo));

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

	git_repository_free(repo);
	giterr_clear();
	return arg;
}

static void *create_refs(void *arg)
{
	int i;
	struct th_data *data = (struct th_data *) arg;
	git_oid head;
	char name[128];
	git_reference *ref[10];
	git_repository *repo;

	cl_git_pass(git_repository_open(&repo, data->path));

	cl_git_pass(git_reference_name_to_id(&head, repo, "HEAD"));

	for (i = 0; i < 10; ++i) {
		p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
		cl_git_pass(git_reference_create(&ref[i], repo, name, &head, 0, NULL));

		if (i == 5) {
			git_refdb *refdb;
			cl_git_pass(git_repository_refdb(&refdb, repo));
			cl_git_pass(git_refdb_compress(refdb));
			git_refdb_free(refdb);
		}
	}

	for (i = 0; i < 10; ++i)
		git_reference_free(ref[i]);

	git_repository_free(repo);

	giterr_clear();
	return arg;
}

static void *delete_refs(void *arg)
{
	int i;
	struct th_data *data = (struct th_data *) arg;
	git_reference *ref;
	char name[128];
	git_repository *repo;

	cl_git_pass(git_repository_open(&repo, data->path));

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

		if (!git_reference_lookup(&ref, repo, name)) {
			cl_git_pass(git_reference_delete(ref));
			git_reference_free(ref);
		}

		if (i == 5) {
			git_refdb *refdb;
			cl_git_pass(git_repository_refdb(&refdb, repo));
			cl_git_pass(git_refdb_compress(refdb));
			git_refdb_free(refdb);
		}
	}

	git_repository_free(repo);
	giterr_clear();
	return arg;
}

void test_threads_refdb__edit_while_iterate(void)
{
	int r, t;
	struct th_data th_data[THREADS];
	git_oid head;
	git_reference *ref;
	char name[128];
	git_refdb *refdb;<commit_msg>refdb: add retry logic to the threaded tests<commit_after>#include "clar_libgit2.h"
#include "git2/refdb.h"
#include "refdb.h"

static git_repository *g_repo;
static int g_expected = 0;

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

struct th_data {
	int id;
	const char *path;
};

static void *iterate_refs(void *arg)
{
	struct th_data *data = (struct th_data *) arg;
	git_reference_iterator *i;
	git_reference *ref;
	int count = 0;
	git_repository *repo;

	cl_git_pass(git_repository_open(&repo, data->path));
	cl_git_pass(git_reference_iterator_new(&i, repo));

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

	git_repository_free(repo);
	giterr_clear();
	return arg;
}

static void *create_refs(void *arg)
{
	int i, error;
	struct th_data *data = (struct th_data *) arg;
	git_oid head;
	char name[128];
	git_reference *ref[10];
	git_repository *repo;

	cl_git_pass(git_repository_open(&repo, data->path));

	cl_git_pass(git_reference_name_to_id(&head, repo, "HEAD"));

	for (i = 0; i < 10; ++i) {
		p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
		cl_git_pass(git_reference_create(&ref[i], repo, name, &head, 0, NULL));

		if (i == 5) {
			git_refdb *refdb;
			cl_git_pass(git_repository_refdb(&refdb, repo));
			do {
				error = git_refdb_compress(refdb);
			} while (error == GIT_ELOCKED);
			git_refdb_free(refdb);
		}
	}

	for (i = 0; i < 10; ++i)
		git_reference_free(ref[i]);

	git_repository_free(repo);

	giterr_clear();
	return arg;
}

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

	cl_git_pass(git_repository_open(&repo, data->path));

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

		if (!git_reference_lookup(&ref, repo, name)) {
			do {
				error = git_reference_delete(ref);
			} while (error == GIT_ELOCKED);
			cl_git_pass(error);
			git_reference_free(ref);
		}

		if (i == 5) {
			git_refdb *refdb;
			cl_git_pass(git_repository_refdb(&refdb, repo));
			do {
				error = git_refdb_compress(refdb);
			} while (error == GIT_ELOCKED);
			cl_git_pass(error);
			git_refdb_free(refdb);
		}
	}

	git_repository_free(repo);
	giterr_clear();
	return arg;
}

void test_threads_refdb__edit_while_iterate(void)
{
	int r, t;
	struct th_data th_data[THREADS];
	git_oid head;
	git_reference *ref;
	char name[128];
	git_refdb *refdb;
{'fraction_non_alphanumeric': 0.14487034949267194, 'fraction_numerical': 0.01615933859451334, 'mean_word_length': 5.07648401826484, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '474951', 'n_tokens_mistral': 2366, 'n_tokens_neox': 2162, 'n_words': 439}