Skip to main content

dualcache_ff/static_cache/
static_cache.rs

1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use core::hash::{BuildHasher, Hash, Hasher};
5use ahash::RandomState;
6use core::cell::UnsafeCell;
7
8use crate::sync::{Arc, ArcSlice, new_arc_slice};
9use crate::sync::atomic::{AtomicBool, AtomicU32, Ordering};
10use crate::filters::{T1, T2};
11use crate::storage::Cache;
12use crate::cache::WorkerState;
13use crate::core_cache::CoreCache;
14use crate::Config;
15
16/// A synchronous, wait-free read / spin-locked write cache interface.
17/// Serves as the standard `no_std` fallback when background Daemon processing is undesirable.
18pub struct StaticDualCache<K, V, S = RandomState> {
19    pub hasher: S,
20    pub t1: Arc<T1<K, V>>,
21    pub t2: Arc<T2<K, V>>,
22    pub cache: Arc<Cache<K, V>>,
23    pub epoch: Arc<AtomicU32>,
24    pub worker_states: ArcSlice<WorkerState>,
25    
26    // Protects `core`
27    lock: AtomicBool,
28    core: UnsafeCell<CoreCache<K, V>>,
29}
30
31unsafe impl<K: Send, V: Send, S: Send> Send for StaticDualCache<K, V, S> {}
32unsafe impl<K: Sync, V: Sync, S: Sync> Sync for StaticDualCache<K, V, S> {}
33
34impl<K, V> StaticDualCache<K, V, RandomState>
35where
36    K: Hash + Eq + Send + Sync + Clone + 'static,
37    V: Send + Sync + Clone + 'static,
38{
39    pub fn new(config: Config) -> Self {
40        Self::with_hasher(config, RandomState::new())
41    }
42
43    pub fn new_headless(config: Config) -> (Self, ()) {
44        (Self::new(config), ())
45    }
46}
47
48impl<K, V, S> StaticDualCache<K, V, S>
49where
50    K: Hash + Eq + Send + Sync + Clone + 'static,
51    V: Send + Sync + Clone + 'static,
52    S: BuildHasher + Clone + Send + 'static,
53{
54    pub fn with_hasher(config: Config, hasher: S) -> Self {
55        let t1 = Arc::new(T1::new(config.capacity));
56        let t2 = Arc::new(T2::new(config.capacity));
57        let cache = Arc::new(Cache::new(config.capacity));
58        let epoch = Arc::new(AtomicU32::new(0));
59        let is_cold_start = Arc::new(AtomicBool::new(true));
60
61        let mut states = Vec::with_capacity(config.threads);
62        for _ in 0..config.threads {
63            states.push(WorkerState::new());
64        }
65        let worker_states = new_arc_slice(states);
66
67        let core = CoreCache::new(
68            config.capacity,
69            t1.clone(),
70            t2.clone(),
71            cache.clone(),
72            epoch.clone(),
73            config.duration,
74            worker_states.clone(),
75            is_cold_start,
76        );
77
78        Self {
79            hasher,
80            t1,
81            t2,
82            cache,
83            epoch,
84            worker_states,
85            lock: AtomicBool::new(false),
86            core: UnsafeCell::new(core),
87        }
88    }
89
90    #[inline(always)]
91    fn acquire_lock(&self) -> &mut CoreCache<K, V> {
92        while self.lock.compare_exchange_weak(
93            false,
94            true,
95            Ordering::Acquire,
96            Ordering::Relaxed,
97        ).is_err() {
98            core::hint::spin_loop();
99        }
100        unsafe { &mut *self.core.get() }
101    }
102
103    #[inline(always)]
104    fn release_lock(&self) {
105        self.lock.store(false, Ordering::Release);
106    }
107
108    /// Read operations are fully Lock-Free.
109    pub fn get(&self, key: &K) -> Option<V> {
110        let current_epoch = self.epoch.load(Ordering::Relaxed);
111        let mut s = self.hasher.build_hasher();
112        key.hash(&mut s);
113        let hash = s.finish();
114        let tag = (hash >> 48) as u16;
115
116        let res = if let Some(node) = self.t1.get_node(hash) {
117            if node.key == *key && (node.expire_at == 0 || node.expire_at >= current_epoch) {
118                Some(node.value.clone())
119            } else {
120                None
121            }
122        } else if let Some(node) = self.t2.get_node(hash) {
123            if node.key == *key && (node.expire_at == 0 || node.expire_at >= current_epoch) {
124                Some(node.value.clone())
125            } else {
126                None
127            }
128        } else {
129            let mut val = None;
130            if let Some(global_idx) = self.cache.index_probe(hash, tag) {
131                if let Some(v) = self.cache.node_get_full(global_idx, key, current_epoch) {
132                    val = Some(v);
133                }
134            }
135            val
136        };
137
138        // For StaticDualCache, we register hits synchronously.
139        if res.is_some() {
140            if let Some(global_idx) = self.cache.index_probe(hash, tag) {
141                let core = self.acquire_lock();
142                core.process_hits(&[global_idx]);
143                self.release_lock();
144            }
145        }
146
147        res
148    }
149
150    /// Insert synchronously acquires the lock and updates the core.
151    pub fn insert(&self, key: K, value: V) {
152        let mut s = self.hasher.build_hasher();
153        key.hash(&mut s);
154        let hash = s.finish();
155
156        let core = self.acquire_lock();
157        core.handle_admission_insert(key, value, hash, false);
158        self.release_lock();
159    }
160
161    pub fn insert_t1(&self, key: K, value: V) {
162        let mut s = self.hasher.build_hasher();
163        key.hash(&mut s);
164        let hash = s.finish();
165
166        let core = self.acquire_lock();
167        core.handle_insert_t1(key, value, hash);
168        self.release_lock();
169    }
170
171    pub fn remove(&self, key: &K) {
172        let mut s = self.hasher.build_hasher();
173        key.hash(&mut s);
174        let hash = s.finish();
175
176        let core = self.acquire_lock();
177        core.handle_remove(key.clone(), hash); // key isn't actually used by handle_remove except for signature? wait, cache.rs handle_remove takes K, let's clone.
178        self.release_lock();
179    }
180
181    pub fn maintenance(&self) {
182        let core = self.acquire_lock();
183        core.maintenance();
184        self.release_lock();
185    }
186
187    pub fn clear(&self) {
188        let core = self.acquire_lock();
189        core.handle_clear();
190        self.release_lock();
191    }
192}