use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
#[derive(Debug, Clone)]
pub struct AsyncCompactionCache {
pub note1: String,
pub prefix_len: usize,
pub fingerprint: u64,
pub model_slug: String,
pub pass1_latency_ms: u64,
}
#[derive(Default)]
pub struct PrefireState {
in_flight: AtomicBool,
cache: Mutex<Option<AsyncCompactionCache>>,
}
impl PrefireState {
pub fn try_begin(&self) -> bool {
self.in_flight
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
}
pub fn finish(&self) {
self.in_flight.store(false, Ordering::Release);
}
pub fn is_in_flight(&self) -> bool {
self.in_flight.load(Ordering::Acquire)
}
pub fn store(&self, cache: AsyncCompactionCache) {
*self.cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner) = Some(cache);
}
pub fn take(&self) -> Option<AsyncCompactionCache> {
self.cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner).take()
}
pub fn clear(&self) {
*self.cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner) = None;
}
pub fn has_cache(&self) -> bool {
self.cache.lock().unwrap_or_else(std::sync::PoisonError::into_inner).is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_begin_blocks_concurrent_wins() {
let state = PrefireState::default();
assert!(state.try_begin());
assert!(!state.try_begin());
state.finish();
assert!(state.try_begin());
}
#[test]
fn store_take_roundtrip() {
let state = PrefireState::default();
state.store(AsyncCompactionCache {
note1: "note".to_string(),
prefix_len: 3,
fingerprint: 42,
model_slug: "model".to_string(),
pass1_latency_ms: 5,
});
assert!(state.has_cache());
let cache = state.take().unwrap();
assert_eq!(cache.note1, "note");
assert_eq!(cache.prefix_len, 3);
assert!(!state.has_cache());
}
#[test]
fn clear_drops_cache() {
let state = PrefireState::default();
state.store(AsyncCompactionCache {
note1: "note".to_string(),
prefix_len: 3,
fingerprint: 42,
model_slug: "model".to_string(),
pass1_latency_ms: 5,
});
assert!(state.has_cache());
state.clear();
assert!(!state.has_cache());
}
#[test]
fn is_in_flight_reflects_try_begin() {
let state = PrefireState::default();
assert!(!state.is_in_flight());
state.try_begin();
assert!(state.is_in_flight());
state.finish();
assert!(!state.is_in_flight());
}
}