#[cfg(test)]
mod tests {
use super::super::contended_mutex::ContendedMutex;
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
use super::super::rwlock::RwLock;
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
use crate::cx::{Cx, cap};
use std::sync::Arc;
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
use std::thread;
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
fn test_correct_lock_ordering() {
let config_lock = Arc::new(ContendedMutex::new("config_cache", 0));
let regions_lock = Arc::new(ContendedMutex::new("regions_table", 0));
let tasks_lock = Arc::new(ContendedMutex::new("tasks_queue", 0));
let _config_guard = config_lock.lock().unwrap(); let _regions_guard = regions_lock.lock().unwrap(); let _tasks_guard = tasks_lock.lock().unwrap();
}
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
#[should_panic(expected = "Lock ordering violation")]
fn test_lock_ordering_violation() {
let config_lock = Arc::new(ContendedMutex::new("config_cache", 0));
let tasks_lock = Arc::new(ContendedMutex::new("tasks_queue", 0));
let _tasks_guard = tasks_lock.lock().unwrap();
let _config_guard = config_lock.lock().unwrap(); }
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
fn test_same_rank_locks_allowed() {
let tasks_lock1 = Arc::new(ContendedMutex::new("tasks_queue1", 0));
let tasks_lock2 = Arc::new(ContendedMutex::new("tasks_queue2", 0));
let _guard1 = tasks_lock1.lock().unwrap(); let _guard2 = tasks_lock2.lock().unwrap(); }
#[test]
fn test_unknown_rank_locks_no_enforcement() {
let unknown_lock1 = Arc::new(ContendedMutex::new("unknown_lock", 0));
let unknown_lock2 = Arc::new(ContendedMutex::new("another_unknown", 0));
let config_lock = Arc::new(ContendedMutex::new("config_cache", 0));
let _unknown1 = unknown_lock1.lock().unwrap(); let _config = config_lock.lock().unwrap(); let _unknown2 = unknown_lock2.lock().unwrap(); }
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
fn test_lock_release_and_reacquire() {
let config_lock = Arc::new(ContendedMutex::new("config_cache", 0));
let tasks_lock = Arc::new(ContendedMutex::new("tasks_queue", 0));
{
let _config_guard = config_lock.lock().unwrap(); let _tasks_guard = tasks_lock.lock().unwrap(); }
{
let _tasks_guard = tasks_lock.lock().unwrap(); let _config_guard = config_lock.lock().unwrap(); }
}
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
fn test_cross_thread_lock_ordering_isolation() {
let config_lock = Arc::new(ContendedMutex::new("config_cache", 0));
let tasks_lock = Arc::new(ContendedMutex::new("tasks_queue", 0));
let config_clone = Arc::clone(&config_lock);
let tasks_clone = Arc::clone(&tasks_lock);
let handle1 = thread::spawn(move || {
let _tasks_guard = tasks_clone.lock().unwrap(); std::panic::catch_unwind(|| {
let _config_guard = config_clone.lock().unwrap(); })
});
let config_clone2 = Arc::clone(&config_lock);
let tasks_clone2 = Arc::clone(&tasks_lock);
let handle2 = thread::spawn(move || {
let _config_guard = config_clone2.lock().unwrap(); let _tasks_guard = tasks_clone2.lock().unwrap(); "success"
});
let result1 = handle1.join().unwrap(); assert!(
result1.is_err(),
"Thread 1 should have panicked due to lock ordering violation"
);
let result2 = handle2.join().unwrap(); assert_eq!(
result2, "success",
"Thread 2 should have succeeded with correct ordering"
);
}
#[test]
fn test_all_lock_ranks() {
use crate::sync::lock_ordering::LockRank;
assert_eq!(LockRank::from_name("config_cache"), Some(LockRank::Config));
assert_eq!(
LockRank::from_name("metrics_collector"),
Some(LockRank::Instrumentation)
);
assert_eq!(
LockRank::from_name("trace_buffer"),
Some(LockRank::Instrumentation)
);
assert_eq!(
LockRank::from_name("regions_table"),
Some(LockRank::Regions)
);
assert_eq!(LockRank::from_name("region_state"), Some(LockRank::Regions));
assert_eq!(LockRank::from_name("tasks_queue"), Some(LockRank::Tasks));
assert_eq!(
LockRank::from_name("scheduler_state"),
Some(LockRank::Tasks)
);
assert_eq!(
LockRank::from_name("obligations_ledger"),
Some(LockRank::Obligations)
);
assert_eq!(
LockRank::from_name("obligation_tracker"),
Some(LockRank::Obligations)
);
assert_eq!(LockRank::from_name("Config_Global"), Some(LockRank::Config));
assert_eq!(LockRank::from_name("TASKS_QUEUE"), Some(LockRank::Tasks));
assert_eq!(LockRank::from_name("unknown_lock"), None);
assert_eq!(LockRank::from_name(""), None);
}
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
fn test_cx() -> Cx<cap::All> {
Cx::for_testing()
}
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
#[should_panic(expected = "Lock ordering violation")]
fn test_rwlock_owned_futures_respect_lock_ordering() {
let cx = test_cx();
let tasks_rwlock = Arc::new(RwLock::with_name("tasks_scheduler", 42));
let config_rwlock = Arc::new(RwLock::with_name("config_cache", 1));
let _tasks_read_guard =
futures_lite::future::block_on(async { tasks_rwlock.read(&cx).await.unwrap() });
let _config_read_guard = futures_lite::future::block_on(async {
config_rwlock.read(&cx).await.unwrap() });
}
#[test]
#[cfg(any(debug_assertions, feature = "lock-metrics"))]
#[should_panic(expected = "Lock ordering violation")]
fn test_rwlock_owned_write_futures_respect_lock_ordering() {
let cx = test_cx();
let obligations_rwlock = Arc::new(RwLock::with_name("obligations_ledger", 0));
let regions_rwlock = Arc::new(RwLock::with_name("regions_table", 0));
let _obligations_write_guard =
futures_lite::future::block_on(async { obligations_rwlock.write(&cx).await.unwrap() });
let _regions_write_guard = futures_lite::future::block_on(async {
regions_rwlock.write(&cx).await.unwrap() });
}
}