Files
Notes/notes-service/vendor/mea/src/mutex/test.rs
T
2026-08-01 16:11:49 +03:00

367 lines
9.7 KiB
Rust

// Copyright 2024 tison <wander4096@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::Arc;
use super::*;
#[test]
fn test_try_lock_never_blocks() {
// Test that try_lock and try_lock_owned never block, even under contention
let mutex = Arc::new(Mutex::new(9));
let _guard = mutex.try_lock().unwrap();
let result = mutex.try_lock();
assert!(result.is_none());
let result = mutex.clone().try_lock_owned();
assert!(result.is_none());
}
#[test]
fn test_get_mut_provides_exclusive_access() {
// Test that get_mut provides direct access when we have exclusive ownership
let mut mutex = Mutex::new(11);
let data = mutex.get_mut();
*data = 100;
assert_eq!(*mutex.get_mut(), 100);
let inner = mutex.into_inner();
assert_eq!(inner, 100);
}
#[tokio::test]
async fn test_guard_map_preserves_lock() {
let data = (99i32, vec![1, 2, 3]);
let mutex = Mutex::new(data);
let guard = mutex.lock().await;
let mut mapped_guard = MutexGuard::map(guard, |data| &mut data.0);
assert!(mutex.try_lock().is_none());
*mapped_guard = 100;
// After dropping, mutex should be available
drop(mapped_guard);
let guard = mutex.try_lock().unwrap();
assert_eq!(guard.0, 100);
}
#[tokio::test]
async fn test_mapped_guard_holds_lock() {
// Test that MappedMutexGuard properly holds the lock even after the original guard is moved
let mutex = Arc::new(Mutex::new((10, 20)));
let guard = mutex.lock().await;
let mapped_guard = MutexGuard::map(guard, |data| &mut data.0);
assert!(
mutex.try_lock().is_none(),
"Lock should be held by the mapped guard"
);
assert_eq!(*mapped_guard, 10);
drop(mapped_guard);
assert!(
mutex.try_lock().is_some(),
"Lock should be released after mapped guard is dropped"
);
}
#[tokio::test]
async fn test_owned_mapped_guard_holds_lock() {
// Test that mapped owned guard properly holds the lock
let mutex = Arc::new(Mutex::new((30, 40)));
let owned_guard = mutex.clone().lock_owned().await;
let mapped_owned_guard = OwnedMutexGuard::map(owned_guard, |data| &mut data.1);
assert!(
mutex.try_lock().is_none(),
"Lock should be held by the mapped owned guard"
);
assert_eq!(*mapped_owned_guard, 40);
// When mapped owned guard is dropped, lock should be released
drop(mapped_owned_guard);
assert!(
mutex.try_lock().is_some(),
"Lock should be released after mapped owned guard is dropped"
);
}
#[tokio::test]
async fn test_guard_filter_map_failure() {
let data: Vec<i32> = vec![];
let mutex = Mutex::new(data);
let guard = mutex.lock().await;
let result = MutexGuard::filter_map(guard, |vec| vec.get_mut(0));
assert!(result.is_err());
if let Err(mut original_guard) = result {
original_guard.push(100);
assert_eq!(*original_guard, vec![100]);
} else {
panic!("Expected Err, but got Ok");
}
}
#[tokio::test]
async fn test_owned_guard_filter_map_failure() {
let data: Vec<i32> = vec![];
let mutex = Arc::new(Mutex::new(data));
let guard = mutex.clone().lock_owned().await;
let result = OwnedMutexGuard::filter_map(guard, |vec| vec.get_mut(0));
assert!(result.is_err());
if let Err(mut original_guard) = result {
original_guard.push(200);
assert_eq!(*original_guard, vec![200]);
} else {
panic!("Expected Err, but got Ok");
}
}
#[tokio::test]
async fn test_multiple_map_operations() {
// Test multiple consecutive map operations
let data = vec![vec![1, 2], vec![3, 4]];
let mutex = Mutex::new(data);
let guard = mutex.lock().await;
let first_vec = MutexGuard::map(guard, |data| &mut data[0]);
let mut first_element = MappedMutexGuard::map(first_vec, |vec| &mut vec[0]);
*first_element = 100;
drop(first_element);
let guard = mutex.lock().await;
assert_eq!(guard[0][0], 100);
assert_eq!(guard[0][1], 2);
assert_eq!(guard[1][0], 3);
}
#[tokio::test]
async fn test_stress() {
let mutex = Arc::new(Mutex::new(0));
let mut handles = Vec::new();
// Create many concurrent tasks
for i in 0..1000 {
let mutex = mutex.clone();
handles.push(tokio::spawn(async move {
let mut guard = mutex.lock().await;
*guard += 1;
if i % 10 == 0 {
tokio::task::yield_now().await;
}
}));
}
for handle in handles {
handle.await.unwrap();
}
let final_value = *mutex.lock().await;
assert_eq!(final_value, 1000);
}
#[tokio::test]
async fn test_guard_prevents_concurrent_access() {
// Test that holding a guard prevents other tasks from acquiring the lock
let mutex = Arc::new(Mutex::new(0));
let mutex_clone = mutex.clone();
let guard = mutex.lock().await;
assert!(
mutex.try_lock().is_none(),
"Lock should be held by the first guard"
);
let handle = tokio::spawn(async move {
let _guard2 = mutex_clone.lock().await;
123
});
tokio::task::yield_now().await;
assert!(
mutex.try_lock().is_none(),
"Lock should still be held after yielding"
);
drop(guard);
let result = handle.await.unwrap();
assert_eq!(result, 123);
assert!(
mutex.try_lock().is_some(),
"Lock should be available after all guards are dropped"
);
}
#[test]
fn test_lock_panic_safety() {
use std::panic::AssertUnwindSafe;
let mutex = Arc::new(Mutex::new(0));
let mutex_clone = mutex.clone();
let result = std::panic::catch_unwind(AssertUnwindSafe(move || {
let _guard = mutex_clone.try_lock().unwrap();
panic!("test panic");
}));
assert!(result.is_err());
// Lock should be released after panic
assert!(mutex.try_lock().is_some());
}
#[tokio::test]
async fn test_async_lock_panic_safety() {
// Test panic safety with async locks
let mutex = Arc::new(Mutex::new(0));
let mutex_clone = mutex.clone();
let handle = tokio::spawn(async move {
let _guard = mutex_clone.lock().await;
panic!("async test panic");
});
// panic
assert!(handle.await.is_err());
let guard = mutex.try_lock();
assert!(guard.is_some());
}
#[tokio::test]
async fn test_owned_guard_panic_safety() {
let mutex = Arc::new(Mutex::new(0));
let mutex_clone = mutex.clone();
let handle = tokio::spawn(async move {
let _guard = mutex_clone.clone().lock_owned().await;
panic!("owned guard panic");
});
assert!(handle.await.is_err());
// Lock should be available after the panicked task
let guard = mutex.try_lock();
assert!(guard.is_some());
}
#[tokio::test]
async fn test_mapped_guard_panic_safety() {
// Test panic safety with mapped guards
let mutex = Arc::new(Mutex::new((66, vec![1, 2, 3])));
let mutex_clone = mutex.clone();
let handle = tokio::spawn(async move {
let guard = mutex_clone.lock().await;
let _mapped = MutexGuard::map(guard, |data| &mut data.0);
panic!("mapped guard panic");
});
assert!(handle.await.is_err());
let guard = mutex.try_lock();
assert!(guard.is_some());
}
#[tokio::test]
async fn test_memory_ordering_correctness() {
// Test that mutex provides proper memory ordering guarantees
// When one task modifies data under mutex protection,
// another task should see the modification after acquiring the lock
let mutex = Arc::new(Mutex::new(vec![1, 2, 3]));
let mutex_clone = mutex.clone();
let handle = tokio::spawn(async move {
let mut guard = mutex_clone.lock().await;
guard.push(4);
guard[0] = 100;
// Lock is released when guard is dropped
});
handle.await.unwrap();
let guard = mutex.lock().await;
assert_eq!(*guard, vec![100, 2, 3, 4]);
// This test relies on mutex's acquire-release semantics to ensure
// that modifications made in the critical section are visible
// to subsequent lock acquisitions
}
#[tokio::test]
async fn test_mutex_zst() {
// Test that Mutex works correctly with Zero-Sized Types
let mutex = Arc::new(Mutex::new(()));
let mutex_clone = mutex.clone();
let handle = tokio::spawn(async move {
let guard = mutex_clone.lock().await;
*guard;
});
handle.await.unwrap();
// try_lock and owned guard should also work with ZST
let _owned_guard = mutex.clone().lock_owned().await;
assert!(mutex.try_lock().is_none());
drop(_owned_guard);
let guard = mutex.try_lock().unwrap();
*guard;
}
#[tokio::test]
async fn test_mapped_mutex_guard_send() {
// Test that MappedMutexGuard can be sent across await points
#[derive(Debug)]
struct TestStruct {
field1: i32,
_field2: String,
}
let mutex = Arc::new(Mutex::new(TestStruct {
field1: 2,
_field2: "oh".to_owned(),
}));
let mutex_clone = mutex.clone();
let handle = tokio::spawn(async move {
let guard = mutex_clone.lock().await;
let mapped_guard = MutexGuard::map(guard, |data| &mut data.field1);
tokio::task::yield_now().await;
*mapped_guard
});
let result = handle.await.unwrap();
assert_eq!(result, 2);
}