#![allow(unexpected_cfgs)]
#[cfg(loom)]
mod loom_tests {
use loom::sync::Arc;
use loom::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use loom::thread;
struct PageCommitModel {
page_lock: AtomicU64,
commit_idx: AtomicU64,
fsync_done: AtomicBool,
data_written: AtomicU64,
}
impl PageCommitModel {
fn new() -> Self {
Self {
page_lock: AtomicU64::new(0),
commit_idx: AtomicU64::new(0),
fsync_done: AtomicBool::new(false),
data_written: AtomicU64::new(0),
}
}
}
#[test]
fn t1_correct_ordering_fsync_before_publish() {
loom::model(|| {
let model = Arc::new(PageCommitModel::new());
let seq: u64 = 1;
let txn_id: u64 = 42;
let writer_model = Arc::clone(&model);
let reader_model = Arc::clone(&model);
let writer = thread::spawn(move || {
let m = &*writer_model;
let cas_result =
m.page_lock
.compare_exchange(0, txn_id, Ordering::AcqRel, Ordering::Acquire);
assert!(cas_result.is_ok(), "writer must acquire lock");
m.data_written.store(seq, Ordering::Relaxed);
m.fsync_done.store(true, Ordering::Release);
m.commit_idx.store(seq, Ordering::Release);
let release_result =
m.page_lock
.compare_exchange(txn_id, 0, Ordering::AcqRel, Ordering::Relaxed);
assert!(release_result.is_ok(), "writer must release lock");
});
let reader = thread::spawn(move || {
let m = &*reader_model;
let observed_seq = m.commit_idx.load(Ordering::Acquire);
if observed_seq > 0 {
let fsync = m.fsync_done.load(Ordering::Acquire);
assert!(
fsync,
"INVARIANT VIOLATION: reader saw commit_idx={observed_seq} \
but fsync_done=false"
);
let data = m.data_written.load(Ordering::Acquire);
assert_eq!(
data, observed_seq,
"INVARIANT VIOLATION: reader saw commit_idx={observed_seq} \
but data_written={data}"
);
}
});
writer.join().unwrap();
reader.join().unwrap();
});
}
#[test]
fn t2_two_writers_sequential_lock_handoff() {
loom::model(|| {
let model = Arc::new(PageCommitModel::new());
let w1_model = Arc::clone(&model);
let w2_model = Arc::clone(&model);
let reader_model = Arc::clone(&model);
let w1 = thread::spawn(move || {
let m = &*w1_model;
let txn: u64 = 10;
if m.page_lock
.compare_exchange(0, txn, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
m.data_written.store(1, Ordering::Relaxed);
m.fsync_done.store(true, Ordering::Release);
m.commit_idx.store(1, Ordering::Release);
let _ =
m.page_lock
.compare_exchange(txn, 0, Ordering::AcqRel, Ordering::Relaxed);
}
});
let w2 = thread::spawn(move || {
let m = &*w2_model;
let txn: u64 = 20;
if m.page_lock
.compare_exchange(0, txn, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
m.data_written.store(2, Ordering::Relaxed);
m.fsync_done.store(true, Ordering::Release);
m.commit_idx.store(2, Ordering::Release);
let _ =
m.page_lock
.compare_exchange(txn, 0, Ordering::AcqRel, Ordering::Relaxed);
}
});
let reader = thread::spawn(move || {
let m = &*reader_model;
let observed = m.commit_idx.load(Ordering::Acquire);
if observed > 0 {
let fsync = m.fsync_done.load(Ordering::Acquire);
assert!(
fsync,
"INVARIANT VIOLATION: commit_idx={observed} but fsync_done=false"
);
let data = m.data_written.load(Ordering::Acquire);
assert!(
data >= observed || data > 0,
"INVARIANT VIOLATION: commit_idx={observed} but data_written={data}"
);
}
});
w1.join().unwrap();
w2.join().unwrap();
reader.join().unwrap();
});
}
#[test]
fn t3_weakened_ordering_publish_before_fsync_detected() {
let found_violation = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let found_clone = std::sync::Arc::clone(&found_violation);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
let found_outer = found_clone;
loom::model(move || {
let found_inner = std::sync::Arc::clone(&found_outer);
let model = Arc::new(PageCommitModel::new());
let seq: u64 = 1;
let txn_id: u64 = 42;
let writer_model = Arc::clone(&model);
let reader_model = Arc::clone(&model);
let writer = thread::spawn(move || {
let m = &*writer_model;
let _ = m.page_lock.compare_exchange(
0,
txn_id,
Ordering::AcqRel,
Ordering::Acquire,
);
m.data_written.store(seq, Ordering::Relaxed);
m.commit_idx.store(seq, Ordering::Release);
m.fsync_done.store(true, Ordering::Release);
let _ = m.page_lock.compare_exchange(
txn_id,
0,
Ordering::AcqRel,
Ordering::Relaxed,
);
});
let reader = thread::spawn(move || {
let m = &*reader_model;
let observed = m.commit_idx.load(Ordering::Acquire);
if observed > 0 {
let fsync = m.fsync_done.load(Ordering::Acquire);
if !fsync {
found_inner.store(true, std::sync::atomic::Ordering::SeqCst);
panic!(
"EXPECTED VIOLATION: commit_idx={observed} but fsync_done=false"
);
}
}
});
writer.join().unwrap();
reader.join().unwrap();
});
}));
assert!(
result.is_err() || found_violation.load(std::sync::atomic::Ordering::SeqCst),
"BUG: weakened ordering was NOT detected by the model — \
the model is not actually checking the invariant"
);
}
#[test]
fn t4_batch_update_fence_then_relaxed_stores() {
loom::model(|| {
let data = Arc::new(AtomicU64::new(0));
let commit_a = Arc::new(AtomicU64::new(0));
let commit_b = Arc::new(AtomicU64::new(0));
let fsync = Arc::new(AtomicBool::new(false));
let w_data = Arc::clone(&data);
let w_ca = Arc::clone(&commit_a);
let w_cb = Arc::clone(&commit_b);
let w_fsync = Arc::clone(&fsync);
let r_data = Arc::clone(&data);
let r_ca = Arc::clone(&commit_a);
let r_cb = Arc::clone(&commit_b);
let r_fsync = Arc::clone(&fsync);
let writer = thread::spawn(move || {
w_data.store(1, Ordering::Relaxed);
w_fsync.store(true, Ordering::Release);
loom::sync::atomic::fence(Ordering::Release);
w_ca.store(1, Ordering::Relaxed);
w_cb.store(1, Ordering::Relaxed);
});
let reader = thread::spawn(move || {
let a = r_ca.load(Ordering::Acquire);
let b = r_cb.load(Ordering::Acquire);
if a > 0 || b > 0 {
let f = r_fsync.load(Ordering::Acquire);
assert!(
f,
"INVARIANT VIOLATION: saw commit_a={a} commit_b={b} \
but fsync_done=false"
);
let d = r_data.load(Ordering::Acquire);
assert!(d > 0, "INVARIANT VIOLATION: saw commit but data_written=0");
}
});
writer.join().unwrap();
reader.join().unwrap();
});
}
#[test]
fn t5_lock_handoff_data_visibility() {
loom::model(|| {
let lock = Arc::new(AtomicU64::new(0));
let data = Arc::new(AtomicU64::new(0));
let w1_lock = Arc::clone(&lock);
let w1_data = Arc::clone(&data);
let w2_lock = Arc::clone(&lock);
let w2_data = Arc::clone(&data);
let w1 = thread::spawn(move || {
if w1_lock
.compare_exchange(0, 1, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
w1_data.store(42, Ordering::Relaxed);
let _ = w1_lock.compare_exchange(1, 0, Ordering::AcqRel, Ordering::Relaxed);
}
});
let w2 = thread::spawn(move || {
if w2_lock
.compare_exchange(0, 2, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
let val = w2_data.load(Ordering::Relaxed);
assert!(val == 0 || val == 42, "unexpected data value: {val}");
let _ = w2_lock.compare_exchange(2, 0, Ordering::AcqRel, Ordering::Relaxed);
}
});
w1.join().unwrap();
w2.join().unwrap();
});
}
}
#[cfg(not(loom))]
mod std_tests {
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering, fence};
#[test]
fn smoke_correct_ordering() {
let commit_idx = Arc::new(AtomicU64::new(0));
let fsync_done = Arc::new(AtomicBool::new(false));
let data = Arc::new(AtomicU64::new(0));
let w_ci = Arc::clone(&commit_idx);
let w_fs = Arc::clone(&fsync_done);
let w_d = Arc::clone(&data);
let writer = std::thread::spawn(move || {
w_d.store(1, Ordering::Relaxed);
w_fs.store(true, Ordering::Release);
w_ci.store(1, Ordering::Release);
});
let r_ci = Arc::clone(&commit_idx);
let r_fs = Arc::clone(&fsync_done);
let r_d = Arc::clone(&data);
let reader = std::thread::spawn(move || {
let seq = r_ci.load(Ordering::Acquire);
if seq > 0 {
assert!(r_fs.load(Ordering::Acquire));
assert!(r_d.load(Ordering::Acquire) > 0);
}
});
writer.join().unwrap();
reader.join().unwrap();
}
#[test]
fn smoke_batch_update_fence() {
let ca = Arc::new(AtomicU64::new(0));
let cb = Arc::new(AtomicU64::new(0));
let fsync_done = Arc::new(AtomicBool::new(false));
let w_ca = Arc::clone(&ca);
let w_cb = Arc::clone(&cb);
let w_fs = Arc::clone(&fsync_done);
let writer = std::thread::spawn(move || {
w_fs.store(true, Ordering::Release);
fence(Ordering::Release);
w_ca.store(1, Ordering::Relaxed);
w_cb.store(1, Ordering::Relaxed);
});
let r_ca = Arc::clone(&ca);
let r_cb = Arc::clone(&cb);
let r_fs = Arc::clone(&fsync_done);
let reader = std::thread::spawn(move || {
let a = r_ca.load(Ordering::Acquire);
let b = r_cb.load(Ordering::Acquire);
if a > 0 || b > 0 {
assert!(r_fs.load(Ordering::Acquire));
}
});
writer.join().unwrap();
reader.join().unwrap();
}
}