#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::{rc::Rc, sync::Arc};
use serial_test::serial;
use surelock::{
key::{lock_scope, try_lock_scope},
key_handle::KeyHandle,
level::NewHigher,
mutex::Mutex,
raw_mutex::RawMutex as _,
set::LockSet,
};
type Low = surelock::level::Level<1>;
type High = surelock::level::Level<2>;
type LowMutex<T> = Mutex<T, Low>;
type HighMutex<T> = Mutex<T, High>;
mod lock_with {
use super::*;
#[test]
fn single() {
let mut counter: Mutex<u32> = Mutex::new(0);
lock_scope(|key| {
let ((), _key) = key.lock_with(&counter, |mut guard| {
*guard += 1;
});
});
assert_eq!(*counter.get_mut(), 1);
}
#[test]
fn returns_value() {
let counter: Mutex<u32> = Mutex::new(42);
let value = lock_scope(|key| {
let (v, _key) = key.lock_with(&counter, |guard| *guard);
v
});
assert_eq!(value, 42);
}
#[test]
fn two_atomic() {
let mut a: Mutex<u32> = Mutex::new(10);
let mut b: Mutex<u32> = Mutex::new(20);
lock_scope(|key| {
let ((), _key) = key.lock_with(&(&a, &b), |(mut ga, mut gb)| {
*ga += 1;
*gb += 1;
});
});
assert_eq!(*a.get_mut(), 11);
assert_eq!(*b.get_mut(), 21);
}
#[test]
fn three_tuple() {
let mut a: Mutex<u32> = Mutex::new(1);
let mut b: Mutex<u32> = Mutex::new(2);
let mut c: Mutex<u32> = Mutex::new(3);
lock_scope(|key| {
let ((), _key) = key.lock_with(&(&a, &b, &c), |(mut ga, mut gb, mut gc)| {
*ga += 10;
*gb += 10;
*gc += 10;
});
});
assert_eq!(*a.get_mut(), 11);
assert_eq!(*b.get_mut(), 12);
assert_eq!(*c.get_mut(), 13);
}
#[test]
#[allow(clippy::indexing_slicing)]
fn twelve_tuple() {
let mutexes: Vec<Mutex<u32>> = (0..12).map(Mutex::new).collect();
lock_scope(|key| {
let ((), _key) = key.lock_with(
&(
&mutexes[0],
&mutexes[1],
&mutexes[2],
&mutexes[3],
&mutexes[4],
&mutexes[5],
&mutexes[6],
&mutexes[7],
&mutexes[8],
&mutexes[9],
&mutexes[10],
&mutexes[11],
),
|(g0, g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11)| {
let sum =
*g0 + *g1 + *g2 + *g3 + *g4 + *g5 + *g6 + *g7 + *g8 + *g9 + *g10 + *g11;
assert_eq!(sum, 66); },
);
});
}
#[test]
#[should_panic(expected = "lock_with called with duplicate locks")]
fn rejects_duplicate_mutex() {
let m: Mutex<u32> = Mutex::new(0);
lock_scope(|key| {
let ((), _key) = key.lock_with(&(&m, &m), |(_ga, _gb)| {});
});
}
}
mod lock {
use super::*;
#[test]
fn single_mutex() {
let mut counter: Mutex<u32> = Mutex::new(0);
lock_scope(|key| {
let (mut guard, _key) = key.lock(&counter);
*guard += 1;
});
assert_eq!(*counter.get_mut(), 1);
}
#[test]
fn single_then_higher() {
let mut low: Mutex<u32> = Mutex::new(10);
let mut high: Mutex<u32, surelock::level::Level<1>> = Mutex::new(20);
lock_scope(|key| {
let (mut g, key) = key.lock(&low);
*g += 1;
drop(g);
let (mut g2, _key) = key.lock(&high);
*g2 += 1;
});
assert_eq!(*low.get_mut(), 11);
assert_eq!(*high.get_mut(), 21);
}
}
mod lock_set {
use super::*;
#[test]
fn single() {
let mut counter: Mutex<u32> = Mutex::new(0);
let set = LockSet::new(&counter);
lock_scope(|key| {
let (mut guard, _key) = key.lock(&set);
*guard += 1;
});
assert_eq!(*counter.get_mut(), 1);
}
#[test]
fn two_atomic() {
let mut a: Mutex<u32> = Mutex::new(10);
let mut b: Mutex<u32> = Mutex::new(20);
let set = LockSet::new((&a, &b));
lock_scope(|key| {
let ((mut ga, mut gb), _key) = key.lock(&set);
*ga += 1;
*gb += 1;
});
assert_eq!(*a.get_mut(), 11);
assert_eq!(*b.get_mut(), 21);
}
#[test]
fn multi_level() {
let config: Mutex<u32> = Mutex::new(10);
let mut account = Mutex::new_higher(20u32, &config);
let set = LockSet::new((&config, &account));
lock_scope(|key| {
let ((cfg, mut acct), _key) = key.lock(&set);
*acct += *cfg;
});
assert_eq!(*account.get_mut(), 30);
}
#[test]
fn slice() {
let mutexes: Vec<Mutex<u64>> = (0..5).map(|i| Mutex::new(i * 10)).collect();
let set = LockSet::new(mutexes.as_slice());
let mut handle = KeyHandle::claim();
handle.scope(|key| {
let (guards, _key) = key.lock(&set);
let sum: u64 = guards.iter().map(|g| **g).sum();
assert_eq!(sum, 10 + 20 + 30 + 40);
});
}
#[test]
fn slice_subset() {
let mutexes: Vec<Mutex<u64>> = (0..10).map(Mutex::new).collect();
#[allow(clippy::indexing_slicing)]
let subset = &mutexes[2..5];
let set = LockSet::new(subset);
let mut handle = KeyHandle::claim();
handle.scope(|key| {
let (guards, _key) = key.lock(&set);
let sum: u64 = guards.iter().map(|g| **g).sum();
assert_eq!(sum, 2 + 3 + 4);
});
}
#[test]
#[should_panic(expected = "LockSet contains duplicate locks")]
fn rejects_duplicate_mutex() {
let m: Mutex<u32> = Mutex::new(0);
let _set = LockSet::new((&m, &m));
}
#[test]
fn try_new_returns_none_on_duplicate() {
let m: Mutex<u32> = Mutex::new(0);
assert!(LockSet::try_new((&m, &m)).is_none());
}
#[test]
fn try_new_returns_some_on_distinct() {
let a: Mutex<u32> = Mutex::new(1);
let b: Mutex<u32> = Mutex::new(2);
let set = LockSet::try_new((&a, &b)).unwrap();
lock_scope(|key| {
let ((ga, gb), _key) = key.lock(&set);
assert_eq!(*ga + *gb, 3);
});
}
#[test]
fn empty_slice() {
let mutexes: &[Mutex<u32>] = &[];
let set = LockSet::new(mutexes);
lock_scope(|key| {
let (guards, _key) = key.lock(&set);
assert!(guards.is_empty());
});
}
#[test]
fn try_new_empty_slice_returns_some() {
let mutexes: &[Mutex<u32>] = &[];
assert!(LockSet::try_new(mutexes).is_some());
}
#[test]
fn three_tuple() {
let mut a: Mutex<u32> = Mutex::new(1);
let mut b: Mutex<u32> = Mutex::new(2);
let mut c: Mutex<u32> = Mutex::new(3);
let set = LockSet::new((&a, &b, &c));
lock_scope(|key| {
let ((mut ga, mut gb, mut gc), _key) = key.lock(&set);
*ga += 10;
*gb += 10;
*gc += 10;
});
assert_eq!(*a.get_mut(), 11);
assert_eq!(*b.get_mut(), 12);
assert_eq!(*c.get_mut(), 13);
}
}
mod cross_level {
use super::*;
#[test]
fn with_lock_with() {
let config: LowMutex<u32> = Mutex::new(5);
let mut account: HighMutex<u32> = Mutex::new(100);
lock_scope(|key| {
let (bonus, key) = key.lock_with(&config, |cfg| *cfg);
let ((), _key) = key.lock_with(&account, |mut acct| {
*acct += bonus;
});
});
assert_eq!(*account.get_mut(), 105);
}
#[test]
fn with_lock() {
let config: LowMutex<u32> = Mutex::new(5);
let mut account: HighMutex<u32> = Mutex::new(100);
lock_scope(|key| {
let (cfg, key) = key.lock(&config);
let (mut acct, _key) = key.lock(&account);
*acct += *cfg;
});
assert_eq!(*account.get_mut(), 105);
}
#[test]
fn mixed() {
let config: LowMutex<u32> = Mutex::new(5);
let mut account: HighMutex<u32> = Mutex::new(100);
lock_scope(|key| {
let (bonus, key) = key.lock_with(&config, |cfg| *cfg);
let (mut acct, _key) = key.lock(&account);
*acct += bonus;
});
assert_eq!(*account.get_mut(), 105);
}
}
mod subscope {
use super::*;
#[test]
fn inherits_level() {
let config: LowMutex<u32> = Mutex::new(42);
let mut account: HighMutex<u32> = Mutex::new(0);
lock_scope(|key| {
let (val, key) = key.lock_with(&config, |cfg| *cfg);
let (result, _key) = key.subscope(|inner_key| {
let (r, _inner_key) = inner_key.lock_with(&account, |mut acct| {
*acct = val;
*acct
});
r
});
assert_eq!(result, 42);
});
assert_eq!(*account.get_mut(), 42);
}
}
mod try_lock_scope_tests {
use super::*;
#[test]
fn returns_some_when_no_scope_active() {
let counter: Mutex<u32> = Mutex::new(0);
let result = try_lock_scope(|key| {
let (v, _key) = key.lock_with(&counter, |guard| *guard);
v
});
assert_eq!(result, Some(0));
}
#[test]
fn returns_none_when_nested() {
let result = lock_scope(|_key| try_lock_scope(|_key2| 42));
assert_eq!(result, None);
}
#[test]
fn recovers_after_inner_returns_none() {
lock_scope(|_key| {
let inner = try_lock_scope(|_key2| 42);
assert_eq!(inner, None);
});
let result = try_lock_scope(|_key| 99);
assert_eq!(result, Some(99));
}
}
mod key_handle {
use super::*;
#[test]
fn scope_works() {
let mut keeper = KeyHandle::claim();
let mut counter: Mutex<u32> = Mutex::new(0);
keeper.scope(|key| {
let ((), _key) = key.lock_with(&counter, |mut guard| {
*guard += 1;
});
});
assert_eq!(*counter.get_mut(), 1);
}
#[test]
fn sequential_scopes() {
let mut keeper = KeyHandle::claim();
let a = keeper.scope(|_key| 1);
let b = keeper.scope(|_key| 2);
let c = keeper.scope(|_key| 3);
assert_eq!(a + b + c, 6);
}
#[test]
fn prevents_second_claim() {
let _keeper = KeyHandle::claim();
assert!(KeyHandle::try_claim().is_none());
}
#[test]
fn blocks_try_lock_scope() {
let _keeper = KeyHandle::claim();
let result = try_lock_scope(|_key| 42);
assert_eq!(result, None);
}
#[test]
fn recovered_after_drop() {
{
let _keeper = KeyHandle::claim();
}
let result = try_lock_scope(|_key| 99);
assert_eq!(result, Some(99));
}
#[test]
fn with_levels() {
let mut keeper = KeyHandle::claim();
let config: LowMutex<u32> = Mutex::new(5);
let mut account: HighMutex<u32> = Mutex::new(100);
keeper.scope(|key| {
let (bonus, key) = key.lock_with(&config, |cfg| *cfg);
let ((), _key) = key.lock_with(&account, |mut acct| {
*acct += bonus;
});
});
assert_eq!(*account.get_mut(), 105);
}
}
mod locksmith {
use super::*;
#[test]
#[serial]
fn issue_and_redeem() {
let smith = surelock::locksmith::Locksmith::new(2).unwrap();
let voucher = smith.issue().unwrap();
let mut handle = voucher.redeem().unwrap();
let mut counter: Mutex<u32> = Mutex::new(0);
handle.scope(|key| {
let ((), _key) = key.lock_with(&counter, |mut guard| {
*guard += 1;
});
});
assert_eq!(*counter.get_mut(), 1);
}
#[test]
#[serial]
fn respects_limit() {
let smith = surelock::locksmith::Locksmith::new(2).unwrap();
let _v1 = smith.issue().unwrap();
let _v2 = smith.issue().unwrap();
assert!(smith.issue().is_none());
}
#[test]
#[serial]
fn remaining() {
let smith = surelock::locksmith::Locksmith::new(3).unwrap();
assert_eq!(smith.remaining(), Some(3));
let _v1 = smith.issue().unwrap();
assert_eq!(smith.remaining(), Some(2));
let _v2 = smith.issue().unwrap();
assert_eq!(smith.remaining(), Some(1));
let _v3 = smith.issue().unwrap();
assert_eq!(smith.remaining(), Some(0));
}
#[test]
#[serial]
fn limit_bounded() {
let smith = surelock::locksmith::Locksmith::new(5).unwrap();
assert_eq!(smith.limit(), Some(5));
}
#[test]
#[serial]
fn limit_unlimited() {
let smith = surelock::locksmith::Locksmith::unlimited().unwrap();
assert_eq!(smith.limit(), None);
}
#[test]
#[serial]
fn voucher_redeem_blocks_try_lock_scope() {
let smith = surelock::locksmith::Locksmith::new(1).unwrap();
let voucher = smith.issue().unwrap();
let _handle = voucher.redeem().unwrap();
let result = try_lock_scope(|_key| 42);
assert_eq!(result, None);
}
#[test]
#[serial]
fn voucher_to_thread() {
let smith = surelock::locksmith::Locksmith::new(1).unwrap();
let voucher = smith.issue().unwrap();
let handle = std::thread::spawn(move || {
let mut handle = voucher.redeem().unwrap();
handle.scope(|key| {
let m: Mutex<u32> = Mutex::new(99);
let (v, _key) = key.lock_with(&m, |guard| *guard);
v
})
});
assert_eq!(handle.join().unwrap(), 99);
}
#[test]
#[serial]
fn unlimited() {
let smith = surelock::locksmith::Locksmith::unlimited().unwrap();
for _ in 0..100 {
let _v = smith.issue().unwrap();
}
assert_eq!(smith.issued(), 100);
}
#[test]
#[serial]
fn singleton_enforcement() {
let smith = surelock::locksmith::Locksmith::new(1).unwrap();
assert!(surelock::locksmith::Locksmith::new(2).is_none());
assert!(surelock::locksmith::Locksmith::unlimited().is_none());
drop(smith);
let _smith2 = surelock::locksmith::Locksmith::new(1).unwrap();
}
#[test]
#[serial]
#[should_panic(expected = "a Locksmith already exists")]
fn create_panics_when_exists() {
let _smith = surelock::locksmith::Locksmith::create(1);
let _smith2 = surelock::locksmith::Locksmith::create(1);
}
#[test]
#[serial]
#[should_panic(expected = "a Locksmith already exists")]
fn create_unlimited_panics_when_exists() {
let _smith = surelock::locksmith::Locksmith::create_unlimited();
let _smith2 = surelock::locksmith::Locksmith::create_unlimited();
}
#[test]
#[serial]
fn remaining_none_when_unlimited() {
let smith = surelock::locksmith::Locksmith::unlimited().unwrap();
assert_eq!(smith.remaining(), None);
}
}
mod new_higher {
use super::*;
#[test]
fn linear_chain() {
let config: Mutex<u32> = Mutex::new(10);
let account = Mutex::new_higher(20u32, &config);
let mut txn = Mutex::new_higher(30u32, &account);
lock_scope(|key| {
let (cfg_val, key) = key.lock_with(&config, |g| *g);
let (acct_val, key) = key.lock_with(&account, |g| *g);
let (tx_val, _key) = key.lock_with(&txn, |g| *g);
assert_eq!(cfg_val + acct_val + tx_val, 60);
});
assert_eq!(*txn.get_mut(), 30);
}
#[test]
fn siblings() {
let root: Mutex<u32> = Mutex::new(100);
let mut child_a = Mutex::new_higher(10u32, &root);
let mut child_b = Mutex::new_higher(20u32, &root);
let set = LockSet::new((&child_a, &child_b));
lock_scope(|key| {
let (root_val, key) = key.lock_with(&root, |g| *g);
let ((mut a, mut b), _key) = key.lock(&set);
*a += root_val;
*b += root_val;
});
assert_eq!(*child_a.get_mut(), 110);
assert_eq!(*child_b.get_mut(), 120);
}
#[test]
fn skip_levels() {
let root: Mutex<u32> = Mutex::new(1);
let mid = Mutex::new_higher(2u32, &root);
let deep = Mutex::new_higher(3u32, &mid);
lock_scope(|key| {
let (r, key) = key.lock_with(&root, |g| *g);
let (d, _key) = key.lock_with(&deep, |g| *g);
assert_eq!(r + d, 4);
});
}
#[test]
fn same_level_parents() {
let config: Mutex<u32> = Mutex::new(10);
let logger: Mutex<u32> = Mutex::new(20);
let mut reconciler = Mutex::new_higher(0u32, (&config, &logger));
let set = LockSet::new((&config, &logger));
lock_scope(|key| {
let ((cfg, log), key) = key.lock(&set);
let ((), _key) = key.lock_with(&reconciler, |mut r| {
*r = *cfg + *log;
});
});
assert_eq!(*reconciler.get_mut(), 30);
}
#[test]
fn different_level_parents() {
let root: Mutex<u32> = Mutex::new(1);
let mid = Mutex::new_higher(2u32, &root);
let deep = Mutex::new_higher(3u32, &mid);
let shallow = Mutex::new_higher(4u32, &root);
let combined = Mutex::new_higher(0u32, (&deep, &shallow));
lock_scope(|key| {
let (r, key) = key.lock_with(&root, |g| *g);
let (c, _key) = key.lock_with(&combined, |mut g| {
*g = r;
*g
});
assert_eq!(c, 1);
});
}
#[test]
fn type_alias_levels() {
type Database = surelock::level::Level<3>;
let db: Mutex<String, Database> = Mutex::new("mydb".into());
let table = Mutex::new_higher("users".to_string(), &db);
lock_scope(|key| {
let (db_name, key) = key.lock_with(&db, |g| g.clone());
let (tbl_name, _key) = key.lock_with(&table, |g| g.clone());
assert_eq!(format!("{db_name}.{tbl_name}"), "mydb.users");
});
}
#[test]
fn method_syntax() {
let config: Mutex<u32> = Mutex::new(10);
let account = config.new_higher(20u32);
let mut txn = account.new_higher(30u32);
lock_scope(|key| {
let (cfg, key) = key.lock_with(&config, |g| *g);
let (acct, key) = key.lock_with(&account, |g| *g);
let (tx, _key) = key.lock_with(&txn, |g| *g);
assert_eq!(cfg + acct + tx, 60);
});
assert_eq!(*txn.get_mut(), 30);
}
#[test]
fn method_syntax_arc() {
let parent: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let child = parent.new_higher(20u32);
lock_scope(|key| {
let (p, key) = key.lock(&parent);
let (c, _key) = key.lock(&child);
assert_eq!(*p + *c, 30);
});
}
#[test]
fn method_syntax_tuple() {
let a: Mutex<u32> = Mutex::new(1);
let b = a.new_higher(2u32);
let child = (&a, &b).new_higher(3u32);
lock_scope(|key| {
let (v, _key) = key.lock_with(&child, |g| *g);
assert_eq!(v, 3);
});
}
}
mod concurrent {
use super::*;
#[test]
fn threads_no_deadlock() {
let a: Arc<Mutex<u64>> = Arc::new(Mutex::new(0));
let b: Arc<Mutex<u64>> = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..8)
.map(|i| {
let a = a.clone();
let b = b.clone();
std::thread::spawn(move || {
let mut handle = KeyHandle::claim();
for _ in 0..100 {
handle.scope(|key| {
let set = LockSet::new((&*a, &*b));
let ((mut ga, mut gb), _key) = key.lock(&set);
*ga += i;
*gb += i;
});
}
})
})
.collect();
for h in handles {
h.join().expect("thread panicked");
}
let mut a = Arc::try_unwrap(a).expect("arc still shared");
let mut b = Arc::try_unwrap(b).expect("arc still shared");
assert_eq!(*a.get_mut(), 2800);
assert_eq!(*b.get_mut(), 2800);
}
}
#[cfg(feature = "escape-hatch")]
mod escape_hatch {
use super::*;
#[test]
fn unchecked_lock_bypasses_ordering() {
let m: Mutex<u32> = Mutex::new(42);
let mut guard = m.unchecked_lock();
*guard += 1;
drop(guard);
let mut m = m;
assert_eq!(*m.get_mut(), 43);
}
}
mod smart_pointers {
use super::*;
#[test]
fn arc_mutex_lock() {
let shared: Arc<Mutex<u32>> = Arc::new(Mutex::new(42));
lock_scope(|key| {
let (mut guard, _key) = key.lock(&shared);
*guard += 1;
});
let mut m = Arc::try_unwrap(shared).unwrap();
assert_eq!(*m.get_mut(), 43);
}
#[test]
fn arc_mutex_lock_with() {
let shared: Arc<Mutex<u32>> = Arc::new(Mutex::new(42));
let val = lock_scope(|key| {
let (v, _key) = key.lock_with(&shared, |g| *g);
v
});
assert_eq!(val, 42);
}
#[test]
fn arc_mutex_new_higher() {
let parent: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let child = Mutex::new_higher(20u32, &parent);
lock_scope(|key| {
let (p, key) = key.lock_with(&parent, |g| *g);
let (c, _key) = key.lock_with(&child, |g| *g);
assert_eq!(p + c, 30);
});
}
#[test]
fn arc_mutex_new_higher_mixed_tuple() {
let bare: Mutex<u32> = Mutex::new(1);
let shared: Arc<Mutex<u32>> = Arc::new(Mutex::new(2));
let child = Mutex::new_higher(3u32, (&bare, &shared));
lock_scope(|key| {
let (v, _key) = key.lock_with(&child, |g| *g);
assert_eq!(v, 3);
});
}
#[test]
fn arc_mutex_cross_thread_no_deref() {
let shared: Arc<Mutex<u64>> = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..4)
.map(|i| {
let m = shared.clone();
std::thread::spawn(move || {
let mut handle = KeyHandle::claim();
for _ in 0..100 {
handle.scope(|key| {
let (mut guard, _key) = key.lock(&m);
*guard += i;
});
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let mut m = Arc::try_unwrap(shared).unwrap();
assert_eq!(*m.get_mut(), 600);
}
#[test]
fn rc_mutex_lock() {
let shared: Rc<Mutex<u32>> = Rc::new(Mutex::new(42));
lock_scope(|key| {
let (mut guard, _key) = key.lock(&shared);
*guard += 1;
});
let mut m = Rc::try_unwrap(shared).unwrap();
assert_eq!(*m.get_mut(), 43);
}
#[test]
fn rc_mutex_lock_with() {
let shared: Rc<Mutex<u32>> = Rc::new(Mutex::new(42));
let val = lock_scope(|key| {
let (v, _key) = key.lock_with(&shared, |g| *g);
v
});
assert_eq!(val, 42);
}
#[test]
fn rc_mutex_new_higher() {
let parent: Rc<Mutex<u32>> = Rc::new(Mutex::new(10));
let child = parent.new_higher(20u32);
lock_scope(|key| {
let (p, key) = key.lock(&parent);
let (c, _key) = key.lock(&child);
assert_eq!(*p + *c, 30);
});
}
#[test]
fn box_mutex_lock() {
let boxed: Box<Mutex<u32>> = Box::new(Mutex::new(42));
lock_scope(|key| {
let (mut guard, _key) = key.lock(&boxed);
*guard += 1;
});
let mut m = *boxed;
assert_eq!(*m.get_mut(), 43);
}
#[test]
fn box_mutex_new_higher() {
let parent: Box<Mutex<u32>> = Box::new(Mutex::new(10));
let child = parent.new_higher(20u32);
lock_scope(|key| {
let (p, key) = key.lock(&parent);
let (c, _key) = key.lock(&child);
assert_eq!(*p + *c, 30);
});
}
#[test]
fn arc_tuple_lockset() {
let a: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let b: Arc<Mutex<u32>> = Arc::new(Mutex::new(20));
let set = LockSet::new((&a, &b));
lock_scope(|key| {
let ((ga, gb), _key) = key.lock(&set);
assert_eq!(*ga + *gb, 30);
});
}
#[test]
fn arc_tuple_lock_with() {
let a: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let b: Arc<Mutex<u32>> = Arc::new(Mutex::new(20));
let sum = lock_scope(|key| {
let (s, _key) = key.lock_with(&(&a, &b), |(ga, gb)| *ga + *gb);
s
});
assert_eq!(sum, 30);
}
#[test]
fn mixed_arc_bare_tuple_lockset() {
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let bare: Mutex<u32> = Mutex::new(20);
let set = LockSet::new((&arc, &bare));
lock_scope(|key| {
let ((ga, gb), _key) = key.lock(&set);
assert_eq!(*ga + *gb, 30);
});
}
#[test]
fn arc_3tuple_lock_with() {
let a: Arc<Mutex<u32>> = Arc::new(Mutex::new(1));
let b: Arc<Mutex<u32>> = Arc::new(Mutex::new(2));
let c: Arc<Mutex<u32>> = Arc::new(Mutex::new(3));
let sum = lock_scope(|key| {
let (s, _key) = key.lock_with(&(&a, &b, &c), |(ga, gb, gc)| *ga + *gb + *gc);
s
});
assert_eq!(sum, 6);
}
#[test]
fn rc_tuple_lockset() {
let a: Rc<Mutex<u32>> = Rc::new(Mutex::new(10));
let b: Rc<Mutex<u32>> = Rc::new(Mutex::new(20));
let set = LockSet::new((&a, &b));
lock_scope(|key| {
let ((ga, gb), _key) = key.lock(&set);
assert_eq!(*ga + *gb, 30);
});
}
#[test]
fn box_tuple_lockset() {
let a: Box<Mutex<u32>> = Box::new(Mutex::new(10));
let b: Box<Mutex<u32>> = Box::new(Mutex::new(20));
let set = LockSet::new((&a, &b));
lock_scope(|key| {
let ((ga, gb), _key) = key.lock(&set);
assert_eq!(*ga + *gb, 30);
});
}
#[test]
fn mixed_rc_bare_tuple_lock_with() {
let rc: Rc<Mutex<u32>> = Rc::new(Mutex::new(10));
let bare: Mutex<u32> = Mutex::new(20);
let sum = lock_scope(|key| {
let (s, _key) = key.lock_with(&(&rc, &bare), |(ga, gb)| *ga + *gb);
s
});
assert_eq!(sum, 30);
}
#[test]
fn mixed_arc_rc_tuple_lock_with() {
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let rc: Rc<Mutex<u32>> = Rc::new(Mutex::new(20));
let sum = lock_scope(|key| {
let (s, _key) = key.lock_with(&(&arc, &rc), |(ga, gb)| *ga + *gb);
s
});
assert_eq!(sum, 30);
}
#[test]
fn mixed_box_arc_tuple_lockset() {
let bx: Box<Mutex<u32>> = Box::new(Mutex::new(10));
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(20));
let set = LockSet::new((&bx, &arc));
lock_scope(|key| {
let ((ga, gb), _key) = key.lock(&set);
assert_eq!(*ga + *gb, 30);
});
}
#[test]
fn arc_multi_level_lockset() {
let low: Arc<Mutex<u32>> = Arc::new(Mutex::new(10));
let high: Arc<Mutex<u32, surelock::level::Level<1>>> = Arc::new(Mutex::new(20));
let set = LockSet::new((&low, &high));
lock_scope(|key| {
let ((gl, gh), _key) = key.lock(&set);
assert_eq!(*gl + *gh, 30);
});
}
#[test]
fn box_mutex_lock_with() {
let boxed: Box<Mutex<u32>> = Box::new(Mutex::new(42));
let val = lock_scope(|key| {
let (v, _key) = key.lock_with(&boxed, |g| *g);
v
});
assert_eq!(val, 42);
}
#[test]
fn new_higher_arc_arc_tuple() {
let a: Arc<Mutex<u32>> = Arc::new(Mutex::new(1));
let b: Arc<Mutex<u32>> = Arc::new(Mutex::new(2));
let child = Mutex::new_higher(3u32, (&a, &b));
lock_scope(|key| {
let (v, _key) = key.lock_with(&child, |g| *g);
assert_eq!(v, 3);
});
}
#[test]
fn new_higher_rc_bare_tuple() {
let rc: Rc<Mutex<u32>> = Rc::new(Mutex::new(1));
let bare: Mutex<u32> = Mutex::new(2);
let child = Mutex::new_higher(3u32, (&rc, &bare));
lock_scope(|key| {
let (v, _key) = key.lock_with(&child, |g| *g);
assert_eq!(v, 3);
});
}
}
mod raw_mutex_try_lock {
use super::*;
#[test]
fn succeeds_when_unlocked() {
let raw = surelock::raw_mutex::std_mutex::StdMutex::new();
let guard = raw.try_lock();
assert!(guard.is_some(), "try_lock should succeed on unlocked mutex");
}
#[test]
fn fails_when_locked() {
let raw = surelock::raw_mutex::std_mutex::StdMutex::new();
let _guard = raw.lock();
let result = raw.try_lock();
assert!(result.is_none(), "try_lock should return None when locked");
}
}
mod display_and_debug {
use super::*;
#[test]
fn lockid_display() {
let m: Mutex<u32> = Mutex::new(0);
let id_str = format!("{}", m.id());
let _: u64 = id_str
.parse()
.expect("LockId Display should produce a number");
}
#[test]
fn mutex_guard_debug() {
lock_scope(|key| {
let m: Mutex<u32> = Mutex::new(42);
let (guard, _key) = key.lock(&m);
let dbg = format!("{guard:?}");
assert_eq!(dbg, "42");
});
}
#[test]
fn mutex_debug() {
let m: Mutex<u32> = Mutex::new(0);
let dbg = format!("{m:?}");
assert!(dbg.starts_with("Mutex"));
assert!(dbg.contains("id"));
}
#[test]
fn mutex_key_debug() {
lock_scope(|key| {
let dbg = format!("{key:?}");
assert!(dbg.contains("MutexKey"));
});
}
#[test]
fn lockset_debug() {
let m: Mutex<u32> = Mutex::new(0);
let set = LockSet::new(&m);
let dbg = format!("{set:?}");
assert!(dbg.starts_with("LockSet"));
}
}
#[cfg(feature = "lock-api")]
mod lock_api_backend {
use super::*;
#[test]
fn spin_mutex_lock() {
type SpinMutex<T> = Mutex<T, surelock::level::Base, spin::Mutex<()>>;
let m: SpinMutex<u32> = Mutex::new(42);
lock_scope(|key| {
let (mut guard, _key) = key.lock(&m);
*guard += 1;
});
let mut m = m;
assert_eq!(*m.get_mut(), 43);
}
#[test]
fn spin_mutex_new_higher() {
type SpinMutex<T, Lvl = surelock::level::Base> = Mutex<T, Lvl, spin::Mutex<()>>;
let parent: SpinMutex<u32> = Mutex::new(10);
let child = parent.new_higher(20u32);
lock_scope(|key| {
let (p, key) = key.lock(&parent);
let (c, _key) = key.lock(&child);
assert_eq!(*p + *c, 30);
});
}
}
mod mutable_access {
use super::*;
#[test]
fn get_mut_without_lock() {
let mut m: Mutex<String> = Mutex::new("hello".into());
m.get_mut().push_str(" world");
assert_eq!(m.into_inner(), "hello world");
}
}