use std::{thread, time::Duration};
use bytes::Bytes;
use squib_host::{
PageRequest, PageSource, PageSourceError, Pager, PagerConfig, PagerStatsSnapshot, PrewarmList,
spawn_mach_server,
};
#[derive(Debug)]
struct ConstSource(Bytes);
impl PageSource for ConstSource {
fn fetch(&self, _req: &PageRequest) -> Result<Bytes, PageSourceError> {
Ok(self.0.clone())
}
}
fn make_pager() -> Pager {
Pager::new(
Box::new(ConstSource(Bytes::from(vec![0u8; 16 * 1024]))),
PagerConfig {
poll_interval: Duration::from_millis(50),
prewarm: PrewarmList::default(),
},
)
}
#[test]
fn pager_lifecycle_accepts_shutdown_in_lldb_after_ordering() {
let pager = make_pager();
pager.register_region(0x8000_0000, 0x1000_0000);
let join = spawn_mach_server(&pager).expect("spawn pager");
thread::sleep(Duration::from_millis(150));
pager.request_shutdown();
wait_for_join(join, Duration::from_secs(5))
.expect("pager should join cleanly after request_shutdown");
}
#[test]
fn pager_lifecycle_accepts_shutdown_in_lldb_before_ordering() {
let pager = make_pager();
pager.register_region(0x8000_0000, 0x1000_0000);
let join = spawn_mach_server(&pager).expect("spawn pager");
thread::sleep(Duration::from_millis(120));
pager.request_shutdown();
wait_for_join(join, Duration::from_secs(5))
.expect("pager should join cleanly after request_shutdown");
}
#[test]
fn pager_serves_concurrent_faults_without_corrupting_stats() {
let pager = make_pager();
pager.register_region(0x8000_0000, 0x1000_0000);
let pager = std::sync::Arc::new(pager);
let mut handles = vec![];
for t in 0..4u64 {
let p = pager.clone();
handles.push(thread::spawn(move || {
for i in 0..100u64 {
let ipa = 0x8000_0000 + ((t * 100 + i) * 16 * 1024);
if ipa < 0x9000_0000 {
p.serve_fault(ipa, 16 * 1024).unwrap();
}
}
}));
}
for h in handles {
h.join().unwrap();
}
let s: PagerStatsSnapshot = pager.stats();
assert_eq!(s.faults, 400);
}
fn wait_for_join<T>(join: thread::JoinHandle<T>, timeout: Duration) -> Result<(), &'static str> {
let start = std::time::Instant::now();
let mut maybe_join = Some(join);
loop {
if let Some(j) = maybe_join.take() {
if j.is_finished() {
let _ = j.join().map_err(|_| "pager thread panicked")?;
return Ok(());
}
maybe_join = Some(j);
}
if start.elapsed() > timeout {
return Err("pager thread did not join within timeout");
}
thread::sleep(Duration::from_millis(20));
}
}