Skip to main content

dualcache_ff/core/
static_cache.rs

1use crate::core::engine::DualCacheCore;
2#[cfg(not(feature = "std"))]
3use crate::componant::qsbr::{ThreadStateNode, pin};
4use crate::componant::config::CachePolicy;
5use core::hash::Hash;
6use no_std_tool::sync::SpinMutex;
7use ::core::sync::atomic::{AtomicUsize, Ordering};
8
9#[cfg(feature = "std")]
10pub struct StaticDualCache<K, V, P: CachePolicy, const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize> {
11    core: DualCacheCore<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP>,
12    tls_registry: crate::componant::tls::TlsRegistry<K, V, 64, 4096, 128>,
13    reclaim_lock: SpinMutex<()>,
14    insert_count: AtomicUsize,
15}
16
17#[cfg(feature = "std")]
18impl<K, V, P: CachePolicy, const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize> StaticDualCache<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP> 
19where
20    K: Clone + Eq + Hash + Send + Sync + 'static,
21    V: Clone + Send + Sync + 'static,
22{
23    pub const fn new(eviction: P::Evict) -> Self {
24        Self {
25            core: DualCacheCore::new(eviction),
26            tls_registry: crate::componant::tls::TlsRegistry::new(),
27            reclaim_lock: SpinMutex::new(()),
28            insert_count: AtomicUsize::new(0),
29        }
30    }
31
32    /// Retrieve a value from the cache lock-freely.
33    pub fn get(&self, key: &K) -> Option<V> {
34        thread_local! {
35            static THREAD_HANDLE: std::cell::OnceCell<crate::componant::tls::TlsHandle> = const { std::cell::OnceCell::new() };
36        }
37        
38        let node_ptr = THREAD_HANDLE.with(|cell| {
39            cell.get_or_init(|| self.tls_registry.register_thread()).qsbr_node
40        });
41        
42        let guard = unsafe { crate::componant::qsbr::Guard::unpinned(node_ptr) };
43        self.core.get(key, &guard, 16).map(|(v_ref, _tier, _hint)| v_ref.clone())
44    }
45
46    /// Insert a key-value pair into the cache. Handles inline reclamation synchronously.
47    pub fn put(&self, key: K, value: V) {
48        thread_local! {
49            static THREAD_HANDLE: std::cell::OnceCell<crate::componant::tls::TlsHandle> = const { std::cell::OnceCell::new() };
50        }
51        
52        let node_ptr = THREAD_HANDLE.with(|cell| {
53            cell.get_or_init(|| self.tls_registry.register_thread()).qsbr_node
54        });
55        
56        self.core.put(key, value, node_ptr);
57        
58        let count = self.insert_count.fetch_add(1, Ordering::Relaxed);
59        
60        // Inline QSBR reclamation every 1024 inserts to prevent Arena OOM
61        if count % 1024 == 1023 && self.reclaim_lock.lock().is_ok() {
62            self.core.sync_reclaim();
63        }
64    }
65}
66
67#[cfg(not(feature = "std"))]
68pub struct StaticDualCache<K, V, P: CachePolicy, const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize> {
69    inner: SpinMutex<(DualCacheCore<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP>, ThreadStateNode, bool)>,
70    insert_count: AtomicUsize,
71}
72
73#[cfg(not(feature = "std"))]
74impl<K, V, P: CachePolicy, const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize> StaticDualCache<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP> 
75where
76    K: Clone + Eq + Hash,
77    V: Clone,
78{
79    pub const fn new(eviction: P::Evict) -> Self {
80        Self {
81            inner: SpinMutex::new((DualCacheCore::new(eviction), ThreadStateNode::new(), false)),
82            insert_count: AtomicUsize::new(0),
83        }
84    }
85
86    /// Retrieve a value from the cache synchronously.
87    pub fn get(&self, key: &K) -> Option<V> {
88        let mut inner = match self.inner.lock() {
89            Ok(guard) => guard,
90            Err(_) => return None, // Aerospace-grade: treat lock timeout as cache miss to guarantee bounded latency
91        };
92        let (engine, qsbr_node, registered) = &mut *inner;
93        
94        if !*registered {
95            crate::componant::qsbr::register_node(qsbr_node as *mut _, 0, ::core::ptr::null(), None);
96            *registered = true;
97        }
98        
99        let guard = pin(qsbr_node as *mut ThreadStateNode);
100        
101        // Map the reference to an owned value to allow releasing the lock safely
102        // Pass 16 to force record_hit for static cache which doesn't have a thread local op_count
103        engine.get(key, &guard, 16).map(|(v_ref, _tier, _hint)| v_ref.clone())
104    }
105
106    /// Insert a key-value pair into the cache. Handles inline reclamation synchronously.
107    pub fn put(&self, key: K, value: V) {
108        let mut inner = match self.inner.lock() {
109            Ok(guard) => guard,
110            Err(_) => return, // Aerospace-grade: drop insert on lock timeout to guarantee bounded latency
111        };
112        let (engine, qsbr_node, registered) = &mut *inner;
113        
114        if !*registered {
115            crate::componant::qsbr::register_node(qsbr_node as *mut _, 0, ::core::ptr::null(), None);
116            *registered = true;
117        }
118        
119        let node_ptr = qsbr_node as *mut ThreadStateNode;
120        
121        engine.put(key, value, node_ptr);
122        
123        let count = self.insert_count.fetch_add(1, Ordering::Relaxed);
124        
125        // Inline QSBR reclamation every 1024 inserts to prevent Arena OOM
126        if count % 1024 == 1023 {
127            engine.sync_reclaim();
128        }
129    }
130}
131
132#[cfg(feature = "std")]
133impl<K, V, P: CachePolicy, const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize> Default for StaticDualCache<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP> 
134where
135    K: Clone + Eq + Hash + Send + Sync + 'static,
136    V: Clone + Send + Sync + 'static,
137{
138    fn default() -> Self {
139        Self::new(P::Evict::default())
140    }
141}
142
143#[cfg(not(feature = "std"))]
144impl<K, V, P: CachePolicy, const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize> Default for StaticDualCache<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP> 
145where
146    K: Clone + Eq + Hash,
147    V: Clone,
148{
149    fn default() -> Self {
150        Self::new(P::Evict::default())
151    }
152}
153
154/// A default configured Bottom-Up Anchoring StaticDualCache
155pub type StaticBottomUpCache<K, V> = StaticDualCache<K, V, crate::componant::config::DefaultExponentialPolicy, 64, 4096, 262144, { 64 + 4096 + 262144 }>;