#![cfg(all(
feature = "alloc-core",
feature = "alloc-global",
feature = "alloc-xthread",
feature = "numa-aware"
))]
use core::alloc::Layout;
use std::sync::mpsc;
use std::thread;
use sefer_alloc::alloc_core::{numa, AllocCore};
use sefer_alloc::Heap;
fn numa_test_enabled() -> bool {
std::env::var("SEFER_NUMA_TEST").as_deref() == Ok("1")
}
#[test]
fn same_thread_segments_same_node() {
if !numa_test_enabled() {
eprintln!(
"SEFER_NUMA_TEST != 1 — пропускаю \
(нужна multi-NUMA топология: QEMU -numa или numa=fake)"
);
return;
}
let mut core = AllocCore::new().expect("AllocCore::new failed");
let layout = Layout::from_size_align(64, 8).unwrap();
let ptr_a = core.alloc(layout);
assert!(!ptr_a.is_null(), "first alloc returned null");
let ptr_b = core.alloc(layout);
assert!(!ptr_b.is_null(), "second alloc returned null");
let node_a = core
.dbg_node_id_for(ptr_a)
.expect("dbg_node_id_for: ptr_a not in a known segment");
let node_b = core
.dbg_node_id_for(ptr_b)
.expect("dbg_node_id_for: ptr_b not in a known segment");
eprintln!(
"same_thread_segments_same_node: node_a={node_a}, node_b={node_b}, NO_NODE={}",
numa::NO_NODE
);
assert_eq!(
node_a, node_b,
"same-thread allocations landed in segments of different NUMA nodes \
({node_a} vs {node_b}) — NUMA preference in find_segment_with_free broken"
);
unsafe {
std::ptr::write_bytes(ptr_a, 0xAA, layout.size());
std::ptr::write_bytes(ptr_b, 0xBB, layout.size());
assert_eq!((ptr_a as *const u8).read(), 0xAA, "ptr_a readback failed");
assert_eq!((ptr_b as *const u8).read(), 0xBB, "ptr_b readback failed");
}
core.dealloc(ptr_a, layout);
core.dealloc(ptr_b, layout);
}
#[test]
fn cross_node_handoff_safe() {
if !numa_test_enabled() {
eprintln!(
"SEFER_NUMA_TEST != 1 — пропускаю \
(нужна multi-NUMA топология: QEMU -numa или numa=fake)"
);
return;
}
const N: usize = 64;
let layout = Layout::from_size_align(128, 8).unwrap();
let mut heap = Heap::new().expect("Heap::new failed");
let mut ptrs: Vec<(usize, u8)> = Vec::with_capacity(N);
for i in 0..N {
let ptr = heap.alloc(layout);
assert!(!ptr.is_null(), "heap alloc #{i} returned null");
let pat = ((i * 7 + 0xA5) & 0xFF) as u8;
unsafe {
std::ptr::write_bytes(ptr, pat, layout.size());
assert_eq!(
(ptr as *const u8).read(),
pat,
"main thread: pattern readback failed at block {i}"
);
}
ptrs.push((ptr as usize, pat));
}
let (tx, rx) = mpsc::channel::<Vec<(usize, u8)>>();
tx.send(ptrs).expect("channel send failed");
drop(tx);
let consumer = thread::spawn(move || {
let blocks = rx.recv().expect("channel recv failed");
for (addr, pat) in blocks {
let ptr = addr as *mut u8;
unsafe {
assert_eq!(
(ptr as *const u8).read(),
pat,
"consumer: pattern corrupted before cross-thread free"
);
}
Heap::dealloc_any_thread(ptr, layout);
}
});
consumer.join().expect("consumer thread panicked");
let drain_ptr = heap.alloc(layout);
assert!(
!drain_ptr.is_null(),
"post-handoff alloc returned null (ring drain may have failed)"
);
unsafe {
std::ptr::write_bytes(drain_ptr, 0xCC, layout.size());
assert_eq!(
(drain_ptr as *const u8).read(),
0xCC,
"post-handoff alloc readback failed"
);
}
heap.dealloc(drain_ptr, layout);
}
#[test]
fn find_segment_prefers_local_node() {
if !numa_test_enabled() {
eprintln!(
"SEFER_NUMA_TEST != 1 — пропускаю \
(нужна multi-NUMA топология: QEMU -numa или numa=fake)"
);
return;
}
const N_THREADS: usize = 2;
let (tx, rx) = mpsc::channel::<(u32, u32)>();
let mut handles = Vec::with_capacity(N_THREADS);
for _tid in 0..N_THREADS {
let tx = tx.clone();
handles.push(thread::spawn(move || {
let mut core = AllocCore::new().expect("AllocCore::new in thread failed");
let observed_node = numa::current_node();
let layout = Layout::from_size_align(64, 8).unwrap();
let ptr = core.alloc(layout);
assert!(!ptr.is_null(), "thread alloc returned null");
let stamped_node = core
.dbg_node_id_for(ptr)
.expect("dbg_node_id_for returned None — ptr not in known segment");
unsafe {
std::ptr::write_bytes(ptr, 0xDE, layout.size());
assert_eq!((ptr as *const u8).read(), 0xDE, "readback failed in thread");
}
tx.send((observed_node, stamped_node))
.expect("result channel send failed");
core.dealloc(ptr, layout);
}));
}
drop(tx);
for h in handles {
h.join().expect("worker thread panicked");
}
let mut n_results = 0usize;
for (observed, stamped) in rx {
eprintln!(
"find_segment_prefers_local_node: \
observed_current_node={observed}, stamped_node_id={stamped}"
);
assert_eq!(
stamped, observed,
"segment node_id ({stamped}) != current_node() ({observed}) — \
NUMA stamping in reserve_small_segment is broken"
);
n_results += 1;
}
assert_eq!(
n_results, N_THREADS,
"expected results from {N_THREADS} threads, got {n_results}"
);
}