#![cfg(loom)]
use loom::sync::{Arc, Mutex};
use loom::thread;
struct Session {
inner: Mutex<Arc<u64>>,
}
impl Session {
fn new() -> Self {
Session {
inner: Mutex::new(Arc::new(0)),
}
}
fn version(&self) -> u64 {
**self.inner.lock().unwrap()
}
fn commit(&self, base: u64, check_occ: bool) -> Option<u64> {
let mut guard = self.inner.lock().unwrap();
let current = **guard;
if check_occ && current != base {
return None; }
let new = current + 1;
*guard = Arc::new(new);
Some(new)
}
}
#[test]
fn occ_retry_commits_compose() {
loom::model(|| {
let s = Arc::new(Session::new());
let handles: Vec<_> = (0..2)
.map(|_| {
let s = s.clone();
thread::spawn(move || loop {
let base = s.version();
if s.commit(base, true).is_some() {
break;
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
assert_eq!(*s.inner.lock().unwrap().as_ref(), 2);
});
}
#[test]
fn lww_commits_are_monotonic() {
loom::model(|| {
let s = Arc::new(Session::new());
let handles: Vec<_> = (0..2)
.map(|_| {
let s = s.clone();
thread::spawn(move || {
s.commit(0, false);
})
})
.collect();
for h in handles {
h.join().unwrap();
}
assert_eq!(*s.inner.lock().unwrap().as_ref(), 2);
});
}
#[test]
fn snapshot_never_torn_under_commit() {
loom::model(|| {
let s = Arc::new(Session::new());
let sw = s.clone();
let w = thread::spawn(move || {
sw.commit(0, false);
});
let sr = s.clone();
let r = thread::spawn(move || {
let v = sr.version();
assert!(v <= 1, "snapshot saw an impossible version: {v}");
});
w.join().unwrap();
r.join().unwrap();
assert_eq!(s.version(), 1);
});
}