Hi, I've met a strange lock issue, the scenario is a little bit complicated, let me explain:
I need to write some unit tests, in each unit test, I need to prepare some mock data for a global variable A, and keep it unchanged during the whole test function. To keep the global variable not changed by other test cases, I introduced another global variable LOCK to try to lock it inside a test case:
// common.rs
pub struct Data {
data: String,
}
static A: Mutex<Option<Data>> = Mutex::new();
static LOCK: Mutex<()> = Mutex::new(());
pub fn acquire() -> MutexGuard {
let lock_guard = LOCK.lock();
// Create a mock "A" for test case here.
let a = A.lock();
a.data = ... // mock data here
a.unlock();
// return lock guard
lock_guard
}
// tests.rs
fn test1() {
let lock_guard = acquire();
// start running test logic
// Lock "A", clone the value, and release it
for i in 0..100 {
let data = A.lock().data.clone();
// verify data logic
...
}
// lock_guard automatically release at end, so let other test cases run
}
// Same with test1, except the verification logic is different
fn test2() {
let lock_guard = acquire();
// start running test logic
// Lock "A", clone the value, and release it
for i in 0..100 {
let data = A.lock().data.clone();
// verify data logic
...
}
// lock_guard automatically release at end, so let other test cases run
}
But when running multiple test cases in GitHub Actions, there are still some test cases failed. I guess the global variable A is actually not locked by LOCK, what should I do?
I still don't understand why you need two mutexes. From your snippet I don't see why LOCK is needed. I.e. if A really does have to be global[1], I'd lock A once per test and remove LOCK, instead of locking LOCK for the entirety of a test while locking A multiple times while we have LOCK locked. No idea if this will fix your CI though. I don't know if a synchronisation mishap is actually the cause of it failing.
From previous versions of your question I gathered that you create a config file in a temp directory and populate A with it. Why do we need to synchronise tests on this? Can't every test have their own version A? âŠī¸
Oh, the example code is still too simple, let me make it more complicated:
In each test case, after A data is initialized (prepared), it will be read multiple times during the test case, and I need to make sure A is not changed during a whole test case:
// Lock "A", clone the value, and release it
for i in 0..100 {
let data = A.lock().data.clone();
// verify data logic
...
}
When cargo test runs, two test functions could be running parallel, so the global A data change be changed by different test cases.
Yes, you are right..... Thanks for your reply!
I think I need to try to provide a best minimal re-producible example that can describe my issue before further discussion