use crate::counter::ConnectionCounter;
use crate::error::WsError;
use loom::sync::atomic::{AtomicUsize, Ordering};
use loom::sync::Arc;
#[test]
fn loom_inc_dec_pairs_balance_to_zero() {
loom::model(|| {
let counter = ConnectionCounter::new();
let c2 = counter.clone();
let handle = loom::thread::spawn(move || {
c2.increment(None).unwrap();
c2.decrement();
});
counter.increment(None).unwrap();
counter.decrement();
handle.join().unwrap();
assert_eq!(counter.get(), 0);
});
}
#[test]
fn loom_increment_respects_limit_under_race() {
loom::model(|| {
let counter = ConnectionCounter::new();
let ok_count = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..2 {
let counter = counter.clone();
let ok_count = Arc::clone(&ok_count);
handles.push(loom::thread::spawn(move || {
match counter.increment(Some(1)) {
Ok(_) => {
ok_count.fetch_add(1, Ordering::Relaxed);
}
Err(WsError::TooManyConnections) => {}
Err(_) => panic!("unexpected error"),
}
}));
}
for h in handles {
h.join().unwrap();
}
assert_eq!(ok_count.load(Ordering::Relaxed), 1);
assert_eq!(counter.get(), 1);
counter.decrement();
counter.decrement(); assert_eq!(counter.get(), 0);
});
}
#[test]
fn loom_decrement_races_capped_increment_never_under_or_overshoots() {
loom::model(|| {
let counter = ConnectionCounter::new();
let a = {
let counter = counter.clone();
loom::thread::spawn(move || counter.increment(Some(1)))
};
let b = {
let counter = counter.clone();
loom::thread::spawn(move || counter.decrement())
};
let incremented = a.join().unwrap();
b.join().unwrap();
match incremented {
Ok(_) | Err(WsError::TooManyConnections) => {}
Err(_) => panic!("unexpected error"),
}
let final_count = counter.get();
assert!(
final_count <= 1,
"capped increment raced past the limit: {final_count}"
);
assert!(
final_count == 0 || final_count == 1,
"inc/dec race must land on 0 (dec after inc) or 1 (dec before inc)"
);
});
}
#[test]
fn loom_three_way_cas_race_yields_exactly_limit_winners() {
loom::model(|| {
let counter = ConnectionCounter::new();
let winners = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..3 {
let counter = counter.clone();
let winners = Arc::clone(&winners);
handles.push(loom::thread::spawn(move || {
match counter.increment(Some(2)) {
Ok(_) => {
winners.fetch_add(1, Ordering::Relaxed);
}
Err(WsError::TooManyConnections) => {}
Err(_) => panic!("unexpected error"),
}
}));
}
for h in handles {
h.join().unwrap();
}
assert_eq!(
winners.load(Ordering::Relaxed),
2,
"exactly two CAS winners"
);
assert_eq!(counter.get(), 2);
for _ in 0..3 {
counter.decrement(); }
assert_eq!(counter.get(), 0, "teardown must saturate at zero");
});
}