mod common;
#[cfg(feature = "std")]
mod tests {
use locktick::{clear_lock_infos, lock_snapshots, std::*, GuardKind};
use serial_test::serial;
use super::*;
use common::*;
#[test]
#[serial]
fn mutex() {
clear_lock_infos();
let lock1 = Mutex::new(Object);
check_locks!(1, 0, 0);
let lock2 = Mutex::new(Object);
check_locks!(2, 0, 0);
let guard1 = lock1.lock().unwrap();
check_guard!(guard1, 1, 1);
check_locks!(2, 1, 1);
let guard2 = lock2.lock().unwrap();
check_guard!(guard2, 1, 1);
check_locks!(2, 2, 2);
}
#[test]
#[serial]
fn rwlock() {
clear_lock_infos();
let lock1 = RwLock::new(Object);
check_locks!(1, 0, 0);
let read1 = lock1.read().unwrap();
check_guard!(read1, 1, 1);
let read2 = lock1.read().unwrap();
check_guard!(read2, 1, 1);
drop(read1);
check_locks!(1, 2, 1);
drop(read2);
check_locks!(1, 2, 0);
let write = lock1.write().unwrap();
check_guard!(write, 1, 1);
drop(write);
check_locks!(1, 3, 0);
let _lock2 = RwLock::new(Object);
check_locks!(2, 3, 0);
}
#[test]
#[serial]
fn lock_locations() {
clear_lock_infos();
let lock = Mutex::new(Object);
let lock_line = line!() - 1;
let guard = lock.lock().unwrap();
check_lock_loc!(guard, lock_line);
assert!(guard.guard_location.col > 0);
assert_eq!(guard.lock_location.line, lock_line);
drop(guard);
let guard2 = lock.lock().unwrap();
assert_eq!(guard2.lock_location.line, lock_line);
}
#[test]
#[serial]
fn try_lock_location() {
clear_lock_infos();
let lock = Mutex::new(Object);
let lock_line = line!() - 1;
let guard = lock.try_lock().unwrap();
check_guard!(guard, 1, 1);
check_lock_loc!(guard, lock_line);
assert!(guard.guard_location.col > 0);
}
#[test]
#[serial]
fn default_location() {
clear_lock_infos();
let lock: Mutex<Object> = Default::default();
let lock_line = line!() - 1;
let guard = lock.lock().unwrap();
check_lock_loc!(guard, lock_line);
}
#[test]
#[serial]
fn rwlock_guard_kinds() {
clear_lock_infos();
let lock = RwLock::new(Object);
let _read = lock.read().unwrap();
let locks = lock_snapshots();
let lock_info = locks
.iter()
.find(|l| l.kind == locktick::LockKind::RwLock)
.unwrap();
let read_guard = lock_info
.known_guards
.values()
.find(|g| g.kind == GuardKind::Read)
.unwrap();
assert_eq!(read_guard.kind, GuardKind::Read);
drop(_read);
let _write = lock.write().unwrap();
let locks = lock_snapshots();
let lock_info = locks
.iter()
.find(|l| l.kind == locktick::LockKind::RwLock)
.unwrap();
let write_guard = lock_info
.known_guards
.values()
.find(|g| g.kind == GuardKind::Write)
.unwrap();
assert_eq!(write_guard.kind, GuardKind::Write);
}
#[test]
#[serial]
fn distinct_lock_locations() {
clear_lock_infos();
let lock_a = Mutex::new(Object);
let lock_a_line = line!() - 1;
let lock_b = Mutex::new(Object);
let lock_b_line = line!() - 1;
let guard_a = lock_a.lock().unwrap();
let guard_b = lock_b.lock().unwrap();
assert_eq!(guard_a.lock_location.line, lock_a_line);
assert_eq!(guard_b.lock_location.line, lock_b_line);
assert_ne!(guard_a.lock_location, guard_b.lock_location);
}
#[test]
#[serial]
fn rwlock_read_locations() {
clear_lock_infos();
let lock = RwLock::new(Object);
let lock_line = line!() - 1;
let read = lock.read().unwrap();
check_guard!(read, 1, 1);
check_lock_loc!(read, lock_line);
assert!(read.guard_location.col > 0);
}
#[test]
#[serial]
fn rwlock_write_locations() {
clear_lock_infos();
let lock = RwLock::new(Object);
let lock_line = line!() - 1;
let write = lock.write().unwrap();
check_guard!(write, 1, 1);
check_lock_loc!(write, lock_line);
assert!(write.guard_location.col > 0);
}
#[test]
#[serial]
fn rwlock_try_read_location() {
clear_lock_infos();
let lock = RwLock::new(Object);
let read = lock.try_read().unwrap();
check_guard!(read, 1, 1);
}
#[test]
#[serial]
fn rwlock_try_write_location() {
clear_lock_infos();
let lock = RwLock::new(Object);
let write = lock.try_write().unwrap();
check_guard!(write, 1, 1);
}
#[test]
#[serial]
fn rwlock_default_location() {
clear_lock_infos();
let lock: RwLock<Object> = Default::default();
let lock_line = line!() - 1;
let read = lock.read().unwrap();
check_lock_loc!(read, lock_line);
}
#[test]
#[serial]
fn contended_mutex() {
use std::sync::Arc;
use std::thread;
use std::time::Duration;
clear_lock_infos();
let lock = Arc::new(Mutex::new(0u32));
let held = lock.lock().unwrap();
check_guard!(held, 1, 1);
let lock_for_thread = Arc::clone(&lock);
let handle = thread::spawn(move || {
let _guard = lock_for_thread.lock().unwrap();
});
thread::sleep(Duration::from_millis(100));
let info = lock_snapshots().into_iter().next().unwrap();
let held_guard_info = info.known_guards.get(&held.guard_location).unwrap();
assert_eq!(held_guard_info.num_active_uses(), 1);
assert_eq!(held_guard_info.num_waiting(), 0);
assert!(held_guard_info.is_in_use());
let waiting_guard_info = info
.known_guards
.values()
.find(|g| g.num_waiting() == 1)
.expect("a thread should be waiting on the lock");
assert_eq!(waiting_guard_info.num_active_uses(), 0);
assert_eq!(waiting_guard_info.num_waiting(), 1);
let waiting = waiting_guard_info.waiting_call_indices();
let active = held_guard_info.active_call_indices();
assert_eq!(waiting.len(), 1);
assert_eq!(active.len(), 1);
assert!(waiting[0] > active[0]);
drop(held);
handle.join().unwrap();
let info = lock_snapshots().into_iter().next().unwrap();
assert_eq!(info.known_guards.len(), 2);
for g in info.known_guards.values() {
assert_eq!(g.num_active_uses(), 0);
assert_eq!(g.num_waiting(), 0);
assert!(!g.is_in_use());
}
let thread_guard_info = info
.known_guards
.values()
.find(|g| g.max_wait_time > Duration::ZERO)
.expect("the waiting thread's guard_info should have recorded a wait time");
assert_eq!(thread_guard_info.num_uses, 1);
}
}