Initial commit - full huishou project

This commit is contained in:
jiapengyu
2026-07-27 14:07:26 +08:00
commit 60790ad1b3
39127 changed files with 5989265 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
#include "clar_libgit2.h"
#include "thread_helpers.h"
#include "cache.h"
static git_repository *g_repo;
void test_threads_basic__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
}
void test_threads_basic__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_threads_basic__cache(void)
{
/* run several threads polling the cache at the same time */
cl_assert(1 == 1);
}
void test_threads_basic__multiple_init(void)
{
git_repository *nested_repo;
git_libgit2_init();
cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
git_repository_free(nested_repo);
git_libgit2_shutdown();
cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
git_repository_free(nested_repo);
}
static void *set_error(void *dummy)
{
git_error_set(GIT_ERROR_INVALID, "oh no, something happened!\n");
return dummy;
}
/* Set errors so we can check that we free it */
void test_threads_basic__set_error(void)
{
run_in_parallel(1, 4, set_error, NULL, NULL);
}
#ifdef GIT_THREADS
static void *return_normally(void *param)
{
return param;
}
#endif
void test_threads_basic__exit(void)
{
#ifndef GIT_THREADS
clar__skip();
#else
git_thread thread;
void *result;
/* Ensure that the return value of the threadproc is returned. */
cl_git_pass(git_thread_create(&thread, return_normally, (void *)424242));
cl_git_pass(git_thread_join(&thread, &result));
cl_assert_equal_sz(424242, (size_t)result);
/* Ensure that the return value of `git_thread_exit` is returned. */
cl_git_pass(git_thread_create(&thread, return_normally, (void *)232323));
cl_git_pass(git_thread_join(&thread, &result));
cl_assert_equal_sz(232323, (size_t)result);
#endif
}
+218
View File
@@ -0,0 +1,218 @@
#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;
#ifdef GIT_WIN32
static int _retries;
#endif
#define THREADS 20
void test_threads_diff__initialize(void)
{
#ifdef GIT_WIN32
_retries = git_win32__retries;
git_win32__retries = 1;
#endif
}
void test_threads_diff__cleanup(void)
{
cl_git_sandbox_cleanup();
#ifdef GIT_WIN32
git_win32__retries = _retries;
#endif
}
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_repository *repo;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff = NULL;
size_t i;
int exp[4] = { 0, 0, 0, 0 };
cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
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);
git_repository_free(repo);
git_error_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;
git_repository *repo;
cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
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);
git_repository_free(repo);
git_error_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);
}
+55
View File
@@ -0,0 +1,55 @@
#include "clar_libgit2.h"
#include "thread_helpers.h"
#include "iterator.h"
static git_repository *_repo;
void test_threads_iterator__cleanup(void)
{
cl_git_sandbox_cleanup();
}
static void *run_workdir_iterator(void *arg)
{
int error = 0;
git_repository *repo;
git_iterator *iter;
git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
const git_index_entry *entry = NULL;
iter_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
cl_git_pass(git_iterator_for_workdir(
&iter, repo, NULL, NULL, &iter_opts));
while (!error) {
if (entry && entry->mode == GIT_FILEMODE_TREE) {
error = git_iterator_advance_into(&entry, iter);
if (error == GIT_ENOTFOUND)
error = git_iterator_advance(&entry, iter);
} else {
error = git_iterator_advance(&entry, iter);
}
if (!error)
(void)git_iterator_current_is_ignored(iter);
}
cl_assert_equal_i(GIT_ITEROVER, error);
git_iterator_free(iter);
git_repository_free(repo);
git_error_clear();
return arg;
}
void test_threads_iterator__workdir(void)
{
_repo = cl_git_sandbox_init("status");
run_in_parallel(
1, 20, run_workdir_iterator, NULL, NULL);
}
+220
View File
@@ -0,0 +1,220 @@
#include "clar_libgit2.h"
#include "git2/refdb.h"
#include "refdb.h"
static git_repository *g_repo;
static int g_expected = 0;
#ifdef GIT_WIN32
static bool concurrent_compress = false;
#else
static bool concurrent_compress = true;
#endif
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
/* Number of references to create or delete in each thread */
#define NREFS 10
struct th_data {
cl_git_thread_err error;
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, error;
git_repository *repo;
cl_git_thread_pass(data, git_repository_open(&repo, data->path));
do {
error = git_reference_iterator_new(&i, repo);
} while (error == GIT_ELOCKED);
cl_git_thread_pass(data, error);
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);
git_error_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[NREFS];
git_repository *repo;
cl_git_thread_pass(data, git_repository_open(&repo, data->path));
do {
error = git_reference_name_to_id(&head, repo, "HEAD");
} while (error == GIT_ELOCKED);
cl_git_thread_pass(data, error);
for (i = 0; i < NREFS; ++i) {
p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
do {
error = git_reference_create(&ref[i], repo, name, &head, 0, NULL);
} while (error == GIT_ELOCKED);
cl_git_thread_pass(data, error);
if (concurrent_compress && i == NREFS/2) {
git_refdb *refdb;
cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
do {
error = git_refdb_compress(refdb);
} while (error == GIT_ELOCKED);
cl_git_thread_pass(data, error);
git_refdb_free(refdb);
}
}
for (i = 0; i < NREFS; ++i)
git_reference_free(ref[i]);
git_repository_free(repo);
git_error_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_thread_pass(data, git_repository_open(&repo, data->path));
for (i = 0; i < NREFS; ++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);
/* Sometimes we race with other deleter threads */
if (error == GIT_ENOTFOUND)
error = 0;
cl_git_thread_pass(data, error);
git_reference_free(ref);
}
if (concurrent_compress && i == NREFS/2) {
git_refdb *refdb;
cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
do {
error = git_refdb_compress(refdb);
} while (error == GIT_ELOCKED);
cl_git_thread_pass(data, error);
git_refdb_free(refdb);
}
}
git_repository_free(repo);
git_error_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;
#ifdef GIT_THREADS
git_thread th[THREADS];
#endif
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) {
p_snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL));
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;
}
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
}
#ifdef GIT_THREADS
for (t = 0; t < THREADS; ++t) {
cl_git_pass(git_thread_join(&th[t], NULL));
cl_git_thread_check(&th_data[t]);
}
memset(th, 0, sizeof(th));
for (t = 0; t < THREADS; ++t) {
th_data[t].id = t;
cl_git_pass(git_thread_create(&th[t], iterate_refs, &th_data[t]));
}
for (t = 0; t < THREADS; ++t) {
cl_git_pass(git_thread_join(&th[t], NULL));
cl_git_thread_check(&th_data[t]);
}
#endif
}
+44
View File
@@ -0,0 +1,44 @@
#include "clar_libgit2.h"
#include "thread_helpers.h"
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));
cl_assert(th != NULL);
#else
void *th = NULL;
#endif
cl_assert(id != NULL);
for (r = 0; r < repeats; ++r) {
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], 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);
}
+8
View File
@@ -0,0 +1,8 @@
#include "thread-utils.h"
void run_in_parallel(
int repeats,
int threads,
void *(*func)(void *),
void (*before_test)(void),
void (*after_test)(void));