#![cfg(all(feature = "alloc-core", feature = "alloc-xthread"))]
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::sync::Mutex;
use sefer_alloc::alloc_core::remote_free_ring::{RemoteFreeRing, FOOTPRINT};
struct SendRing(RemoteFreeRing);
unsafe impl Send for SendRing {}
unsafe impl Sync for SendRing {}
const DEADLINE_SECS: u64 = 20;
struct Watchdog;
impl Watchdog {
fn start(label: &'static str) -> WatchdogHandle {
let done = Arc::new(AtomicBool::new(false));
let done_w = Arc::clone(&done);
let handle = std::thread::Builder::new()
.name(format!("watchdog-{label}"))
.spawn(move || {
let start = std::time::Instant::now();
while start.elapsed().as_secs() < DEADLINE_SECS {
if done_w.load(Ordering::Relaxed) {
return;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
eprintln!(
"\n[watchdog-{label}] TEST EXCEEDED {DEADLINE_SECS}s — likely deadlock. \
Aborting process to fail fast (task #36 watchdog)."
);
std::process::abort();
})
.expect("spawn watchdog");
WatchdogHandle {
done,
handle: Some(handle),
}
}
}
struct WatchdogHandle {
done: Arc<AtomicBool>,
handle: Option<std::thread::JoinHandle<()>>,
}
impl Drop for WatchdogHandle {
fn drop(&mut self) {
self.done.store(true, Ordering::Relaxed);
if let Some(h) = self.handle.take() {
let _ = h.join();
}
}
}
fn ring_buffer() -> Box<[u8]> {
let mut buf: Vec<u8> = vec![0u8; FOOTPRINT];
assert!(
buf.as_mut_ptr() as usize % core::mem::align_of::<u32>() == 0,
"ring buffer must be 4-byte aligned"
);
buf.into_boxed_slice()
}
#[test]
fn ring_isolated_mpsc_no_loss_no_dup() {
let _wd = Watchdog::start("mpsc");
const PRODUCERS: usize = 4;
const OFFSETS_PER_PRODUCER: usize = 2_000;
const OFFSET_STRIDE: u32 = 16;
let buf = Arc::new(ring_buffer());
let base = buf.as_ptr() as *mut u8;
RemoteFreeRing::init_test_buffer(base);
let ring = Arc::new(SendRing(RemoteFreeRing::over_test_buffer(base)));
let attempted = Arc::new(AtomicU64::new(0));
let succeeded = Arc::new(AtomicU64::new(0));
let reclaimed_map = Arc::new(Mutex::new(std::collections::HashMap::<u32, u32>::new()));
let reclaimed_count = Arc::new(AtomicU64::new(0));
let stop = Arc::new(AtomicBool::new(false));
let reclaimed_map_c = Arc::clone(&reclaimed_map);
let reclaimed_count_c = Arc::clone(&reclaimed_count);
let stop_c = Arc::clone(&stop);
let ring_c = Arc::clone(&ring);
let consumer = std::thread::Builder::new()
.name("ring-consumer".into())
.spawn(move || {
loop {
let mut local = 0u64;
ring_c.0.drain(|off| {
let mut m = reclaimed_map_c.lock().expect("reclaim map poisoned");
let e = m.entry(off).or_insert(0);
*e += 1;
local += 1;
});
reclaimed_count_c.fetch_add(local, Ordering::Relaxed);
if stop_c.load(Ordering::Acquire) {
let mut local2 = 0u64;
ring_c.0.drain(|off| {
let mut m = reclaimed_map_c.lock().expect("reclaim map poisoned");
let e = m.entry(off).or_insert(0);
*e += 1;
local2 += 1;
});
reclaimed_count_c.fetch_add(local2, Ordering::Relaxed);
return;
}
std::thread::yield_now();
}
})
.expect("spawn consumer");
let mut producers = Vec::with_capacity(PRODUCERS);
for p in 0..PRODUCERS {
let attempted = Arc::clone(&attempted);
let succeeded = Arc::clone(&succeeded);
let ring_p = Arc::clone(&ring);
producers.push(
std::thread::Builder::new()
.name(format!("ring-producer-{p}"))
.spawn(move || {
let band_base = (p as u32) * (OFFSETS_PER_PRODUCER as u32) * OFFSET_STRIDE;
for i in 0..OFFSETS_PER_PRODUCER {
let off = band_base + (i as u32) * OFFSET_STRIDE;
assert_ne!(off, u32::MAX, "offset must not equal the sentinel");
attempted.fetch_add(1, Ordering::Relaxed);
match ring_p.0.push(off) {
Ok(()) => {
succeeded.fetch_add(1, Ordering::Relaxed);
}
Err(_) => {
}
}
}
})
.expect("spawn producer"),
);
}
for h in producers {
h.join().expect("producer must not abort");
}
stop.store(true, Ordering::Release);
consumer.join().expect("consumer must not abort");
let attempted = attempted.load(Ordering::Acquire);
let succeeded = succeeded.load(Ordering::Acquire);
let reclaimed = reclaimed_count.load(Ordering::Acquire);
let overflow = ring.0.overflow_count() as u64;
let map = reclaimed_map.lock().expect("reclaim map poisoned");
let mut doubles = 0u64;
let mut distinct = 0u64;
for (_off, &cnt) in map.iter() {
if cnt > 1 {
doubles += 1;
}
distinct += 1;
}
assert_eq!(
doubles, 0,
"RING DOUBLE-RECLAIM: {doubles} offsets reclaimed >1 time — the ring \
re-emitted an offset it already drained (the drain wrap/clear bug, \
or ABA). This is the precise symptom task #36 disentangles."
);
assert_eq!(
reclaimed, distinct,
"reclaimed ({reclaimed}) != distinct offsets ({distinct}) — a double \
occurred despite the doubles-count being zero (bookkeeping bug)"
);
assert_eq!(
succeeded, reclaimed,
"RING LOSS: {succeeded} pushes succeeded but only {reclaimed} were \
reclaimed — an offset was pushed Ok (reserved + published) but never \
drained (the drain stop-on-unpublished break, or the wrap bug, lost it)"
);
let overflow_expected = attempted - succeeded;
assert_eq!(
overflow, overflow_expected,
"RING OVERFLOW MISMATCH: overflow counter = {overflow}, but \
attempted({attempted}) - succeeded({succeeded}) = {overflow_expected}"
);
assert_eq!(
reclaimed + overflow,
attempted,
"RING IDENTITY BROKEN: reclaimed({reclaimed}) + overflow({overflow}) \
!= attempted({attempted}) — an offset vanished into the ring (neither \
reclaimed nor overflowed)"
);
eprintln!(
"ring_isolated_mpsc: attempted={attempted} succeeded={succeeded} \
reclaimed={reclaimed} overflow={overflow} distinct={distinct}"
);
}
#[test]
fn ring_isolated_single_thread_basic() {
let _wd = Watchdog::start("basic");
let buf = ring_buffer();
let base = buf.as_ptr() as *mut u8;
RemoteFreeRing::init_test_buffer(base);
let ring = RemoteFreeRing::over_test_buffer(base);
const N: u32 = 64;
for i in 0..N {
let off = i * 16;
let r = ring.push(off);
assert!(r.is_ok(), "push of {off} failed inside RING_CAP");
}
assert_eq!(
ring.overflow_count(),
0,
"no overflow expected below RING_CAP"
);
let mut reclaimed = Vec::new();
ring.drain(|off| reclaimed.push(off));
assert_eq!(reclaimed.len(), N as usize, "drained all N");
for (i, &off) in reclaimed.iter().enumerate() {
assert_eq!(off, (i as u32) * 16, "FIFO order broken at {i}");
}
let mut second = 0;
ring.drain(|_| second += 1);
assert_eq!(second, 0, "second drain of quiescent ring must be empty");
}