1#![cfg_attr(not(any(feature = "std", feature = "daemon", test)), no_std)]
2
3pub mod utils;
4pub mod componant;
5pub mod core;
6
7#[cfg(feature = "std")]
8use ::core::sync::atomic::{AtomicBool, Ordering};
9#[cfg(feature = "std")]
10use crate::componant::tls::{TlsRegistry, TlsHandle};
11#[cfg(feature = "std")]
12use crate::componant::daemon::DaemonMessage;
13
14#[cfg(feature = "std")]
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ThreadCount {
17 Pool(usize),
18 Pin(usize),
19 Mixed(usize, usize),
20}
21
22#[cfg(feature = "std")]
23#[repr(C, align(64))]
26pub struct DualCacheFF<
27 K,
28 V,
29 P,
30 const CAP2: usize,
31 const CAP1: usize,
32 const CAP0: usize,
33 const TOTAL_CAP: usize,
34 const MAX_THREADS: usize,
35 const TLS_CAP: usize,
36 const TLS_INDEX_CAP: usize,
37>
38where
39 P: crate::componant::config::CachePolicy + Send + Sync,
40{
41 core: crate::core::DualCacheCore<K, V, P, CAP2, CAP1, CAP0, TOTAL_CAP>,
42 pub daemon_mode: AtomicBool,
43 #[cfg(feature = "std")]
44 pub cata_mode: AtomicBool,
45 pub tls_registry: TlsRegistry<K, V, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>,
46 #[cfg(feature = "std")]
47 #[allow(clippy::type_complexity)]
48 pub global_tx: std::sync::RwLock<Option<::std::sync::Arc<no_std_tool::collections::mpsc_queue::BoundedQueue<DaemonMessage<K, V>, 65536>>>>,
49 #[cfg(feature = "std")]
50 pub daemon_handle: std::sync::RwLock<Option<crate::componant::daemon::Daemon>>,
51}
52
53#[cfg(feature = "std")]
54impl<
55 K,
56 V,
57 P,
58 const CAP2: usize,
59 const CAP1: usize,
60 const CAP0: usize,
61 const TOTAL_CAP: usize,
62 const MAX_THREADS: usize,
63 const TLS_CAP: usize,
64 const TLS_INDEX_CAP: usize,
65> Default for DualCacheFF<K, V, P, CAP2, CAP1, CAP0, TOTAL_CAP, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>
66where
67 K: Clone + Eq + ::core::hash::Hash + Send + Sync + 'static,
68 V: Clone + Send + Sync + 'static,
69 P: crate::componant::config::CachePolicy + Send + Sync + 'static,
70 {
71 fn default() -> Self {
72 Self::new(P::Evict::default())
73 }
74}
75
76#[cfg(feature = "std")]
77impl<
78 K,
79 V,
80 P,
81 const CAP2: usize,
82 const CAP1: usize,
83 const CAP0: usize,
84 const TOTAL_CAP: usize,
85 const MAX_THREADS: usize,
86 const TLS_CAP: usize,
87 const TLS_INDEX_CAP: usize,
88> DualCacheFF<K, V, P, CAP2, CAP1, CAP0, TOTAL_CAP, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>
89where
90 K: Clone + Eq + ::core::hash::Hash + Send + Sync + 'static,
91 V: Clone + Send + Sync + 'static,
92 P: crate::componant::config::CachePolicy + Send + Sync + 'static,
93{
94 pub const fn new(eviction: P::Evict) -> Self {
95 Self {
96 core: crate::core::DualCacheCore::new(eviction),
97 daemon_mode: AtomicBool::new(false),
98 #[cfg(feature = "std")]
99 cata_mode: AtomicBool::new(false),
100 tls_registry: TlsRegistry::new(),
101 #[cfg(feature = "std")]
102 global_tx: std::sync::RwLock::new(None),
103 #[cfg(feature = "std")]
104 daemon_handle: std::sync::RwLock::new(None),
105 }
106 }
107
108 #[cfg(feature = "std")]
110 pub fn set_cata_tuning(&'static self, on: bool) {
111 if on && !self.cata_mode.swap(true, std::sync::atomic::Ordering::SeqCst) {
112 crate::componant::cata::spawn_demiurge(self);
113 } else if !on {
114 self.cata_mode.store(false, std::sync::atomic::Ordering::SeqCst);
115 }
116 }
117
118 #[cfg(feature = "std")]
121 pub fn set_daemon_mode(&'static self, on: bool) {
122 self.daemon_mode.store(on, Ordering::SeqCst);
123
124 if on {
125 let tx = unsafe { ::std::sync::Arc::<no_std_tool::collections::mpsc_queue::BoundedQueue<DaemonMessage<K, V>, 65536>>::new_zeroed().assume_init() };
126 let rx = tx.clone();
127 let mut broadcast_txs = std::vec::Vec::with_capacity(self.tls_registry.max_threads());
128
129 for i in 0..self.tls_registry.max_threads() {
130 let dummy_handle = TlsHandle { id: i, qsbr_node: ::core::ptr::null_mut() };
131 let block = self.tls_registry.get_block_mut(&dummy_handle);
132 block.tx = Some(tx.clone());
133
134 let hit_queue = unsafe { ::std::sync::Arc::<no_std_tool::collections::mpsc_queue::BoundedQueue<(usize, u8), 1024>>::new_zeroed().assume_init() };
135 block.hit_rx = Some(hit_queue.clone());
136 broadcast_txs.push(hit_queue);
137 }
138
139 let daemon_node = {
140 let node = std::boxed::Box::into_raw(std::boxed::Box::new(crate::componant::qsbr::ThreadStateNode::new()));
141 crate::componant::qsbr::register_node(node, 0, ::core::ptr::null(), None);
142 node
143 };
144 let daemon = crate::componant::daemon::Daemon::spawn(&self.core, rx, broadcast_txs, daemon_node);
145 if let Ok(mut handle_guard) = self.daemon_handle.write() {
146 *handle_guard = Some(daemon);
147 }
148 if let Ok(mut gtx) = self.global_tx.write() {
149 *gtx = Some(tx.clone());
150 }
151 } else {
152 if let Ok(mut gtx) = self.global_tx.write() {
153 *gtx = None;
154 }
155 self.tls_registry.clear_channels();
156 if let Ok(mut handle_guard) = self.daemon_handle.write()
157 && let Some(mut daemon) = handle_guard.take()
158 {
159 daemon.join();
160 }
161 }
162 }
163
164 pub fn register_thread(&self) -> TlsHandle {
166 let handle = self.tls_registry.register_thread();
167
168 #[cfg(feature = "std")]
169 if let Ok(gtx) = self.global_tx.read()
170 && let Some(ref global_tx) = *gtx {
171 let block = self.tls_registry.get_block_mut(&handle);
172 block.tx = Some(global_tx.clone());
173 }
174
175 handle
176 }
177
178 pub fn get(&self, key: &K, handle: &TlsHandle) -> Option<V> {
179 let block = self.tls_registry.get_block_mut(handle);
180 block.op_count = block.op_count.wrapping_add(1);
181
182 let op_count = block.op_count as u32;
183
184 #[cfg(feature = "std")]
185 if op_count & 63 == 0 {
186 let global = crate::componant::qsbr::get_global_epoch();
187 unsafe {
188 let node = &mut *handle.qsbr_node;
189 node.epoch.store(global, ::core::sync::atomic::Ordering::Relaxed);
190 node.active.store(true, ::core::sync::atomic::Ordering::Relaxed);
191 }
192 }
193
194 let guard = ::core::mem::ManuallyDrop::new(unsafe { crate::componant::qsbr::Guard::unpinned(handle.qsbr_node) });
195 let hash = self.core.hash_key(key);
196
197 if let Some(val) = self.core.get_t0(hash, key, &guard, op_count) {
199 block.warmup_state = block.warmup_state.saturating_add(10);
200 block.cache.insert_fast_pass(hash, key.clone(), val.clone());
201 return Some(val.clone());
202 }
203
204 if let Some(val) = self.core.get_t1(hash, key, &guard, op_count) {
206 block.cache.insert(hash, key.clone(), val.clone());
207 return Some(val.clone());
208 }
209
210 let (val_opt, promote, _sync) = block.cache.get(hash, key);
212 if let Some(val) = val_opt {
213 if promote {
214 self.core.put_t0(key.clone(), val.clone(), handle.qsbr_node);
215 }
216 #[cfg(feature = "std")]
217 if _sync > 0 {
218 if block.hit_batch_len < 32 {
219 block.hit_batch[block.hit_batch_len as usize] = (hash, _sync);
220 block.hit_batch_len += 1;
221 }
222 if block.hit_batch_len == 32 {
223 if let Some(ref tx) = block.tx {
224 let mut batch = [(0, 0); 32];
225 batch.copy_from_slice(&block.hit_batch);
226 let _ = tx.push(crate::componant::daemon::DaemonMessage::HitBatch(batch, 32));
227 }
228 block.hit_batch_len = 0;
229 }
230 }
231 return Some(val.clone());
232 }
233
234 if let Some(val) = self.core.get_t2(hash, key, &guard, op_count) {
236 block.warmup_state = block.warmup_state.saturating_sub(10);
237 block.cache.insert(hash, key.clone(), val.0.clone());
238 return Some(val.0.clone());
239 }
240 None
241 }
242
243 pub fn insert(&self, key: K, value: V, handle: &TlsHandle) {
244 let block = self.tls_registry.get_block_mut(handle);
245 block.op_count = block.op_count.wrapping_add(1);
246 if crate::utils::unlikely(block.op_count.is_multiple_of(64)) {
247 self.core.try_reclaim(handle.qsbr_node);
248 }
249
250 let hash = self.core.hash_key(&key);
251
252 let (_, _, _, warmup_thresh) = self.core.blackjack.load_params();
253 if block.warmup_state > warmup_thresh {
254 block.cache.insert_fast_pass(hash, key.clone(), value.clone());
255 self.core.put_t0(key, value, handle.qsbr_node);
256 block.warmup_state = block.warmup_state.saturating_sub(20);
257 } else {
258 if block.cache.insert(hash, key.clone(), value.clone()) {
259 self.core.put(key, value, handle.qsbr_node);
260 }
261 }
262 }
263
264 pub fn warmup(&self, key: K, value: V, handle: &TlsHandle) {
268 let block = self.tls_registry.get_block_mut(handle);
269 block.op_count = block.op_count.wrapping_add(1);
270 if crate::utils::unlikely(block.op_count.is_multiple_of(64)) {
271 self.core.try_reclaim(handle.qsbr_node);
272 }
273
274 let hash = self.core.hash_key(&key);
275 block.cache.insert_fast_pass(hash, key.clone(), value.clone());
276 self.core.put_t0(key, value, handle.qsbr_node);
277 }
278}
279
280#[cfg(feature = "std")]
281unsafe impl<
282 K,
283 V,
284 P,
285 const CAP2: usize,
286 const CAP1: usize,
287 const CAP0: usize,
288 const TOTAL_CAP: usize,
289 const MAX_THREADS: usize,
290 const TLS_CAP: usize,
291 const TLS_INDEX_CAP: usize,
292> Send for DualCacheFF<K, V, P, CAP2, CAP1, CAP0, TOTAL_CAP, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>
293where
294 P: crate::componant::config::CachePolicy + Send + Sync,
295{}
296
297#[cfg(feature = "std")]
298unsafe impl<
299 K,
300 V,
301 P,
302 const CAP2: usize,
303 const CAP1: usize,
304 const CAP0: usize,
305 const TOTAL_CAP: usize,
306 const MAX_THREADS: usize,
307 const TLS_CAP: usize,
308 const TLS_INDEX_CAP: usize,
309> Sync for DualCacheFF<K, V, P, CAP2, CAP1, CAP0, TOTAL_CAP, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>
310where
311 P: crate::componant::config::CachePolicy + Send + Sync,
312{}
313
314
315#[cfg(feature = "std")]
316impl<
317 K, V, P,
318 const CAP2: usize, const CAP1: usize, const CAP0: usize, const TOTAL_CAP: usize,
319 const MAX_THREADS: usize, const TLS_CAP: usize, const TLS_INDEX_CAP: usize
320> DualCacheFF<K, V, P, CAP2, CAP1, CAP0, TOTAL_CAP, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>
321where
322 K: Clone + Eq,
323 V: Clone,
324 P: crate::componant::config::CachePolicy + Send + Sync,
325{
326 pub fn get_metrics(&self) -> (u64, u64) {
327 self.tls_registry.get_metrics()
328 }
329}
330
331#[cfg(feature = "std")]
332impl<
333 K, V, P,
334 const T0_CAP: usize, const T1_CAP: usize, const T2_CAP: usize, const TOTAL_CAP: usize,
335 const MAX_THREADS: usize, const TLS_CAP: usize, const TLS_INDEX_CAP: usize
336> Drop for DualCacheFF<K, V, P, T0_CAP, T1_CAP, T2_CAP, TOTAL_CAP, MAX_THREADS, TLS_CAP, TLS_INDEX_CAP>
337where
338 P: crate::componant::config::CachePolicy + Send + Sync,
339{
340 fn drop(&mut self) {
341 self.daemon_mode.store(false, ::core::sync::atomic::Ordering::SeqCst);
344 self.cata_mode.store(false, ::core::sync::atomic::Ordering::SeqCst);
345
346 if let Ok(mut gtx) = self.global_tx.write() {
348 *gtx = None;
349 }
350 self.tls_registry.clear_channels();
351
352 if let Ok(mut handle_guard) = self.daemon_handle.write()
354 && let Some(mut daemon) = handle_guard.take()
355 {
356 daemon.join();
357 }
358
359 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 use std::thread;
367
368 #[test]
369 fn test_static_global_cache() {
370 static GLOBAL_CACHE: DualCacheFF<u64, u64, crate::componant::config::DefaultExponentialPolicy, 256, 1024, 2048, 1024, 10, 256, 512> = DualCacheFF::new(crate::componant::policy::DefaultEvictionPolicy::new());
371 let handle = GLOBAL_CACHE.register_thread();
372 GLOBAL_CACHE.insert(1, 100, &handle);
373 GLOBAL_CACHE.insert(1, 100, &handle);
374 assert_eq!(GLOBAL_CACHE.get(&1, &handle), Some(100));
375 }
376
377 #[test]
378 fn test_daemon_off_sync() {
379 static CACHE: DualCacheFF<u64, u64, crate::componant::config::DefaultExponentialPolicy, 256, 1024, 2048, 4096, 10, 256, 512> = DualCacheFF::new(crate::componant::policy::DefaultEvictionPolicy::new());
380 let handle = CACHE.register_thread();
381
382 CACHE.insert(1, 100, &handle);
384 CACHE.insert(1, 100, &handle);
385 assert_eq!(CACHE.get(&1, &handle), Some(100));
386
387 CACHE.insert(2, 200, &handle);
389 CACHE.insert(2, 200, &handle);
390 assert_eq!(CACHE.get(&2, &handle), Some(200));
391 }
392
393 #[cfg(feature = "std")]
394 #[test]
395 fn test_daemon_on_async() {
396 use std::time::Duration;
397
398 static CACHE: DualCacheFF<u64, u64, crate::componant::config::DefaultExponentialPolicy, 8, 16, 64, 88, 10, 256, 512> = DualCacheFF::new(crate::componant::policy::DefaultEvictionPolicy::new());
399
400 CACHE.set_daemon_mode(true);
402
403 let handle = CACHE.register_thread();
404
405 CACHE.insert(10, 1000, &handle);
407 CACHE.insert(10, 1000, &handle);
408
409 for i in 100..175 {
412 CACHE.insert(i, i * 10, &handle);
413 CACHE.insert(i, i * 10, &handle);
414 }
415
416 for _ in 0..5 {
418 let _ = CACHE.get(&10, &handle);
419 }
420
421 if let Some(ref tx) = *CACHE.global_tx.read().unwrap() {
423 let _ = tx.push(crate::componant::daemon::DaemonMessage::SetPollInterval(5));
424
425 let ack = crate::componant::daemon::OneshotAck::new();
426 let _ = tx.push(crate::componant::daemon::DaemonMessage::Sync(ack.clone()));
427 ack.wait();
428 }
429
430 thread::sleep(Duration::from_millis(50));
432
433 assert_eq!(CACHE.get(&10, &handle), Some(1000));
435
436 CACHE.set_daemon_mode(false);
438 thread::sleep(Duration::from_millis(50));
439 }
440
441 #[cfg(feature = "std")]
442 #[test]
443 fn test_extensive_coverage() {
444 use std::time::Duration;
445
446 static CACHE: DualCacheFF<u64, u64, crate::componant::config::DefaultExponentialPolicy, 256, 1024, 2048, 4096, 10, 256, 512> = DualCacheFF::new(crate::componant::policy::DefaultEvictionPolicy::new());
447 let handle = CACHE.register_thread();
448
449 for i in 0..100 {
451 CACHE.insert(i, i * 10, &handle);
452 CACHE.insert(i, i * 10, &handle); CACHE.get(&i, &handle);
454 }
455
456 for _ in 0..300 {
458 CACHE.get(&10, &handle);
459 }
460
461 CACHE.set_daemon_mode(true);
463
464 for i in 100..200 {
466 CACHE.insert(i, i * 10, &handle);
467 CACHE.insert(i, i * 10, &handle); for _ in 0..100 {
470 CACHE.get(&i, &handle);
471 }
472 }
473 std::thread::sleep(Duration::from_millis(50));
475
476 if let Ok(gtx) = CACHE.global_tx.read()
478 && let Some(ref tx) = *gtx
479 {
480 let _ = tx.push(crate::componant::daemon::DaemonMessage::Promote(999, 999, 9990, 0));
481 let _ = tx.push(crate::componant::daemon::DaemonMessage::Promote(888, 888, 8880, 2));
482
483 let mut arr = [(0usize, 0u8); 32];
485 arr[0] = (123, 10);
486 arr[1] = (123, 5); arr[2] = (456, 1);
488 let _ = tx.push(crate::componant::daemon::DaemonMessage::HitBatch(arr, 3));
489 }
490 std::thread::sleep(Duration::from_millis(50));
491
492 let handle2 = CACHE.register_thread();
493 for i in 100..200 {
494 CACHE.get(&i, &handle2);
496 }
497
498 for i in 1000..2000 {
500 CACHE.insert(i, i * 10, &handle);
501 }
502
503 for i in 1000..2000 {
507 CACHE.get(&i, &handle2);
508 }
509
510 let warmup = CACHE.tls_registry.get_block_mut(&handle2).warmup_state;
511 println!("Warmup state for handle2 after 1000 gets: {}", warmup);
512
513 for i in 2000..3000 {
516 CACHE.insert(i, i * 10, &handle2);
517 CACHE.insert(i, i * 10, &handle2); }
519
520 for _ in 1..9 {
522 let _ = CACHE.register_thread();
523 }
524 let res = std::panic::catch_unwind(|| {
526 let _ = CACHE.register_thread(); });
528 assert!(res.is_err());
529
530 if let Some(ref tx) = *CACHE.global_tx.read().unwrap() {
532 let _ = tx.push(crate::componant::daemon::DaemonMessage::Promote(123, 123, 123, 0));
533
534 let _ = tx.push(crate::componant::daemon::DaemonMessage::SetPollInterval(5));
536
537 let ack = crate::componant::daemon::OneshotAck::new();
538 let _ = tx.push(crate::componant::daemon::DaemonMessage::Sync(ack.clone()));
539 ack.wait();
540 }
541 thread::sleep(Duration::from_millis(50));
543
544 CACHE.set_daemon_mode(false);
546
547 thread::sleep(Duration::from_millis(50));
549
550 CACHE.core.try_reclaim(handle.qsbr_node);
552 CACHE.core.try_reclaim(handle.qsbr_node);
553 }
554}