#![cfg(loom)]
use loom::sync::atomic::{AtomicUsize, Ordering};
use loom::sync::{Arc, RwLock};
use std::collections::HashMap;
fn increment_via_double_checked(map: &Arc<RwLock<HashMap<u8, Arc<AtomicUsize>>>>) {
let counter = {
let read = map.read().unwrap();
read.get(&0).cloned()
};
let counter = counter.unwrap_or_else(|| {
let mut write = map.write().unwrap();
Arc::clone(
write
.entry(0)
.or_insert_with(|| Arc::new(AtomicUsize::new(0))),
)
});
let _previous = counter.fetch_add(1, Ordering::Relaxed);
}
#[test]
fn loom_counters_for_inserts_exactly_once_under_concurrent_first_dispatch() {
loom::model(|| {
let map: Arc<RwLock<HashMap<u8, Arc<AtomicUsize>>>> = Arc::new(RwLock::new(HashMap::new()));
let map1 = map.clone();
let t1 = loom::thread::spawn(move || increment_via_double_checked(&map1));
let map2 = map.clone();
let t2 = loom::thread::spawn(move || increment_via_double_checked(&map2));
t1.join().unwrap();
t2.join().unwrap();
let map = map.read().unwrap();
let counter = map
.get(&0)
.expect("counter must exist after either thread inserts");
assert_eq!(counter.load(Ordering::Relaxed), 2);
});
}
#[test]
fn loom_counters_for_returns_same_arc_across_concurrent_callers() {
loom::model(|| {
let map: Arc<RwLock<HashMap<u8, Arc<AtomicUsize>>>> = Arc::new(RwLock::new(HashMap::new()));
let map1 = map.clone();
let map2 = map.clone();
let t1 = loom::thread::spawn(move || acquire_counter(&map1));
let t2 = loom::thread::spawn(move || acquire_counter(&map2));
let c1 = t1.join().unwrap();
let c2 = t2.join().unwrap();
assert!(
Arc::ptr_eq(&c1, &c2),
"concurrent callers received distinct Arcs for the same key"
);
});
}
fn acquire_counter(map: &Arc<RwLock<HashMap<u8, Arc<AtomicUsize>>>>) -> Arc<AtomicUsize> {
let counter = {
let read = map.read().unwrap();
read.get(&0).cloned()
};
counter.unwrap_or_else(|| {
let mut write = map.write().unwrap();
Arc::clone(
write
.entry(0)
.or_insert_with(|| Arc::new(AtomicUsize::new(0))),
)
})
}