use libgrite_git::LockManager;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc, Barrier,
};
use std::thread;
use tempfile::tempdir;
fn init_git_repo(path: &std::path::Path) {
let repo = git2::Repository::init(path).expect("Failed to init git repo");
let sig = git2::Signature::now("Test", "test@example.com").unwrap();
let tree_id = repo.index().unwrap().write_tree().unwrap();
let tree = repo.find_tree(tree_id).unwrap();
repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[])
.expect("Failed to create initial commit");
}
#[test]
fn test_concurrent_lock_acquisition_same_resource() {
let dir = tempdir().unwrap();
let git_dir = dir.path().join(".git");
init_git_repo(dir.path());
let git_dir = Arc::new(git_dir);
let resource = "issue:test123";
let num_threads = 8;
let barrier = Arc::new(Barrier::new(num_threads));
let success_counter = Arc::new(AtomicUsize::new(0));
let handles: Vec<_> = (0..num_threads)
.map(|thread_id| {
let git_dir = Arc::clone(&git_dir);
let barrier = Arc::clone(&barrier);
let success_counter = Arc::clone(&success_counter);
let actor = format!("actor-{:02}", thread_id);
thread::spawn(move || {
let lock_manager =
LockManager::open(&git_dir).expect("Failed to open lock manager");
barrier.wait();
if let Ok(_lock) = lock_manager.acquire(resource, &actor, None) {
thread::sleep(std::time::Duration::from_millis(10));
let _ = lock_manager.release(resource, &actor);
success_counter.fetch_add(1, Ordering::SeqCst);
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let successes = success_counter.load(Ordering::SeqCst);
assert!(
successes >= 1,
"Expected at least 1 success, got {}",
successes
);
println!(
"Lock same-resource test: {} threads got the lock",
successes
);
}
#[test]
fn test_concurrent_lock_acquisition_different_resources() {
let dir = tempdir().unwrap();
let git_dir = dir.path().join(".git");
init_git_repo(dir.path());
let git_dir = Arc::new(git_dir);
let num_threads = 8;
let barrier = Arc::new(Barrier::new(num_threads));
let success_counter = Arc::new(AtomicUsize::new(0));
let handles: Vec<_> = (0..num_threads)
.map(|thread_id| {
let git_dir = Arc::clone(&git_dir);
let barrier = Arc::clone(&barrier);
let success_counter = Arc::clone(&success_counter);
let actor = format!("actor-{:02}", thread_id);
let resource = format!("issue:test{:02}", thread_id);
thread::spawn(move || {
let lock_manager =
LockManager::open(&git_dir).expect("Failed to open lock manager");
barrier.wait();
if let Ok(_lock) = lock_manager.acquire(&resource, &actor, None) {
thread::sleep(std::time::Duration::from_millis(5));
let _ = lock_manager.release(&resource, &actor);
success_counter.fetch_add(1, Ordering::SeqCst);
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let successes = success_counter.load(Ordering::SeqCst);
assert!(
successes >= num_threads / 2,
"Expected at least {} successes, got {}",
num_threads / 2,
successes
);
println!(
"Lock different-resources test: {}/{} succeeded",
successes, num_threads
);
}
#[test]
fn test_lock_acquire_release_cycle() {
let dir = tempdir().unwrap();
let git_dir = dir.path().join(".git");
init_git_repo(dir.path());
let git_dir = Arc::new(git_dir);
let resource = "issue:shared";
let num_threads = 4;
let cycles_per_thread = 5; let barrier = Arc::new(Barrier::new(num_threads));
let total_acquired = Arc::new(AtomicUsize::new(0));
let handles: Vec<_> = (0..num_threads)
.map(|thread_id| {
let git_dir = Arc::clone(&git_dir);
let barrier = Arc::clone(&barrier);
let total_acquired = Arc::clone(&total_acquired);
let actor = format!("actor-{:02}", thread_id);
thread::spawn(move || {
let lock_manager =
LockManager::open(&git_dir).expect("Failed to open lock manager");
barrier.wait();
for _ in 0..cycles_per_thread {
if lock_manager.acquire(resource, &actor, None).is_ok() {
total_acquired.fetch_add(1, Ordering::SeqCst);
thread::sleep(std::time::Duration::from_millis(5));
let _ = lock_manager.release(resource, &actor);
}
thread::sleep(std::time::Duration::from_millis(2));
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let acquired = total_acquired.load(Ordering::SeqCst);
assert!(acquired > 0, "No lock acquisitions succeeded");
println!(
"Lock cycle test: {} total acquisitions across {} threads",
acquired, num_threads
);
}
#[test]
fn test_lock_list_during_operations() {
let dir = tempdir().unwrap();
let git_dir = dir.path().join(".git");
init_git_repo(dir.path());
let git_dir = Arc::new(git_dir);
let barrier = Arc::new(Barrier::new(3));
let acquire_success = Arc::new(AtomicUsize::new(0));
let list_success = Arc::new(AtomicUsize::new(0));
let gc_success = Arc::new(AtomicUsize::new(0));
let gd1 = Arc::clone(&git_dir);
let b1 = Arc::clone(&barrier);
let as1 = Arc::clone(&acquire_success);
let acquire_handle = thread::spawn(move || {
let lm = LockManager::open(&gd1).expect("Failed to open lock manager");
b1.wait();
for i in 0..10 {
let resource = format!("issue:list-test-{}", i);
let actor = "list-actor";
if lm.acquire(&resource, actor, Some(100)).is_ok() {
as1.fetch_add(1, Ordering::SeqCst);
}
}
});
let gd2 = Arc::clone(&git_dir);
let b2 = Arc::clone(&barrier);
let ls2 = Arc::clone(&list_success);
let list_handle = thread::spawn(move || {
let lm = LockManager::open(&gd2).expect("Failed to open lock manager");
b2.wait();
for _ in 0..20 {
if lm.list_locks().is_ok() {
ls2.fetch_add(1, Ordering::SeqCst);
}
thread::sleep(std::time::Duration::from_millis(5));
}
});
let gd3 = Arc::clone(&git_dir);
let b3 = Arc::clone(&barrier);
let gs3 = Arc::clone(&gc_success);
let gc_handle = thread::spawn(move || {
let lm = LockManager::open(&gd3).expect("Failed to open lock manager");
b3.wait();
for _ in 0..5 {
thread::sleep(std::time::Duration::from_millis(30));
if lm.gc().is_ok() {
gs3.fetch_add(1, Ordering::SeqCst);
}
}
});
acquire_handle.join().unwrap();
list_handle.join().unwrap();
gc_handle.join().unwrap();
let acquires = acquire_success.load(Ordering::SeqCst);
let lists = list_success.load(Ordering::SeqCst);
let gcs = gc_success.load(Ordering::SeqCst);
assert!(acquires > 0, "No locks acquired");
assert!(lists > 0, "No list operations succeeded");
assert!(gcs > 0, "No GC runs succeeded");
println!(
"Lock operations test: {} acquires, {} lists, {} GCs",
acquires, lists, gcs
);
}