use std::any::Any;
use std::marker::PhantomData;
use std::path::Path;
use std::sync::{Arc, Mutex};
use anyhow::Result;
use crate::serve::kv_persist::registry::KvPersistRegistry;
use crate::serve::kv_persist::spiller::{BlockPrefixCacheSpiller, KvCacheSpill};
use crate::serve::multi_model::{EngineConfig, ModelLoader};
use crate::serve::quant_select::QuantType;
pub struct LoaderWrapper<E> {
inner: Arc<dyn ModelLoader<E>>,
registry: Arc<KvPersistRegistry>,
pending_bind: Mutex<Option<(String, QuantType)>>,
spiller: Mutex<Option<Arc<BlockPrefixCacheSpiller<E>>>>,
substitute_lock: Mutex<()>,
_phantom: PhantomData<fn(E)>,
}
impl<E> LoaderWrapper<E>
where
E: Send + Sync + 'static,
{
pub fn new(inner: Arc<dyn ModelLoader<E>>, registry: Arc<KvPersistRegistry>) -> Self {
Self {
inner,
registry,
pending_bind: Mutex::new(None),
spiller: Mutex::new(None),
substitute_lock: Mutex::new(()),
_phantom: PhantomData,
}
}
pub fn set_spiller(&self, spiller: Arc<BlockPrefixCacheSpiller<E>>) {
let mut g = self
.spiller
.lock()
.expect("LoaderWrapper::spiller Mutex poisoned");
*g = Some(spiller);
}
pub fn set_pending_bind(&self, repo: String, quant: QuantType) {
let mut g = self
.pending_bind
.lock()
.expect("LoaderWrapper::pending_bind Mutex poisoned");
*g = Some((repo, quant));
}
pub fn clear_pending_bind(&self) {
let mut g = self
.pending_bind
.lock()
.expect("LoaderWrapper::pending_bind Mutex poisoned");
*g = None;
}
pub fn pending_bind(&self) -> Option<(String, QuantType)> {
let g = self
.pending_bind
.lock()
.expect("LoaderWrapper::pending_bind Mutex poisoned");
g.clone()
}
pub fn drive_unbind(&self, repo: &str, quant: QuantType) {
self.registry.unbind_for(repo, quant);
}
pub fn registry(&self) -> Arc<KvPersistRegistry> {
Arc::clone(&self.registry)
}
fn update_spiller_registration(
&self,
repo: &str,
quant: QuantType,
kv_hook: Arc<Mutex<dyn KvCacheSpill>>,
) {
let g = self
.spiller
.lock()
.expect("LoaderWrapper::spiller Mutex poisoned");
if let Some(spiller_arc) = g.as_ref() {
spiller_arc.register_family(repo.to_string(), quant, kv_hook);
}
}
}
impl<E> ModelLoader<E> for LoaderWrapper<E>
where
E: Send + Sync + 'static,
{
fn load(&self, path: &Path, config: &EngineConfig) -> Result<E> {
let engine = self.inner.load(path, config)?;
let pending = {
let mut g = self
.pending_bind
.lock()
.expect("LoaderWrapper::pending_bind Mutex poisoned");
g.take()
};
let Some((repo, quant)) = pending else {
return Ok(engine);
};
let arc_engine: Arc<E> = Arc::new(engine);
let dyn_view: Arc<dyn Any + Send + Sync> = Arc::clone(&arc_engine) as _;
let dyn_view_for_substitute: Arc<dyn Any + Send + Sync> = Arc::clone(&dyn_view);
{
let _substitute_guard = self
.substitute_lock
.lock()
.expect("LoaderWrapper::substitute_lock Mutex poisoned");
if let Some(kv_hook) =
self.registry
.try_substitute_on_load(&repo, quant, dyn_view_for_substitute)
{
self.update_spiller_registration(&repo, quant, kv_hook);
}
}
self.registry.bind_for(&repo, quant, dyn_view);
Arc::try_unwrap(arc_engine).map_err(|_| {
anyhow::anyhow!(
"LoaderWrapper: registry hook for (repo={repo}, quant={}) \
retained a clone of the type-erased engine Arc; \
this violates the EngineBindable contract",
quant.as_str()
)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serve::kv_persist::EngineBindable;
use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Debug)]
struct TestEngine {
marker: u32,
}
struct MockLoader {
load_count: AtomicU32,
next_marker: AtomicU32,
fail_next: AtomicU32, }
impl MockLoader {
fn new() -> Arc<Self> {
Arc::new(Self {
load_count: AtomicU32::new(0),
next_marker: AtomicU32::new(0xC1A1),
fail_next: AtomicU32::new(0),
})
}
fn force_fail_next(&self) {
self.fail_next.store(1, Ordering::SeqCst);
}
fn load_count(&self) -> u32 {
self.load_count.load(Ordering::SeqCst)
}
}
impl ModelLoader<TestEngine> for MockLoader {
fn load(&self, _path: &Path, _config: &EngineConfig) -> Result<TestEngine> {
self.load_count.fetch_add(1, Ordering::SeqCst);
if self.fail_next.swap(0, Ordering::SeqCst) == 1 {
anyhow::bail!("mock loader: synthetic load failure")
}
let m = self.next_marker.fetch_add(1, Ordering::SeqCst);
Ok(TestEngine { marker: m })
}
}
struct LwMockBindable {
binds: AtomicU32,
unbinds: AtomicU32,
retain_arc: bool,
retained: std::sync::Mutex<Option<Arc<dyn Any + Send + Sync>>>,
}
impl LwMockBindable {
fn new() -> Arc<Self> {
Arc::new(Self {
binds: AtomicU32::new(0),
unbinds: AtomicU32::new(0),
retain_arc: false,
retained: std::sync::Mutex::new(None),
})
}
fn new_retaining() -> Arc<Self> {
Arc::new(Self {
binds: AtomicU32::new(0),
unbinds: AtomicU32::new(0),
retain_arc: true,
retained: std::sync::Mutex::new(None),
})
}
fn bind_count(&self) -> u32 {
self.binds.load(Ordering::SeqCst)
}
fn unbind_count(&self) -> u32 {
self.unbinds.load(Ordering::SeqCst)
}
}
impl EngineBindable for LwMockBindable {
fn bind_engine(&self, engine_dyn: Arc<dyn Any + Send + Sync>) {
self.binds.fetch_add(1, Ordering::SeqCst);
if self.retain_arc {
let mut g = self.retained.lock().unwrap();
*g = Some(engine_dyn);
}
}
fn unbind_engine(&self) {
self.unbinds.fetch_add(1, Ordering::SeqCst);
let mut g = self.retained.lock().unwrap();
*g = None;
}
}
fn dummy_path_and_config() -> (std::path::PathBuf, EngineConfig) {
(
std::path::PathBuf::from("/dev/null/synthetic.gguf"),
EngineConfig {
tokenizer_path: None,
config_path: None,
queue_capacity: 32,
warmup_synchronously: false,
kv_metrics_sink: None,
dwq_overlay_path: None,
engine_mode: crate::serve::api::engine::EngineMode::SerialFifo,
},
)
}
#[test]
fn loader_wrapper_passes_through_load_when_no_registry_match() {
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let mock_hook = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
mock_hook.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> =
LoaderWrapper::new(inner.clone() as Arc<dyn ModelLoader<TestEngine>>, registry);
let (path, cfg) = dummy_path_and_config();
let engine = wrapper.load(&path, &cfg).expect("inner load OK");
assert!(engine.marker >= 0xC1A1);
assert_eq!(inner.load_count(), 1);
assert_eq!(mock_hook.bind_count(), 0);
}
#[test]
fn loader_wrapper_calls_bind_after_successful_load() {
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let mock_hook = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
mock_hook.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner.clone() as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_pending_bind("acme/m1".to_string(), QuantType::Q4_K_M);
assert_eq!(
wrapper.pending_bind(),
Some(("acme/m1".to_string(), QuantType::Q4_K_M))
);
let (path, cfg) = dummy_path_and_config();
let engine = wrapper.load(&path, &cfg).expect("inner load OK");
assert!(engine.marker >= 0xC1A1);
assert_eq!(inner.load_count(), 1);
assert_eq!(mock_hook.bind_count(), 1, "bind_engine fired exactly once");
assert_eq!(wrapper.pending_bind(), None);
}
#[test]
fn loader_wrapper_does_not_bind_on_load_failure() {
let inner = MockLoader::new();
inner.force_fail_next();
let registry = Arc::new(KvPersistRegistry::new());
let mock_hook = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
mock_hook.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner.clone() as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_pending_bind("acme/m1".to_string(), QuantType::Q4_K_M);
let (path, cfg) = dummy_path_and_config();
let result = wrapper.load(&path, &cfg);
assert!(result.is_err(), "inner failure propagates");
assert_eq!(mock_hook.bind_count(), 0, "no bind on load failure");
assert_eq!(
wrapper.pending_bind(),
Some(("acme/m1".to_string(), QuantType::Q4_K_M))
);
}
#[test]
fn loader_wrapper_drive_unbind_calls_registry_unbind() {
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let mock_hook = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
mock_hook.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.drive_unbind("acme/m1", QuantType::Q4_K_M);
assert_eq!(mock_hook.unbind_count(), 1);
wrapper.drive_unbind("unknown/repo", QuantType::Q4_K_M);
assert_eq!(mock_hook.unbind_count(), 1);
}
#[test]
fn loader_wrapper_clear_pending_bind_drops_slot() {
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let mock_hook = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
mock_hook.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_pending_bind("acme/m1".to_string(), QuantType::Q4_K_M);
wrapper.clear_pending_bind();
assert_eq!(wrapper.pending_bind(), None);
let (path, cfg) = dummy_path_and_config();
let _engine = wrapper.load(&path, &cfg).expect("load");
assert_eq!(mock_hook.bind_count(), 0, "no bind after clear");
}
#[test]
fn loader_wrapper_contract_violation_surfaces_error() {
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let bad_hook = LwMockBindable::new_retaining();
registry.register(
"acme/bad".to_string(),
QuantType::Q4_K_M,
bad_hook.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_pending_bind("acme/bad".to_string(), QuantType::Q4_K_M);
let (path, cfg) = dummy_path_and_config();
let result = wrapper.load(&path, &cfg);
assert!(result.is_err(), "contract violation surfaces as Err");
let msg = format!("{}", result.err().unwrap());
assert!(msg.contains("acme/bad"), "error mentions repo: {msg}");
assert_eq!(bad_hook.bind_count(), 1);
wrapper.drive_unbind("acme/bad", QuantType::Q4_K_M);
}
#[test]
fn cmd_serve_constructs_spiller_when_flag_on_smoke() {
use crate::serve::kv_persist::{KvCacheSpill, StubGemma4Spill};
use std::sync::Mutex;
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let recorder = LwMockBindable::new();
registry.register(
"google/gemma-4".to_string(),
QuantType::Q4_K_M,
recorder.clone() as Arc<dyn EngineBindable>,
);
let _kv_hook: Arc<Mutex<dyn KvCacheSpill>> = Arc::new(Mutex::new(StubGemma4Spill));
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner.clone() as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_pending_bind("google/gemma-4".to_string(), QuantType::Q4_K_M);
let (path, cfg) = dummy_path_and_config();
let _engine = wrapper.load(&path, &cfg).expect("load OK");
assert_eq!(recorder.bind_count(), 1, "bind fired through registry");
assert_eq!(inner.load_count(), 1);
wrapper.drive_unbind("google/gemma-4", QuantType::Q4_K_M);
assert_eq!(recorder.unbind_count(), 1, "unbind fired through registry");
eprintln!(
"[C.1 smoke] PASS — wrapper.load fired {} binds, drive_unbind fired {} unbinds",
recorder.bind_count(),
recorder.unbind_count()
);
}
struct LwMockKvSpill {
block_alignment_value: u32,
}
impl crate::serve::kv_persist::spiller::KvCacheSpill for LwMockKvSpill {
fn block_alignment(&self) -> u32 {
self.block_alignment_value
}
fn snapshot_block(
&self,
_layer_rank: usize,
_range: std::ops::Range<u32>,
) -> Option<Vec<u8>> {
None
}
fn restore_block(
&mut self,
_layer_rank: usize,
_range: std::ops::Range<u32>,
_payload: &[u8],
) -> std::result::Result<(), crate::serve::multi_model::SpillErrorKind> {
Ok(())
}
}
#[derive(Debug)]
struct LwExpectedHandle {
_marker: u32,
}
struct LwMockFactory;
impl crate::serve::kv_persist::registry::FamilyHookFactory for LwMockFactory {
fn try_construct(
&self,
engine_dyn: Arc<dyn Any + Send + Sync>,
) -> Option<(
Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>>,
Arc<dyn EngineBindable>,
)> {
engine_dyn
.downcast::<LwExpectedHandle>()
.ok()
.map(|_handle_arc| {
let kv: Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>> =
Arc::new(Mutex::new(LwMockKvSpill {
block_alignment_value: 256,
}));
let bindable: Arc<dyn EngineBindable> = LwMockBindable::new();
(kv, bindable)
})
}
}
struct LwMockLoaderHandle;
impl ModelLoader<LwExpectedHandle> for LwMockLoaderHandle {
fn load(&self, _path: &Path, _config: &EngineConfig) -> Result<LwExpectedHandle> {
Ok(LwExpectedHandle { _marker: 0xCAFE })
}
}
#[test]
fn loader_wrapper_substitutes_on_load_when_factory_matches() {
use crate::serve::kv_persist::block_store::DiskBlockStore;
use crate::serve::kv_persist::registry::FamilyHookFactory;
use crate::serve::kv_persist::writer::AsyncWriterHandle;
use crate::serve::kv_persist::{BlockPrefixCacheSpiller, DEFAULT_CHANNEL_CAPACITY};
let tmp = std::env::temp_dir().join(format!(
"hf2q-lw-substitute-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
std::fs::create_dir_all(&tmp).expect("mkdir tmp");
let store = Arc::new(DiskBlockStore::new(tmp.clone(), 0).expect("DiskBlockStore"));
let writer = Arc::new(AsyncWriterHandle::spawn(
Arc::clone(&store),
DEFAULT_CHANNEL_CAPACITY,
));
let spiller: Arc<BlockPrefixCacheSpiller<LwExpectedHandle>> = Arc::new(
BlockPrefixCacheSpiller::new(Arc::clone(&store), Arc::clone(&writer)),
);
struct OldStub;
impl crate::serve::kv_persist::spiller::KvCacheSpill for OldStub {
fn block_alignment(&self) -> u32 {
999
}
fn snapshot_block(
&self,
_layer_rank: usize,
_range: std::ops::Range<u32>,
) -> Option<Vec<u8>> {
None
}
fn restore_block(
&mut self,
_layer_rank: usize,
_range: std::ops::Range<u32>,
_payload: &[u8],
) -> std::result::Result<(), crate::serve::multi_model::SpillErrorKind> {
Ok(())
}
}
let old_stub: Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>> =
Arc::new(Mutex::new(OldStub));
spiller.register_family("acme/m1".to_string(), QuantType::Q4_K_M, old_stub);
assert_eq!(spiller.registered_count(), 1);
let registry = Arc::new(KvPersistRegistry::new());
let pre_stub_bindable = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
pre_stub_bindable.clone() as Arc<dyn EngineBindable>,
);
let factory: Arc<dyn FamilyHookFactory> = Arc::new(LwMockFactory);
registry.register_factory("acme/m1".to_string(), QuantType::Q4_K_M, factory);
let inner: Arc<dyn ModelLoader<LwExpectedHandle>> = Arc::new(LwMockLoaderHandle);
let wrapper: LoaderWrapper<LwExpectedHandle> =
LoaderWrapper::new(inner, Arc::clone(®istry));
wrapper.set_spiller(Arc::clone(&spiller));
wrapper.set_pending_bind("acme/m1".to_string(), QuantType::Q4_K_M);
let (path, cfg) = dummy_path_and_config();
let _engine = wrapper.load(&path, &cfg).expect("load OK");
assert_eq!(spiller.registered_count(), 1);
assert_eq!(
pre_stub_bindable.bind_count(),
0,
"factory substitution overwrites the registry's hook entry"
);
}
#[test]
fn loader_wrapper_does_not_substitute_on_factory_mismatch() {
use crate::serve::kv_persist::block_store::DiskBlockStore;
use crate::serve::kv_persist::registry::FamilyHookFactory;
use crate::serve::kv_persist::writer::AsyncWriterHandle;
use crate::serve::kv_persist::{BlockPrefixCacheSpiller, DEFAULT_CHANNEL_CAPACITY};
let tmp = std::env::temp_dir().join(format!(
"hf2q-lw-no-substitute-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
std::fs::create_dir_all(&tmp).expect("mkdir tmp");
let store = Arc::new(DiskBlockStore::new(tmp.clone(), 0).expect("DiskBlockStore"));
let writer = Arc::new(AsyncWriterHandle::spawn(
Arc::clone(&store),
DEFAULT_CHANNEL_CAPACITY,
));
let spiller: Arc<BlockPrefixCacheSpiller<TestEngine>> = Arc::new(
BlockPrefixCacheSpiller::new(Arc::clone(&store), Arc::clone(&writer)),
);
struct InitialStub;
impl crate::serve::kv_persist::spiller::KvCacheSpill for InitialStub {
fn block_alignment(&self) -> u32 {
111
}
fn snapshot_block(
&self,
_layer_rank: usize,
_range: std::ops::Range<u32>,
) -> Option<Vec<u8>> {
None
}
fn restore_block(
&mut self,
_layer_rank: usize,
_range: std::ops::Range<u32>,
_payload: &[u8],
) -> std::result::Result<(), crate::serve::multi_model::SpillErrorKind> {
Ok(())
}
}
let initial_stub: Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>> =
Arc::new(Mutex::new(InitialStub));
spiller.register_family("acme/m1".to_string(), QuantType::Q4_K_M, initial_stub);
let registry = Arc::new(KvPersistRegistry::new());
let stub_bindable = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
stub_bindable.clone() as Arc<dyn EngineBindable>,
);
let factory: Arc<dyn FamilyHookFactory> = Arc::new(LwMockFactory);
registry.register_factory("acme/m1".to_string(), QuantType::Q4_K_M, factory);
let inner = MockLoader::new();
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner.clone() as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_spiller(Arc::clone(&spiller));
wrapper.set_pending_bind("acme/m1".to_string(), QuantType::Q4_K_M);
let (path, cfg) = dummy_path_and_config();
let _engine = wrapper.load(&path, &cfg).expect("load OK");
assert_eq!(spiller.registered_count(), 1);
assert_eq!(
stub_bindable.bind_count(),
1,
"C.1 stub still fires bind_for on the auto-Arc<E> mismatched path"
);
}
#[test]
fn p1_1_concurrent_substitute_does_not_split_registry_and_spiller_state() {
use crate::serve::kv_persist::block_store::DiskBlockStore;
use crate::serve::kv_persist::registry::FamilyHookFactory;
use crate::serve::kv_persist::writer::AsyncWriterHandle;
use crate::serve::kv_persist::{BlockPrefixCacheSpiller, DEFAULT_CHANNEL_CAPACITY};
use std::sync::atomic::{AtomicBool, Ordering};
let tmp = std::env::temp_dir().join(format!(
"hf2q-lw-p1-1-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
std::fs::create_dir_all(&tmp).expect("mkdir tmp");
let store = Arc::new(DiskBlockStore::new(tmp.clone(), 0).expect("DiskBlockStore"));
let writer = Arc::new(AsyncWriterHandle::spawn(
Arc::clone(&store),
DEFAULT_CHANNEL_CAPACITY,
));
let spiller: Arc<BlockPrefixCacheSpiller<LwExpectedHandle>> = Arc::new(
BlockPrefixCacheSpiller::new(Arc::clone(&store), Arc::clone(&writer)),
);
struct PreStub;
impl crate::serve::kv_persist::spiller::KvCacheSpill for PreStub {
fn block_alignment(&self) -> u32 {
111
}
fn snapshot_block(&self, _: usize, _: std::ops::Range<u32>) -> Option<Vec<u8>> {
None
}
fn restore_block(
&mut self,
_: usize,
_: std::ops::Range<u32>,
_: &[u8],
) -> std::result::Result<(), crate::serve::multi_model::SpillErrorKind> {
Ok(())
}
}
let pre_stub: Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>> =
Arc::new(Mutex::new(PreStub));
spiller.register_family("acme/m1".to_string(), QuantType::Q4_K_M, pre_stub);
assert_eq!(spiller.registered_count(), 1);
let probe: Arc<
Mutex<Option<Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>>>>,
> = Arc::new(Mutex::new(None));
struct ProbingFactory {
probe:
Arc<Mutex<Option<Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>>>>>,
}
impl FamilyHookFactory for ProbingFactory {
fn try_construct(
&self,
engine_dyn: Arc<dyn Any + Send + Sync>,
) -> Option<(
Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>>,
Arc<dyn EngineBindable>,
)> {
engine_dyn
.downcast::<LwExpectedHandle>()
.ok()
.map(|_handle_arc| {
let kv: Arc<Mutex<dyn crate::serve::kv_persist::spiller::KvCacheSpill>> =
Arc::new(Mutex::new(LwMockKvSpill {
block_alignment_value: 256,
}));
{
let mut g = self.probe.lock().expect("probe poisoned");
*g = Some(Arc::clone(&kv));
}
let bindable: Arc<dyn EngineBindable> = LwMockBindable::new();
(kv, bindable)
})
}
}
let registry = Arc::new(KvPersistRegistry::new());
let pre_stub_bindable = LwMockBindable::new();
registry.register(
"acme/m1".to_string(),
QuantType::Q4_K_M,
pre_stub_bindable.clone() as Arc<dyn EngineBindable>,
);
let factory: Arc<dyn FamilyHookFactory> = Arc::new(ProbingFactory {
probe: Arc::clone(&probe),
});
registry.register_factory("acme/m1".to_string(), QuantType::Q4_K_M, factory);
let inner: Arc<dyn ModelLoader<LwExpectedHandle>> = Arc::new(LwMockLoaderHandle);
let wrapper: Arc<LoaderWrapper<LwExpectedHandle>> =
Arc::new(LoaderWrapper::new(inner, Arc::clone(®istry)));
wrapper.set_spiller(Arc::clone(&spiller));
wrapper.set_pending_bind("acme/m1".to_string(), QuantType::Q4_K_M);
let stop = Arc::new(AtomicBool::new(false));
let mut readers = Vec::new();
for _ in 0..4 {
let s = Arc::clone(&spiller);
let stop_c = Arc::clone(&stop);
readers.push(std::thread::spawn(move || {
let mut observed = Vec::new();
while !stop_c.load(Ordering::Relaxed) {
observed.push(s.registered_count());
}
observed
}));
}
let (path, cfg) = dummy_path_and_config();
let _engine = wrapper.load(&path, &cfg).expect("load OK");
stop.store(true, Ordering::Relaxed);
for r in readers {
let observed = r.join().expect("reader thread");
for c in observed {
assert!(
c == 1,
"P1-1 falsifier: spiller.registered_count must \
remain 1 across substitute-on-load (saw {c})"
);
}
}
assert_eq!(spiller.registered_count(), 1);
assert_eq!(
pre_stub_bindable.bind_count(),
0,
"registry hook was substituted before bind_for fired"
);
let probed = probe.lock().expect("probe poisoned").take();
let probed = probed.expect(
"P1-1 fixture: factory must have produced a kv_hook (else \
substitution didn't fire and the test is decorative)",
);
assert!(
Arc::strong_count(&probed) >= 2,
"P1-1 post-condition: factory-produced kv_hook must be \
retained by the spiller's registration (probe + spiller \
expected; got strong_count={})",
Arc::strong_count(&probed)
);
drop(wrapper);
drop(spiller);
drop(writer);
drop(store);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn loader_wrapper_set_pending_bind_overwrites_prior() {
let inner = MockLoader::new();
let registry = Arc::new(KvPersistRegistry::new());
let mock_a = LwMockBindable::new();
let mock_b = LwMockBindable::new();
registry.register(
"acme/a".to_string(),
QuantType::Q4_K_M,
mock_a.clone() as Arc<dyn EngineBindable>,
);
registry.register(
"acme/b".to_string(),
QuantType::Q4_K_M,
mock_b.clone() as Arc<dyn EngineBindable>,
);
let wrapper: LoaderWrapper<TestEngine> = LoaderWrapper::new(
inner as Arc<dyn ModelLoader<TestEngine>>,
Arc::clone(®istry),
);
wrapper.set_pending_bind("acme/a".to_string(), QuantType::Q4_K_M);
wrapper.set_pending_bind("acme/b".to_string(), QuantType::Q4_K_M);
let (path, cfg) = dummy_path_and_config();
let _engine = wrapper.load(&path, &cfg).expect("load");
assert_eq!(mock_a.bind_count(), 0);
assert_eq!(mock_b.bind_count(), 1);
}
}