use super::ack::AckTx;
use super::{AckMsg, AckRef, AckStatus, BatchId};
use crate::record::PartitionId;
use loom::sync::{Arc, Mutex};
fn recorded_ack() -> (AckRef, Arc<Mutex<Vec<AckMsg>>>) {
let recorder = Arc::new(Mutex::new(Vec::new()));
let ack = AckRef::new(
BatchId {
partition: PartitionId(3),
epoch: 1,
seq: 9,
},
99,
AckTx::Recorder(Arc::clone(&recorder)),
);
(ack, recorder)
}
#[test]
fn resolves_exactly_once_with_worst_status() {
loom::model(|| {
let (ack, recorder) = recorded_ack();
let failing = ack.clone();
let passing = ack.clone();
let t1 = loom::thread::spawn(move || {
failing.fail();
drop(failing);
});
let t2 = loom::thread::spawn(move || {
drop(passing);
});
drop(ack);
t1.join().unwrap();
t2.join().unwrap();
let msgs = recorder.lock().unwrap();
assert_eq!(msgs.len(), 1, "batch must resolve exactly once");
assert_eq!(msgs[0].status, AckStatus::Failed, "worst status must win");
assert_eq!(msgs[0].id.seq, 9);
assert_eq!(msgs[0].last_offset, 99);
});
}
#[test]
fn resolves_delivered_without_failure() {
loom::model(|| {
let (ack, recorder) = recorded_ack();
let a = ack.clone();
let b = ack.clone();
let t1 = loom::thread::spawn(move || drop(a));
let t2 = loom::thread::spawn(move || drop(b));
drop(ack);
t1.join().unwrap();
t2.join().unwrap();
let msgs = recorder.lock().unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0].status, AckStatus::Delivered);
});
}
#[test]
fn clone_drop_races_resolve_once() {
loom::model(|| {
let (ack, recorder) = recorded_ack();
let seed = ack.clone();
let t1 = loom::thread::spawn(move || {
let extra = seed.clone();
drop(seed);
drop(extra);
});
drop(ack);
t1.join().unwrap();
let msgs = recorder.lock().unwrap();
assert_eq!(msgs.len(), 1);
assert_eq!(msgs[0].status, AckStatus::Delivered);
});
}